从一个密码派生多个密钥是否安全?

Raz*_*ick 6 c# security encryption hash

问题

我的应用程序(我将用 C# 编写)使用密钥推导方法(Rfc2898DeriveBytes,4000 次迭代)和盐来生成密码的“散列”。然后将此散列发送到数据库,以便用户将来可以使用该密码对其帐户进行身份验证。

到目前为止,这应该是安全的,但在那之后,我想在相同的密码上使用 Rfc2898DeriveBytes 来生成一个可以用于加密的密钥。现在我打算这样做的方法是使用相同方法的 5000 次迭代来获取不同的密钥,但我担心如果存储在数据库中的哈希值被泄露(或者我被迫透露它),它将是可能以某种方式导出第二个密钥。那可能吗?

潜在的解决方案

  • 理想情况下,我想使用相同的盐,但使用不同的盐会解决问题吗?
  • 将 SHA 用于数据库散列和 Rfc 用于加密密钥怎么样?
  • 在派生密钥之前将唯一但硬编码的字符串(分别如“网站”和“加密”)附加到每个字符串。(Damien_the_Unbeliever)
  • 生成双倍长度的密钥,并将派生密钥的前半部分用于认证,后半部分用于加密。(埃里克森)

如果您有任何关于如何最好地改进此流程的建议,我将不胜感激。我会发布代码,但我还没有写。

Raz*_*ick 4

为了方便参考,我将收到的建议整理成一个答案。

迭代次数

尽管这不是我的问题的一部分,但 erickson 指出 Rfc2898DeriveBytes(C# 的 PBKDF2 方法)的 5,000 次迭代太少,建议至少 50,000 次。

在其他地方查找更多信息后,似乎 50,000 到 1,000,000 之间的迭代次数是不错的,但请记住,速度和安全性之间存在反比关系:随着迭代次数的增加,安全性也会增加,并且迭代次数也会增加。派生密钥所需的时间(这就是它更安全的原因)。

解决方案

尽管根据埃里克森的说法,第一个密钥无法用来确定第二个密钥,但它确实使暴力攻击变得更容易。因此,可以使用以下任何解决方案来解决该问题:

  • 生成双长密钥,并使用派生密钥的前半部分进行身份验证,后半部分进行加密。(埃里克森)
  • 为每个键使用单独的盐。
  • 在派生密钥之前,向每个附加唯一但硬编码的盐(分别如“网站”和“加密”)。另外还应该使用一种独特的盐。(Damien_the_Unknowner)

被拒绝的解决方案

  • SHA_512 速度太快,无法满足我的目的(以及大多数使用哈希值的目的)。使用多次迭代可能会有所帮助,但使用 PBKDF2 方法(例如 Rfc2898DeriveBytes)要安全得多。

结论

感谢大家的帮助,如果您有任何进一步的见解,请评论,我会尽力添加。