AES-256-CBC用PHP加密并用Java解密
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AES-256-CBC用PHP加密并用Java解密相关的知识,希望对你有一定的参考价值。
我在php的openssl_encrypt
中加密JSON并需要在JAVA中解密。
$encrypted = "...ENCRYPTED DATA...";
$secretFile = "/path/to/secret/saved/in/text_file";
$secret = base64_decode(file_get_contents($secretFile));
var_dump(strlen($secret)); // prints : int(370)
$iv = substr($encrypted, 0, 16);
$data = substr($encrypted, 16);
$decrypted = openssl_decrypt($data, "aes-256-cbc", $secret, null, $iv);
这个$decrypted
有正确的数据,现在已被解密。
现在,问题是当我尝试用Java做同样的事情时,它不起作用:(
String path = "/path/to/secret/saved/in/text";
String payload = "...ENCRYPTED DATA...";
StringBuilder output = new StringBuilder();
String iv = payload.substring(0, 16);
byte[] secret = Base64.getDecoder().decode(Files.readAllBytes(Paths.get(path)));
String data = payload.substring(16);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(), 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); // This line throws exception :
cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
这里是:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 370 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.init(Cipher.java:1394)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.sample.App.main(App.java:70)
我已经访问过类似的问题了
AES-256 CBC encrypt in php and decrypt in Java or vice-versa
openssl_encrypt 256 CBC raw_data in java
Unable to exchange data encrypted with AES-256 between Java and PHP
并列表继续......但没有运气
顺便说一下,这就是PHP中加密的方式
$secretFile = "/path/to/secret/saved/in/text_file";
$secret = base64_decode(file_get_contents($secretFile));
$iv = bin2hex(openssl_random_pseudo_bytes(8));
$enc = openssl_encrypt($plainText, "aes-256-cbc", $secret, false, $iv);
return $iv.$enc;
是的,我忘了提到我的JRE已经在UnlimitedJCEPolicy
而我无法更改PHP代码。
我完全陷入困境,不能前进。请帮忙。
编辑#1
byte[] payload = ....;
byte[] iv = ....;
byte[] secret = ....; // Now 370 bits
byte[] data = Base64.getDecoder().decode(payload);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOfRange(secret, 0, 32), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv, 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] output = cipher.doFinal(data);
System.out.println(new String(output).trim());
以上片段似乎与openssl_encrypt
合作
编辑#2
我不确定这是否正确,但以下是我所做的,双方的加密解密工作正常。
在PHP中加密,JAVA中的Decrypt使用AES/CBC/NoPadding
在JAVA中加密,在PHP中解密使用AES/CBC/PKCS5Padding
我不会提供完整的解决方案,但您应该注意一些差异
编码方式:
String iv = payload.substring(0, 16);
String data = payload.substring(16);
你确定Java和PHP中的IV和数据是一样的(IV是字符串吗?)?如果数据是加密的,则应将它们视为字节数组,而不是字符串。真的确保它们是相同的(在php和java中打印hex / base64)
对于IV,你最后调用iv.getBytes()
,但语言环境编码可能会/会破坏你的值。只有当字符串真正是字符串(文本)时才应该使用String。不要将字符串用于二进制文件。
简单地将数据和iv视为byte []
根据openssl的密钥生成
对于使用的aes-256-cbc
,AES密钥的长度必须为256位。问题是 - 默认情况下openssl不使用提供的秘密作为密钥(我相信它可以,但我不知道如何在PHP中指定)。
见OpenSSL EVP_BytesToKey issue in Java
这是EVP_BytesToKey实现:https://olabini.com/blog/tag/evp_bytestokey/
您应该使用EVP_BytesToKey函数生成256位密钥(它是openssl使用的密钥派生函数)。
编辑:
Maarten(在评论中)是对的。关键参数是关键。似乎PHP函数正在接受任何长度误导的参数。根据一些文章(例如http://thefsb.tumblr.com/post/110749271235/using-opensslendecrypt-in-php-instead-of),密钥被断言或填充到必要的长度(因此看起来370位密钥被截断为256位的长度)。
根据您的示例,我编写了完整的PHP和Java代码: AesCipher类:https://gist.github.com/demisang/716250080d77a7f65e66f4e813e5a636
笔记: - 默认算法是AES-128-CBC。 - 默认的初始向量是16个字节。 -Encoded result = base64(initVector + aes crypt)。 -Encoded / Decoded结果作为自身对象出现,它更有帮助,并且可以检查错误,获取错误消息并在编码/解码操作后获取初始向量值。
PHP:
$secretKey = '26kozQaKwRuNJ24t';
$text = 'Some text'
$encrypted = AesCipher::encrypt($secretKey, $text);
$decrypted = AesCipher::decrypt($secretKey, $encrypted);
$encrypted->hasError(); // TRUE if operation failed, FALSE otherwise
$encrypted->getData(); // Encoded/Decoded result
$encrypted->getInitVector(); // Get used (random if encode) init vector
// $decrypted->* has identical methods
JAVA:
String secretKey = "26kozQaKwRuNJ24t";
String text = "Some text";
AesCipher encrypted = AesCipher.encrypt(secretKey, text);
AesCipher decrypted = AesCipher.decrypt(secretKey, encrypted);
encrypted.hasError(); // TRUE if operation failed, FALSE otherwise
encrypted.getData(); // Encoded/Decoded result
encrypted.getInitVector(); // Get used (random if encode) init vector
// decrypted.* has identical methods
以上是关于AES-256-CBC用PHP加密并用Java解密的主要内容,如果未能解决你的问题,请参考以下文章
php openssl aes-256-cbc key长度自动匹配了128的长度,为啥
无法使用来自 AES-256-CBC 的 pgcrypto 解密,但 AES-128-CBC 可以
错误的加密(QT c++ OpenSSL AES 256 CBC)