我应该使用不同的盐分别对两者进行密钥推导吗?
您当然可以这样做,但是具有大致相同安全性的更快替代方案是使用如下内容:
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 位的随机数 (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();
那么您还需要在密文中包含身份验证标签,并检查它在接收方是否匹配before解密。
您还可以使用 Node.js 支持的身份验证模式,例如 GCM。