某次在使用 AES 在线加密网站的时候遇到了两个问题:
- 相同明文和密钥的情况下,每次加密结果不一致,但都可以正常解密出相同的明文。
- 密钥长度无需指定,甚至用空密钥也可以。
示例网站:在线 AES 加密 | AES 解密 – 在线工具 (sojson.com)
虽然每次加密结果不一致,但开头的一段数据总是 U2FsdGVkX1
,于是先解 base64
查看有没有可读的内容。
密文总是以 Salted__
开头,看来是加了盐,密文中应该包含了盐的信息。
查看网站代码,看样子是使用了一个叫做 CryptoJS
的第三方库。


在 npm
上能找到 crypto-js
,可阅读代码。
cryptojs-npm
查看源码,文件 cipher-core.js
第 646 行左右,parse 函数的作用是解析出实际密文和 salt 值。以 word(一个 word 是 8 个 16 进制,就是 32 个 bit,4 个字节)为单位将原密文分割为数组,ciphertextWords[0]
是 0x53616c74
(Salt),ciphertextWords[1]
是 0x65645f5f
(ed__),ciphertextWords[2]
和 ciphertextWords[3]
就是盐值,剩下的就是实际的密文。
parse: function (openSSLStr) {
var salt;
// 先解 base64 编码
var ciphertext = Base64.parse(openSSLStr);
// 以 word 为单位分割成数组
var ciphertextWords = ciphertext.words;
// 判断开始是否是 Salted__
if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) {
// 解析出盐值
salt = WordArray.create(ciphertextWords.slice(2, 4));
// 去掉开头和盐值,剩下的就是实际密文
ciphertextWords.splice(0, 4); //删除"salted__"+salt
ciphertext.sigBytes -= 16;
}
return CipherParams.create({ ciphertext: ciphertext, salt: salt });
}
进一步阅读源码可知,盐值是随机生成,不需要指定盐值。同时有一个密钥派生函数,根据输入的字符串派生出符合长度要求的密钥,所以即使用户输入的密钥长度不满足条件,也是可以正常加密的。