crypto.pbkdf2导出IV和密钥到crypto.createCipheriv的正确设置是什么?

Kar*_*lek 5 encryption cryptography aes pbkdf2 node.js

在node.js的应用程序中,我使用crypto模块进行对称加密/解密.

我使用的是AES-256-CTR.我最初假设crypto.createCipher将"正常工作"和"手动"的细节.现在我正在阅读文档:

注意:createCipher使用OpenSSL函数EVP_BytesToKey派生密钥,摘要算法设置为MD5,一次迭代,无盐.缺少盐允许字典攻击,因为相同的密码始终创建相同的密钥.低迭代计数和非加密安全散列算法允许非常快速地测试密码.

根据OpenSSL建议使用pbkdf2而不是EVP_BytesToKey,建议您使用crypto.pbkdf2派生密钥并自行定义,然后使用createCipheriv()创建密码流.

好吧,我可以自己推导出IV和键.

但是,我不确定,这样做的正确和推荐方法是什么 - 我应该分别对不同的盐进行密钥推导吗?我应该进行一次密钥推导,然后将其减半吗?对于这个特定的用例,我应该使用盐吗?我应该随机生成盐并将其与数据一起保存吗?

Art*_* B. 3

我应该使用不同的盐分别对两者进行密钥推导吗?

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

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
Run Code Online (Sandbox Code Playgroud)

我应该进行一次密钥推导然后将其减半吗?

请求比底层哈希函数提供的输出字节更多的输出字节是有问题的。如果您想要 AES-256 密钥(256 位)和 64 至 128 位的随机数 (IV),那么您需要使用 SHA-384 (sha384) 或 SHA-512 (sha512) 作为基础,两者均digest提供通过node.js。

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

是的,您需要将盐与密文一起发送,以便接收者可以使用他们拥有的密码和盐来生成相同的密钥+随机数。

也许你指的是随机数本身。这是第三个选项,您必须随机生成随机数并将其与随机(加密)盐和密文一起存储。

结论

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

您还应该实施密文身份验证。如果您不这样做,那么您的系统可能容易受到 padding 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();
Run Code Online (Sandbox Code Playgroud)

那么您还需要在密文中包含身份验证标签,并在解密之前检查它在接收方是否匹配。

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