在 java 中加密文件并在 openssl 中使用密钥 aes 解密
Encrypt file in java and decrypt in openssl with key aes
我在java中提到link对文件进行加密(加密文件创建成功)
后来我想用 openssl 解密它。
我尝试了以下命令,但其中 none 似乎有效。
请建议在 openssl 中解密的正确命令。
openssl aes-256-cbc -d -in sample.crypt -out sample.txt -k testpass
shows "bad magic number"
openssl enc -aes-256-cbc -base64 -pass pass:testpass -d -p -in sample.crypt -out sample.txt
shows "error reading input file"
这是我的加密 java 代码:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
public class EncryptTest {
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
static String password = "testpass";
public static String encryptfile(Context context, String path, int category) {
try {
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
Log.e(TAG, "encrypt done " + path);
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
return path;
}
OpenSSL 需要一种特殊的格式,即Salted__
的ASCII 编码值,后面是8 字节的salt,后面是实际的密文。在发布的 Java 代码中,第一块(ASCII 编码 Salted__
加上盐)没有写入,即必须添加,例如:
...
final byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(SALTED_MAGIC);
fos.write(salt);
...
经过这次改动,密文包含了使用 OpenSSL 解密所需的信息和格式。
需要注意的是,OpenSSL 在早期版本中使用 MD5 作为默认摘要,从 v1.1.0 版本开始使用 SHA256。对于后面的版本,因此必须将 -md md5
添加到 OpenSSL 语句(对于早期版本,它是可选的)。
两个发布的 OpenSSL 语句都成功地解密了在我的机器上使用(修改过的)Java 代码创建的密文(在第二条语句中,必须删除 -base64
选项,因为 Java代码没有Base64编码密文):
openssl aes-256-cbc -d -in sample.crypt -out sample.txt -k testpass -md md5
openssl enc -aes-256-cbc -pass pass:testpass -d -p -in sample.crypt -out sample.txt -md md5
还应该提到的是,OpenSSL 应用了 EVP_BytesToKey()
function to derive the key and IV. This function is not a standard and is considered relatively insecure, see e.g. here。新版本的 OpenSSL 会生成相应的警告(警告:使用了弃用的密钥派生)。
我在java中提到
后来我想用 openssl 解密它。 我尝试了以下命令,但其中 none 似乎有效。 请建议在 openssl 中解密的正确命令。
openssl aes-256-cbc -d -in sample.crypt -out sample.txt -k testpass
shows "bad magic number"
openssl enc -aes-256-cbc -base64 -pass pass:testpass -d -p -in sample.crypt -out sample.txt
shows "error reading input file"
这是我的加密 java 代码:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
public class EncryptTest {
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
static String password = "testpass";
public static String encryptfile(Context context, String path, int category) {
try {
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
Log.e(TAG, "encrypt done " + path);
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
return path;
}
OpenSSL 需要一种特殊的格式,即Salted__
的ASCII 编码值,后面是8 字节的salt,后面是实际的密文。在发布的 Java 代码中,第一块(ASCII 编码 Salted__
加上盐)没有写入,即必须添加,例如:
...
final byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(SALTED_MAGIC);
fos.write(salt);
...
经过这次改动,密文包含了使用 OpenSSL 解密所需的信息和格式。
需要注意的是,OpenSSL 在早期版本中使用 MD5 作为默认摘要,从 v1.1.0 版本开始使用 SHA256。对于后面的版本,因此必须将 -md md5
添加到 OpenSSL 语句(对于早期版本,它是可选的)。
两个发布的 OpenSSL 语句都成功地解密了在我的机器上使用(修改过的)Java 代码创建的密文(在第二条语句中,必须删除 -base64
选项,因为 Java代码没有Base64编码密文):
openssl aes-256-cbc -d -in sample.crypt -out sample.txt -k testpass -md md5
openssl enc -aes-256-cbc -pass pass:testpass -d -p -in sample.crypt -out sample.txt -md md5
还应该提到的是,OpenSSL 应用了 EVP_BytesToKey()
function to derive the key and IV. This function is not a standard and is considered relatively insecure, see e.g. here。新版本的 OpenSSL 会生成相应的警告(警告:使用了弃用的密钥派生)。