AES - 用 Java 简单加密,用 openssl 解密 [英] AES - simple encrypt in Java, decrypt with openssl
问题描述
我正在尝试使用 Java Cryto 在 Java 中进行简单的 AES 加密,然后可以在 ObjectiveC 中使用 OpenSSL 对其进行解密.
I am trying to do a simple AES encryption in Java, using Java Cryto, that can then be decrypted in ObjectiveC, using OpenSSL.
因为我不是在做 ObjectiveC 方面的工作,所以我想使用 openSSL 命令行确保它可以工作,但我总是得到错误的幻数"
as I am not doing the ObjectiveC side, I want to make sure it works, using the openSSL command line, but I always get "bad magic number"
这是我的 Java 代码
Here is my Java code
public class EncryptionUtils {
private static final String AES_CIPHER_METHOD = "AES";
private static final int AES_KEY_SIZE = 128;
public static byte[] generateAesKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_CIPHER_METHOD);
keyGenerator.init(AES_KEY_SIZE);
SecretKey key = keyGenerator.generateKey();
return key.getEncoded();
}
public static SecretKeySpec createAesKeySpec(byte[] aesKey) {
return new SecretKeySpec(aesKey, AES_CIPHER_METHOD);
}
public static void aesEncryptFile(File in, File out, SecretKeySpec aesKeySpec) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
Cipher aesCipher = Cipher.getInstance(AES_CIPHER_METHOD);
aesCipher.init(Cipher.ENCRYPT_MODE, aesKeySpec);
InputStream inputStream = new FileInputStream(in);
try {
OutputStream outputStream = new CipherOutputStream(new FileOutputStream(out), aesCipher);
try {
IOUtils.copy(inputStream , outputStream);
} finally {
outputStream.close();
}
} finally {
inputStream.close();
}
}
}
//testcode
@Test
public void testAesEncryptFile() throws IOException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
byte[] aesKey = EncryptionUtils.generateAesKey();
SecretKeySpec aesKeySpec = EncryptionUtils.createAesKeySpec(aesKey);
EncryptionUtils.aesEncryptFile(new File("C:\test\test.txt"), new File("C:\test\test-encrypted.txt"), aesKeySpec);
FileOutputStream outputStream = new FileOutputStream("C:\test\aes.key");
outputStream.write(aesKey);
outputStream.close();
}
@Test
public void testAesDecryptFile() throws IOException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
FileInputStream keyFis = new FileInputStream("C:\test\aes.key");
ByteArrayOutputStream keyBaos = new ByteArrayOutputStream();
IOUtils.copy(keyFis, keyBaos);
SecretKeySpec keySpec = new SecretKeySpec(keyBaos.toByteArray(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
FileInputStream fileInputStream = new FileInputStream("C:\test\test-encrypted.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(fileInputStream, baos);
byte[] decrypted = cipher.doFinal(baos.toByteArray());
FileOutputStream outputStream = new FileOutputStream("C:\test\test-decrypted.txt");
outputStream.write(decrypted);
outputStream.close();
}
现在按预期运行,文件test-encrypted.txt"确实已加密,并且test-decrypted.txt"==test.txt"
Now that runs as expected, file "test-encrypted.txt" is indeed encrypted, and "test-decrypted.txt" == "test.txt"
然后我尝试使用 OpenSSL 在命令行上运行解密
I then tried to run a decryption on the command line using OpenSSL
openssl enc -d -aes-128-ecb -in test-encrypted.txt -k aes.key
然而,这总是给我
bad magic number
据我所知,Java中的使用算法AES"默认使用ECB"模式,所以上面的应该可以工作.我做错了什么.
From what I can see, the using algorithm "AES" in Java uses "ECB" mode by default, so the above should work. What am I doing wrong.
推荐答案
问题确实是由于 OpenSSL 根据密码计算出的密钥造成的.
The problem is indeed due to the key that is computed from the password by OpenSSL.
最可能的原因是 OpenSSL 有自己的算法来从密码派生密钥,EVP_BytesToKey,以及这与 Java 不同.
Most likely the reason is that OpenSSL has its own algorithm to derive a key, EVP_BytesToKey, from the password, and that is not the same as Java's.
我找到的唯一解决方案是使用该算法的 Java 重新实现:
The only solution I found was to use a Java reimplementation of that algorithm:
private static final int KEY_LENGTH = 32;
private byte[] deriveKey(String encryptionPassword, byte[] salt) throws NoSuchAlgorithmException {
final byte[] passAndSalt = ArrayUtils.addAll(encryptionPassword.getBytes(), salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < KEY_LENGTH; i++) {
final byte[] dataToHash = ArrayUtils.addAll(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("SHA-256");
hash = md.digest(dataToHash);
keyAndIv = ArrayUtils.addAll(keyAndIv, hash);
}
return Arrays.copyOfRange(keyAndIv, 0, KEY_LENGTH);
}
ArrayUtils
是 Apache Commons 库的一部分.
ArrayUtils
is part of Apache Commons library.
完全使用:
IvParameterSpec initializationVectorSpec = new IvParameterSpec(
Hex.decodeHex(encryptionInitializationVector.toCharArray()));
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] salt = new SecureRandom().generateSeed(8);
byte[] key = deriveKey(encryptionPassword, salt);
Key keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, initializationVectorSpec);
byte[] rawEncryptedInput = cipher.doFinal(input.getBytes());
byte[] encryptedInputWithPrependedSalt = ArrayUtils.addAll(ArrayUtils.addAll(
"Salted__".getBytes(), salt), rawEncryptedInput);
return Base64.getEncoder()
.encodeToString(encryptedInputWithPrependedSalt);
感谢这个答案为我指明了道路.
这篇关于AES - 用 Java 简单加密,用 openssl 解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!