当前位置: 首页 > 知识库问答 >
问题:

AES-256-CBC用PHP加密并用Java解密

晏正豪
2023-03-14

我遇到的情况是,JSON在PHP的openssl_encrypt中加密,需要在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用php加密,用Java解密,反之亦然

openssl_在java中加密256个CBC原始_数据

无法在Java和PHP之间交换使用AES-256加密的数据

名单还在继续。。。。但是运气不好

顺便说一句,这就是在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\u encrypt

编辑#2

我不确定这是否正确,但以下是我所做的,双方的加密解密工作正常。

在PHP中加密,在JAVA中解密使用AES/CBC/NoPadd

在JAVA中加密,在PHP中解密使用AES/CBC/PKCS5Padding

共有2个答案

山翼
2023-03-14

根据您的示例,我为PHP和Java编写了完整的工作代码:
AESChipher类:https://gist.github.com/demisang/716250080d77a7f65e66f4e813e5a636

注意:
-默认算法是AES-128-CBC。
-默认初始化向量是16字节。
-编码结果=bas64(initVector aes Crypt)。
-编码/解码结果作为自身对象呈现,它得到更多的帮助,并获得检查错误的可能性,获得错误消息,并在编码/解码操作后获得初始化向量值。

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

爪哇:

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
傅博容
2023-03-14

我不会提供一个完整的解决方案,但有一些差异你应该注意

编码:

String iv = payload.substring(0, 16);
String data = payload.substring(16);

你确定IV和数据在Java和PHP中是一样的吗(IV是字符串?)?如果数据是加密的,它们应该被视为字节数组,而不是字符串。只要确保它们是相同的(在PHP和java中打印十六进制/bas64)

对于IV,您可以在结束时调用IV.getBytes(),但区域设置编码可能/将损坏您的值。只有当字符串真正是字符串(文本)时,才应使用该字符串。不要将字符串用于二进制文件

只需将数据和iv视为字节[]

根据openssl生成密钥

AES键的长度必须为256位,才能使用Aes-256-cbc。问题是-openssl默认不使用提供的秘密作为键(我相信它可以,但是我不知道如何在PHP中指定它)。

请参见Java中的OpenSSLEVP_BytesToKey问题

下面是EVP_BytesToKey的实现:https://olabini.com/blog/tag/evp_bytestokey/

应该使用EVP_BytesToKey函数生成一个256位密钥(这是openssl使用的密钥派生函数)

编辑:

Maarten(在评论中)是对的。key参数就是key。似乎PHP函数接受任何长度的参数,这是误导。根据一些文章(例如http://thefsb.tumblr.com/post/110749271235/using-opensslendecrypt-in-php-instead-of),key被构造或填充到必要的长度(因此似乎370位key被截断为256位)。

 类似资料:
  • 我已经设法使它能够处理不包含og a-zA-Z0-9之外的字符和一些特殊字符的文本,但如果我使用丹麦字母,如ielouangØ,解密的文本会显示?而不是实际的字母。所有文件都保存为UTF-8,头字符集=UTF-8 Javascript - input: "tester for php: 我试过选项0,OPENSSL_ZERO_PADDING和OPENSSL_RAW_DATA,结果相同。有人能帮我吗

  • 我只想用这3种模式测试openSSL中的AES:128192和256密钥长度,但我解密的文本与我的输入不同,我不知道为什么。此外,当我传递一个巨大的输入长度(比如1024字节)时,我的程序显示。。。我的意见总是一样的,但这并不重要,至少现在是这样。代码如下: 编辑: 当我将输出大小更改为而不是时,我得到了结果: 那么,是否有可能存在Outpus大小和IV大小的问题?它们应该有什么尺寸(AES-CB

  • 我希望有一个用C编写的程序,可以在没有openssl这样的大型库的帮助下,用AES-CBC对字符串进行编码/解码。 目标: 使用密码短语对字符串进行编码/解码: 因此,应用程序需要接受3个输入参数。。。 输入字符串(待编码)/或已编码字符串(待解码) 用于编码/解码字符串的密码 编码或解码指示器 我对C语言不熟悉(我可以用C#编码)。 我已经找到了https://github.com/kokke/

  • bouncyCastle新手,欢迎提供帮助。我正在尝试使用bounncycastle java API在我的系统上解密一个由第三方加密的文件。它似乎可以很好地解密文件,除了解密文件开头的一团垃圾数据。代码如下 解密的数据块看起来很好,除了开头的“?”??? 用于解密文件的openssl命令在下面的命令中运行良好。事实上,我在解密时使用的是openssl打印的密钥和iv。 加密文件中的openssl

  • 我是密码学的新手。我的要求是对使用openssl加密/解密的文本进行解密/加密。我们在Openssl中使用的算法是aes-256-cbc。因此,我尝试在我的应用程序中实现相同的功能。到目前为止,在谷歌搜索了很多次之后,我所能做的就是。。 我的openssl命令是 我的密钥长度是32位IV是16位 Thnx...