验证解密的字符串

Verifying decrypted string

我正在使用 Node.js 加密库使用 AES-256-ctr 加密较大的文件。我现在遇到的一个问题是验证解密是否成功。目前,如果我使用了错误的密码,我无法知道解密失败,直到我查看文本并意识到它不可读,但这使得很难以编程方式检查并要求重试密码。

到目前为止我从研究中收集到的内容(如果我错了请纠正我):

我的问题: 这是否是错误的,如果是的话,为什么,只是 prepend/append 我要加密的消息的字符串,然后在我解密时检查它是否存在。这似乎是一项简单的检查,但我无法理解为什么不应该这样做。

例如

加密:

  1. 'hello world'
  2. (在前面加上所述字符串)'ENCRYPTED:hello world'
  3. (加密消息)
  4. 'iv:abc123'

解密:

  1. 'iv:abc123' -> (解密)
  2. decStr = 'ENCRYPTED:hello world' 或 'ab/12c879312sc/!/'
  3. if (decStr.split(':')[0] === 'ENCRYPTED') { 正确解密}

我应该为 createHmac() 函数中的 'secret' 使用什么?我不想要求用户持有两个密码。一种用于加密,一种用于验证。我可以使用通过 scrypt 加密密码获得的派生密钥吗?

我不知道这是一个愚蠢的问题还是我只是没有搜索正确的东西,但一些与此类似的问题只是说使用 aes-gcm 以避免担心这个.我主要是想知道怎么做。

您可能不想只在消息前添加一个字符串。这是因为攻击者可以篡改初始字符串之外的消息,而您仍然无法检测到它。因为 CTR 模式使用 XOR 和密钥流进行加密,所以这是微不足道的。

您想做以下两件事之一:使用 AEAD 进行加密,例如 AES-GCM 或 ChaCha20-Poly1305,或者使用 CTR 模式和 HMAC 加密数据。请注意,如果您这样做,您绝不能重复使用加密的密钥随机数对,否则您将失去所有安全性。

最简单、最简单的方法是使用 AES-GCM。您可以以适当的方式生成单个加密安全密钥。然后,从系统 CSPRNG 生成一个随机的 256 位盐。然后这样做(|| 是连接):

PRK = HMAC-SHA-256(salt, initial-key)
key = HMAC-SHA-256(PRK, "encryption key" || 0x01)
IV = HMAC-SHA-256(PRK, "nonce" || 0x01)

这使用 HKDF 生成一个加密密钥,一个 IV(您需要将其截断为 12 个字节)。加密数据,并发出 32 字节的盐和加密消息。

这样,只要您始终为每次加密选择一个随机盐,无论您是否使用相同的初始密钥都没有关系,因为您实际上永远不会重复使用密钥-IV 对。

如果这些文件太大以至于您迫不及待地想检查整个加密以查看它们是否正确,那么请添加一个额外的 32 字节值:

check-value = HMAC-SHA-256(PRK, "check value" || 0x01)

然后,发出盐、校验值和使用 AES-GCM 加密的数据。验证密码,检查校验值是否相同,如果不相同,则为错误。如果 ChaCha20-Poly1305 更适合您的环境(ARM 系统或其他没有 AES 加速的系统),所有这些方法也可以使用 ChaCha20-Poly1305 完成。

额外的 0x01 字节绝对不是必需的,但使用它意味着您使用的是 HKDF,这意味着您可以说您使用的是备受推崇的标准方法。