赞
踩
项目有需求,长明文经过AES-CTR模式加密后,在解密的时候,密文不能直接得到,每次通过某些方法尝试后,只能得到一块密文(按顺序),所以只能一块一块的拼接解密。在使用crypto-js这个库的时候,发送不能直接实现这种局部解密,踩了个大坑,最后经过调试源码,查看文档,花了大半天时间才解决,在此分享一下解决方案。
直接看代码:
import CryptoJS from 'crypto-js';
//参数定义
const key = CryptoJS.enc.Utf8.parse("hv9BbgGupxMIFjFB8hn4KPX9If5G8yw8");
const iv = CryptoJS.enc.Utf8.parse("eUGFebpHnoBov3Pz");
var msg = CryptoJS.enc.Utf8.parse("atfwus_test_test_____"); // 21bytes
//加密
const encrypted = CryptoJS.AES.encrypt(msg, key, { mode: CryptoJS.mode.CTR, iv: iv});
const cipher = encrypted.toString();
const decrypt = CryptoJS.AES.decrypt(cipher, key, { mode: CryptoJS.mode.CTR, iv: iv});
//解密
const deMsg = decrypt.toString(CryptoJS.enc.Utf8);
console.log(deMsg);
可以发现解密成功。
上面的明文加密后得到了两个16字节的密文块,理论上我只给出第一块明文,也是可以正常解密的。但是尝试后,发现并不可以:
//参数定义 const key = CryptoJS.enc.Utf8.parse("hv9BbgGupxMIFjFB8hn4KPX9If5G8yw8"); const iv = CryptoJS.enc.Utf8.parse("eUGFebpHnoBov3Pz"); var msg = CryptoJS.enc.Utf8.parse("atfwus_test_test_____"); // 21bytes //加密 const encrypted = CryptoJS.AES.encrypt(msg, key, { mode: CryptoJS.mode.CTR, iv: iv}); var cipher = encrypted.toString(); //解密 var decrypt = CryptoJS.AES.decrypt(cipher, key, { mode: CryptoJS.mode.CTR, iv: iv}); var deMsg = decrypt.toString(CryptoJS.enc.Utf8); console.log(deMsg); // 尝试局部解密 解密第一个密文块 cipher = CryptoJS.enc.Base64.stringify(CryptoJS.lib.WordArray.create(encrypted.ciphertext.words.slice(0, 4))); var decrypt = CryptoJS.AES.decrypt(cipher, key, { mode: CryptoJS.mode.CTR, iv: iv}); var deMsg = decrypt.toString(CryptoJS.enc.Utf8); console.log(deMsg);
可以发现解密失败。
我们知道AES要求明文必须是16字节的整数倍,而上述的msg是21位,能正常加密,说明CryptoJS 有默认的padding策略。
查阅官方文档可以看出,默认使用的是Pkcs7这个策略。
Pkcs7 是一种常用的填充方式,也是默认的填充方式。它在数据块的末尾添加了几个字节,使得数据块的长度是加密算法所要求的整数倍。这些字节的值等于添加的字节数。特别需要注意的是,就算明文是16字节的整数倍,也会在后面填充16个字节的’\x10’。
这样牺牲了数据长度的做法是为了更为灵活透明的去解包数据,发送端和接收端不需要约定好blockSize,接收端总能通过数据包的最后一个字符得到填充的数据长度。
到这里,我们大概清楚了不能局部解密的原因了:
ZeroPadding 是一种简单的填充方式,它在数据块的末尾添加零或多个字节 0x00,以使得数据块长度为加密算法所要求的整数倍。
只要业务里面不涉及数据末尾零字节的加解密,那么这种填充方式也是可行的,并且可以实现上述的局部解密。
代码如下:
//参数定义 const key = CryptoJS.enc.Utf8.parse("hv9BbgGupxMIFjFB8hn4KPX9If5G8yw8"); const iv = CryptoJS.enc.Utf8.parse("eUGFebpHnoBov3Pz"); var msg = CryptoJS.enc.Utf8.parse("atfwus_test_test_____"); // 21bytes //加密 const encrypted = CryptoJS.AES.encrypt(msg, key, { mode: CryptoJS.mode.CTR, iv: iv, padding: CryptoJS.pad.ZeroPadding}); var cipher = encrypted.toString(); //解密 var decrypt = CryptoJS.AES.decrypt(cipher, key, { mode: CryptoJS.mode.CTR, iv: iv, padding: CryptoJS.pad.ZeroPadding}); var deMsg = decrypt.toString(CryptoJS.enc.Utf8); console.log(deMsg); // 尝试局部解密 解密第一个密文块 cipher = CryptoJS.enc.Base64.stringify(CryptoJS.lib.WordArray.create(encrypted.ciphertext.words.slice(0, 4))); var decrypt = CryptoJS.AES.decrypt(cipher, key, { mode: CryptoJS.mode.CTR, iv: iv, padding: CryptoJS.pad.ZeroPadding}); var deMsg = decrypt.toString(CryptoJS.enc.Utf8); console.log(deMsg);
可以发现,局部解密成功了。
ATFWUS 2023-04-10
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。