Node.js crypto.pbkdf2派生IV和crypto.createCipheriv密钥的正确设置是什么?

Node.js crypto.pbkdf2派生IV和crypto.createCipheriv密钥的正确设置是什么?,node.js,encryption,cryptography,aes,pbkdf2,Node.js,Encryption,Cryptography,Aes,Pbkdf2,在node.js中的一个应用程序中,我使用对称加密/解密 我使用的是AES-256-CTR。我原本以为这将是“只是工作”和“手工”的细节。现在我正在阅读文档: 注意:createCipher使用OpenSSL函数EVP_BytesToKey派生密钥,摘要算法设置为MD5,一次迭代,不使用salt。缺少salt允许字典攻击,因为相同的密码总是创建相同的密钥。低迭代次数和非加密安全哈希算法允许非常快速地测试密码 根据OpenSSL关于使用pbkdf2而不是EVP_BytesToKey的建议,建议您自

在node.js中的一个应用程序中,我使用对称加密/解密

我使用的是AES-256-CTR。我原本以为这将是“只是工作”和“手工”的细节。现在我正在阅读文档:

注意:createCipher使用OpenSSL函数EVP_BytesToKey派生密钥,摘要算法设置为MD5,一次迭代,不使用salt。缺少salt允许字典攻击,因为相同的密码总是创建相同的密钥。低迭代次数和非加密安全哈希算法允许非常快速地测试密码

根据OpenSSL关于使用pbkdf2而不是EVP_BytesToKey的建议,建议您自己使用crypto.pbkdf2派生密钥和iv,然后使用createCipheriv()创建密码流

好的,我可以自己推导IV和钥匙

但是,我不确定,做这件事的正确和推荐的方法是什么——我应该用不同的盐分别对这两种方法进行密钥推导吗?我应该先做一个键的推导,然后把它切成两半吗?对于这个特定的用例,我应该使用盐吗?我应该随机生成盐并将其与数据一起保存吗

我应该用不同的盐分别对这两种方法进行密钥推导吗

您当然可以这样做,但一个具有大致相同安全性的更快的替代方案是使用以下内容:

var master = crypto.pbkdf2Sync(password, randomSalt, 60000, 256, 'sha256');
var hmac = crypto.createHmac('sha256', master);
hmac.update("key");
var key = hmac.digest();

hmac = crypto.createHmac('sha256', master);
hmac.update("nonce");
var nonce = hmac.digest().slice(0,12); // 96 bit for CTR nonce
我应该先做一个键的推导,然后把它切成两半吗

请求比基础哈希函数提供的更多的输出字节是有问题的。如果需要AES-256密钥(256位)和64到128位的nonce(IV),则需要使用SHA-384(sha384)或SHA-512(sha512)作为node.js提供的基础
摘要

我应该随机生成盐并将其与数据一起保存吗

是的,您需要将salt与密文一起发送,以便接收者可以使用他们拥有的密码和salt来生成相同的key+nonce

也许你指的是暂时的。这将是第三种选择,您必须随机生成nonce,并将其与随机(加密)salt和密文一起存储

结论 上述所有方法提供的安全性大致相同,但它们在密文中包含的内容和额外的计算时间上有所不同。我建议用最简单的方法,因为

您还应该实施密文身份验证。如果您不这样做,您的系统可能容易受到oracle攻击

您可以将第一个建议与其他密钥一起用于加密然后MAC解决方案,如下所示:

hmac = crypto.createHmac('sha256', master);
hmac.update("hmac");
var hmacKey = hmac.digest();

// TODO encrypt

hmac = crypto.createHmac('sha256', hmacKey);
hmac.update(ciphertext);
var authenticationTag = hmac.digest();
然后,您还需要将身份验证标记与密文一起包含,并在解密之前在接收方检查它是否匹配


您也可以使用node.js支持的身份验证模式,如GCM模式。

在GCM模式的情况下(我第一次听说它!),您不需要显式地执行MAC,只需假设身份验证可以工作,对吗?没错,您可以使用上述任何方法。不要忘记使用
cipher.getAuthTag()
获取身份验证标记,并将其与密文一起发送。在完成解密之前,接收者应调用
decipher.setAuthTag(buffer)
,并检查调用
decipher.final时是否存在异常
另一个问题。在您的第一个代码示例中,可以将散列值用作伪随机键/IV吗?即使不是直接从pbkdf获取数据?我做HMAC不会损失一些熵吗?我不认为这是个问题。如果你真的想知道,那么我建议你先问这个问题,但先检查一下是否有重复的。我还没有找到一个问题,但我认为它肯定在那里。也许是开着的。我在这里提出了一个后续问题-。(我觉得这样问每件事都有点懒,但我不是唯一一个这样问的人,至少信息会保存在某个地方。)用于一些内部组件。以前使用MD5,1.1.0切换到SHA256。请注意,此更改不会影响
EVP\u BytesToKey
openssl enc
等命令。