PHP 的 AES 加密问题

2020-04-26 17:55:04 +08:00
 youngs

为什么 PHP AES 解密 CBC 模式 iv 可以为空呢? 用 Java 去解密没有 iv 根本解密不出来啊,有什么办法解决?

3143 次点击
所在节点    程序员
19 条回复
youngs
2020-04-26 17:59:56 +08:00
同样的 js 的 CryptoJs 的 iv 也可以为空
GTim
2020-04-26 18:09:43 +08:00
那是因为人家 PHP 底层调用的是 `openssl` 家的函数,`openssl` 家的函数的默认值就是 16 个 0
youngs
2020-04-26 18:18:12 +08:00
@GTim 可是给 iv 赋 16 个 0,解密出来的不是原来的明文
binkcn
2020-04-26 18:27:41 +08:00
@GTim

似乎不是的,参见: https://blog.lancitou.net/how-to-generate-key-and-iv-in-openssl-aes/

OPENSSL AES 算法中的 IV 是这样来的:

```
hash1_128 = MD5(Passphrase + Salt)
hash2_128 = MD5(hash1_128 + Passphrase + Salt)
hash3_128 = MD5(hash2_128 + Passphrase + Salt)
Key = hash1_128 + hash2_128
IV = hash3_128
```

另外,@youngs 不知道你用的是 PHP 什么版本,用的哪个函数在解 AES,最好有代码或者伪代码……
youngs
2020-04-26 18:32:50 +08:00
@binkcn
代码已经加上了
GTim
2020-04-26 19:25:14 +08:00
@GTim CBC 模式是不需要 IV 的,你把你的 Java 代码也贴出来一下
jim9606
2020-04-26 20:34:00 +08:00
CBC 、CFB 、OFB 等流密码模式都是需要 IV 的,不提供的就看默认值是啥了,反正必须要有。

也就 ECB 不需要 IV (千万别用)
rekulas
2020-04-26 22:12:13 +08:00
之前写过 c 版的 aes 虽然加了 iv 但是我不想记录这个 于是每次都是随机 iv 加密 空 iv 解密 解密后抛弃第一个块(因为第一个块无法解密) 后面的就是原始数据

感觉你这问题和这有些关系 你可以使用相同的 iv(例如全 0 值) 打印解密后的原始数据看看一不一样
xLuoBo
2020-04-26 22:24:45 +08:00
千万不要用 php 的 192 256 aes, key 和 iv 处理位数都和通用库不一样;
他是一只特立独行的猪, 自己加的密只能自己解; 让别人懵逼去吧
james122333
2020-04-27 06:11:54 +08:00
youngs
2020-04-27 10:12:40 +08:00
@rekulas
如果是全 0 的话和 Java 解密结果一致,但是都不是正确的解密结果。php 在 iv 为''的情况应该是有默认值,但是不知道这个默认值是啥
youngs
2020-04-27 10:26:40 +08:00
@rekulas 解密出的结果是 SVRQVSSUS***********************91856 确实第一个块解不出来,但是解密出来的密文相比较原来的密文也是查着第一个块的结果,第一个块解密出来是大写字母。。。
binkcn
2020-04-27 14:55:43 +08:00
@youngs

有个关键问题是:你的密文是用 PHP 加密的吗?

我刚才测试了 PHP 下加密的时候 iv 参数不能为空,即 mcrypt_generic_init() 函数第 3 个参数不能为空,一定要设置的,而根据官网文档的说法是建议设置为全 0 值(长度根据 mcrypt_enc_get_iv_size 来设置)。

所以现在问题的关键是你的密文是怎么加密的,openssl ?还是其他语言?
youngs
2020-04-27 15:50:47 +08:00
@binkcn 前端用 CryptoJS 加密的

function Encrypt_aes2(word, privateKey) {
var data = word;
var key = CryptoJS.enc.Utf8.parse('1111111111111111');
var iv = CryptoJS.enc.Utf8.parse('');
//加密
var encrypted = CryptoJS.AES.encrypt(data, key, { iv: iv, mode: CryptoJS.mode.CBC });
console.log('encrypted.toString()', encrypted.toString())
// 这里是解密
console.log('解密' ,CryptoJS.AES.decrypt(encrypted.toString(), key, { iv: iv, mode: CryptoJS.mode.CBC }).toString(CryptoJS.enc.Utf8))
return encrypted.toString();
}
binkcn
2020-04-27 17:11:53 +08:00
@youngs

好了,话题终结了。

看了下 CryptoJs 源码,和我在本帖 4 楼回复的猜想一样,如果你的 key 是个字符串,那么 CryptoJs 底层会用和 Openssl 兼容的函数来生成 iv:根据你的 key 和一个随机的 salt 经过若干次 md5 后生成的,每次 encrypt 的 iv 都不一样,具体方式见我 4 楼的伪代码。

那么,怎么取到 CryptoJS 每次调用 AES.encrypt 时的 iv 呢?很简单……

// Encrypt
var ct = CryptoJS.AES.encrypt('my message', 'secret key 123');

var saltHex = ct.salt.toString(); // random salt
var ctHex = ct.ciphertext.toString(); // actual ciphertext
var ivHex = ct.iv.toString(); // generated IV

console.log(saltHex);
console.log(ctHex);
console.log(ivHex);


你可以试试用打印出来的 iv 到 PHP 或者 Java 里面去解解看。
binkcn
2020-04-27 17:30:40 +08:00
完整的加密、解密,以及显示本次加密的 iv,在我这里测试是通过的。祝你好运:)

// Encrypt
var ct = CryptoJS.AES.encrypt('my message', 'secret key 123');

var saltHex = ct.salt.toString(); // random salt
var ctHex = ct.ciphertext.toString(); // actual ciphertext
var ivHex = ct.iv.toString(); // generated IV

console.log(saltHex);
console.log(ctHex);
console.log(ivHex);

// Decrypt
var bytes = CryptoJS.AES.decrypt(ct.toString(), 'secret key 123', { iv: ct.iv, mode: CryptoJS.mode.CBC });
var originalText = bytes.toString(CryptoJS.enc.Utf8);

console.log(originalText);
youngs
2020-04-27 18:11:23 +08:00
@binkcn 大佬,用 JAVA 怎么取到这个 IV 呢。。。。
binkcn
2020-04-27 18:26:46 +08:00
@youngs 你前端用 CryptoJS 加密的时候,自己额外存一下本次加密时产生的 iv,回头用 java 解密的时候传进去就行了,这样最简单。

如果,你现在想解密之前已经加密出来的密文,那么就稍微麻烦一些:

首先,加密出来的密文是 base64 的,你 base64_decode 之后会发现开头是 “Salted__”这样的,然后紧跟着的 8 个字节就是 salt 了,然后根据我 4 楼的伪代码,Passphrase (即 key ) 和 Salt 你都有了,可以自己算出 iv 。

这个部分你自己实现即可,18 点过了,我下班回家了…… 2333
youngs
2020-04-27 18:28:10 +08:00
@binkcn 大佬!我解出来了。。。详细结果 append 到主题上,感谢~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/666353

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX