某次在使用 AES 在线加密网站的时候遇到了两个问题:

  1. 相同明文和密钥的情况下,每次加密结果不一致,但都可以正常解密出相同的明文。
  2. 密钥长度无需指定,甚至用空密钥也可以。

示例网站:在线 AES 加密 | AES 解密 – 在线工具 (sojson.com)

密文内容会变,base64 编码,开头一段总是固定的字符。

虽然每次加密结果不一致,但开头的一段数据总是 U2FsdGVkX1,于是先解 base64 查看有没有可读的内容。

密文总是以 Salted__开头,看来是加了盐,密文中应该包含了盐的信息。

查看网站代码,看样子是使用了一个叫做 CryptoJS 的第三方库。

npm 上能找到 crypto-js,可阅读代码。

crypto-js – npm (npmjs.com)

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 });
	}
十六进制转字符串

进一步阅读源码可知,盐值是随机生成,不需要指定盐值。同时有一个密钥派生函数,根据输入的字符串派生出符合长度要求的密钥,所以即使用户输入的密钥长度不满足条件,也是可以正常加密的。

订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论