AES-Java中的简单加密,使用openssl解密
我试图用Java Cryto在Java中进行一个简单的AES加密,然后可以用OpenSSL在Objective中解密 由于我没有做Objective方面的工作,我希望使用openSSL命令行确保它工作正常,但我总是得到“坏的幻数” 这是我的Java代码AES-Java中的简单加密,使用openssl解密,java,encryption,cryptography,openssl,Java,Encryption,Cryptography,Openssl,我试图用Java Cryto在Java中进行一个简单的AES加密,然后可以用OpenSSL在Objective中解密 由于我没有做Objective方面的工作,我希望使用openSSL命令行确保它工作正常,但我总是得到“坏的幻数” 这是我的Java代码 public class EncryptionUtils { private static final String AES_CIPHER_METHOD = "AES"; private static final int AES_KEY_SIZ
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”
然后,我尝试使用OpenSSL在命令行上运行解密
openssl enc -d -aes-128-ecb -in test-encrypted.txt -k aes.key
然而,这总是给我带来麻烦
bad magic number
从我所看到的情况来看,Java中的使用算法“AES”默认使用“ECB”模式,因此上述方法应该可以工作。我做错了什么。问题出在钥匙上。
-k
参数需要密码短语,而不是文件。反过来,当openssl加密例程使用密码短语时,会在加密结果前面加上一个魔术和盐。这就是找不到的魔力
要使用openssl命令行,请以十六进制打印密钥,并使用-K
选项而不是小写的-K
选项
您还可以使用:
`cat aes.key`
作为
-K
之后的参数,假设aes.key包含十六进制的密钥。问题确实是由OpenSSL根据密码计算的密钥造成的
最可能的原因是OpenSSL有自己的算法从密码中派生密钥,这与Java的不同
我找到的唯一解决方案是使用该算法的Java重新实现:
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);
}
感谢您为我指路。您的文件中可能有一些无法打印的ascii字符?尝试使用
-base64
选项。似乎与旁注重复:如果需要ECB模式,请指定它。将“AES”
更改为“AES/ECB/”
,以避免歧义。(其中
是您首选的填充方案)。依赖默认值是不明智的做法。可能的重复我无法让它按原样工作,也无法让openssl解密消息。但当我切换到“AES/ECB/PKCS5P”时,它起了作用。
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);