我正在使用 Node.js 的加密模块和 AES-256-CBC 密码算法编写自己的安全类。
但是当我尝试解密一个加密的字符串时,从超过 15 个字符的输入数据加密,失败并出现此错误:
crypto.js:153
var ret = this._handle.final();
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
我认为问题出在加密或 IV 生成上,事实上,加密的十六进制字符串总是 32 个字符长。
让我们一起回顾一下代码:
var crypto = require("crypto"),
password = "mySecureKey",
salt = "mySaltKey";
//generate the IV
crypto.pbkdf2(password , salt, 4096, 8, "sha1", function(err, key) {
if (err) throw err;
var cipher_iv = new Buffer(key.toString('hex'));
//encrypt the string
var input = "helloPrettyWorld";
cipher = crypto.createCipheriv("aes-256-cbc", new Buffer(password), cipher_iv);
cipher.update(input, "utf8", "hex");
var encrypted = cipher.final("hex"); //i.e: input = "hello"; encrypted = "2300743605fbdaf0171052ccc6322e96"
//decrypt the string
cipher = crypto.createDecipheriv("aes-256-cbc", new Buffer(password), cipher_iv); /* THE ERROR IS THROWN HERE */
cipher.update(encrypted, "hex", "utf8")
var decrypted = cipher.final("utf8");
});
我尝试调整密码/salt 长度,甚至使用固定长度的字符串(32、16 等),但没有解决问题。
回顾:
像“helloNiceWorld”(14 个字符)这样的输入数据将被完美加密和解密,而像“helloPrettyWorld”(16 个字符)这样的输入数据则不会。
最佳答案
TL;DR 如果不使用 cipher.update()
的结果,您将丢弃一部分明文和密文。
AES 是一种 block 密码,仅适用于 16 字节( block 大小)的 block 。 CBC 模式将其扩展到长度为 block 大小的倍数的明文。然后需要填充(默认为 PKCS#7 填充)将明文填充到 block 大小的下一个倍数。
这意味着填充是完成加密之前最后操作的一部分。填充应用于 cipher.final()
功能。
cipher.update()
返回密文的一部分,但不处理(它在内部缓存)最后一个字节以应用填充。加密结束cipher.final()
必须调用以将填充应用于最后缓存的字节并进行加密。
由于 PKCS#7 填充总是添加填充,当明文长度为 16 到 31 字节时,您将得到一个两 block 填充的明文。现在,问题是您没有存储 cipher.update()
调用的结果,这导致密文不完整。如果您的消息小于单个 block ,则 cipher.update()
将返回一些空内容(字符串或缓冲区),您将从 cipher.final()
.
不要忘记连接不同的密文和明文部分:
cipher = crypto.createCipheriv("aes-256-cbc", new Buffer(password), cipher_iv);
var encrypted = cipher.update(input, "utf8", "hex");
encrypted += cipher.final("hex");
//decrypt the string
cipher = crypto.createDecipheriv("aes-256-cbc", new Buffer(password), cipher_iv);
var decrypted = cipher.update(encrypted, "hex", "utf8")
decrypted += cipher.final("utf8");
其他注意事项:
password = "mySecureKey"
如果这确实是一些文本,那么它是不安全的,也不是 key ,但正如变量名所说,它是一个密码。不要直接使用密码作为 key 。使用随机生成的盐(每个密码)从密码中导出 key 。
此外,在每次加密过程中生成一个新的随机IV,并将其简单地放在密文前面。 IV 不必是 secret 的,但它必须是不可预测的。如果您重新使用 IV,那么观察您的密文的攻击者可能会确定您是否再次发送之前发送的消息。如果您使用随机 IV,您将获得语义安全。
此外,使用具有强 MAC(如 HMAC-SHA256)的先加密后 MAC 方案验证您的密文。这使您能够检测对密文的任何(恶意)修改,并防止诸如 padding-oracle 攻击之类的攻击。</p>
关于node.js - 使用 AES256 和 Node.js 解密长度超过 15 个字符的输入数据时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33586060/