从一个密码派生多个密钥安全吗



问题

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

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

潜在解决方案

  • 理想情况下,我想使用相同的盐,但会使用换一个解决问题
  • 使用SHA进行数据库哈希,使用Rfc进行加密怎么样钥匙
  • 在导出密钥之前,将一个唯一但硬编码的字符串(分别类似于"网站"one_answers"加密")附加到每个字符串。(Damien_the_Unbeliever)
  • 生成一个双倍长度的密钥,并使用派生密钥的前半部分进行身份验证,后半部分进行加密。(埃里克森)

如果能就如何最好地改进这一过程提出任何建议,我将不胜感激。我会发布代码,但我还没有写。

为了便于参考,我将给出的建议汇编成一个答案

迭代次数

虽然这不是我问题的一部分,但erickson指出Rfc2898DeriveBytes(C#的PBKDF2方法)的5000次迭代太少了,建议至少50000次。

在其他地方寻找更多信息后,50000到1000000之间似乎是一个不错的迭代次数,但请记住,速度和安全性之间存在反比关系:随着迭代次数的增加,安全性和导出密钥所需的时间也在增加(这就是它更安全的原因)。

解决方案

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

  • 生成一个双倍长度的密钥,并使用派生密钥的前半部分进行身份验证,后半部分进行加密。(埃里克森)
  • 对每个键使用单独的盐
  • 在导出密钥之前,为每个salt添加一个唯一但硬编码的salt(分别类似于"网站"one_answers"加密")。此外,还应使用一种独特的盐。(Damien_the_Unbeliever)

拒绝的解决方案

  • SHA_512太快了,对我来说不安全(对大多数使用哈希的目的来说也是如此)。使用多次迭代可能会有所帮助,但使用PBKDF2方法(如Rfc2898DeriveBytes)要安全得多

结论

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

您可以从一个密码中派生多个密钥。

是将不同的盐用于不同的目的,还是生成更长的密钥并将其部分用于不同的用途,取决于是否同时需要不同的密钥。例如,假设用户登录,然后希望能够在不重新输入密码的情况下加密和解密内容。在这种情况下,生成一个双倍长度的密钥,并使用派生密钥的前半部分进行身份验证,后半部分进行加密会更有效。用不同的盐生成两个密钥大约需要两倍的时间。

另一方面,如果用户需要输入密码进行身份验证,然后在其他时间进行加密,那么您不妨选择两种盐。

根据您的建议,暴露数据库中存储的身份验证哈希不会立即导致妥协。也就是说,您不能简单地迭代散列函数;PBKDF2的每次迭代都使用密码作为输入。但这确实意味着,只需要1000次迭代就可以测试作为加密密钥的密码,而且几乎不提供字典攻击的保护。

顺便说一下,5000次迭代还远远不够。50000次迭代将是一个良好的开端。

Rfc2898DeriveBytes的两个调用之间使用不同的salt可以解决您的问题。

这样想吧,两个不同的用户使用相同的密码,知道其中一个的哈希会损害另一个吗?只有两个用户使用相同的盐!

你所做的也没什么不同,只要用两种好的盐同时使用Rfc2898DeriveBytes,你就会没事的。无需更改这两种方法的迭代次数,只需更改salt即可。

没有理由你需要重复使用盐,盐不是秘密信息。大多数文件加密的实现都将salt(也称为IV)放在加密blob的前面。


对于您的其他解决方案"使用SHA作为数据库哈希,使用Rfc作为加密密钥怎么样?"这是一个非常的坏主意。仅对数据库使用SHA就意味着他们将更容易获得原始密码,然后他们可以使用更容易的任务来完成解密基于Rfc的密码的更难任务。

最新更新