41

升级到 .Net 4.5 后,我现在收到“System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile 已过时”的警告,建议改用会员 API。

这对新项目来说非常好,但是在这个阶段(存在用户数据和散列密码),我不能很好地更改为具有可能不同散列方式的自定义成员资格提供程序。

对于此类问题,推荐的前进方式是什么?继续使用“过时”调用显然不是建议的路径,那么它是否已被“仅使用成员资格 API”以外的其他内容所取代?

4

4 回答 4

57

这是 SHA1 变体的解决方案。

     public static string GetSwcSHA1(string value)
     {
        SHA1 algorithm = SHA1.Create();
        byte[] data = algorithm.ComputeHash(Encoding.UTF8.GetBytes(value));
        string sh1 = "";
        for (int i = 0; i < data.Length; i++)
        {
            sh1 += data[i].ToString("x2").ToUpperInvariant();
        }
        return sh1;
     }

对于 MD5,您只需将算法更改为:

MD5 algorithm = MD5.Create();

希望您不介意,只需在上面添加代码的 VB.NET 变体:

    Public Shared Function CreateHash(saltAndPassword) As String
        Dim Algorithm As SHA1 = SHA1.Create()
        Dim Data As Byte() = Algorithm.ComputeHash(Encoding.UTF8.GetBytes(saltAndPassword))
        Dim Hashed As String = ""

        For i As Integer = 0 To Data.Length - 1
            Hashed &= Data(i).ToString("x2").ToUpperInvariant()
        Next

        Return Hashed
    End Function
于 2012-12-13T12:06:33.207 回答
9

对于此类问题,推荐的前进方式是什么?继续使用“过时”调用显然不是建议的路径,那么它是否已被“仅使用成员资格 API”以外的其他内容所取代?

纯粹在 .NET Framework 中的最佳方法(您已经排除)是更改所有内容以使用 PBKDF2、Bcrypt 或 Scrypt 散列密码。PBKDF2 在 .NET 中由Rfc2898DeriveBytes Class提供。

第二种最好的方法是最终得到两个“版本”的密码:

  • 版本 0 本来是旧的 HashPasswordForStoringInConfigFile,但您可以将它们批量、离线更新到版本 1,并在任何人窃取它们之前将这些旧哈希完全删除,您最终会在贸易新闻中被视为具有可悲的 90 年代老式密码哈希。
  • 版本 1,即现有 HashPasswordForStoringInConfigFile 值的 PBKDF2!即,您获取当前讨厌的旧哈希,并使用新的随机盐和大量迭代对它们进行 PBKDF2,然后存储结果。然后,当用户想要登录时,您将他们的密码提供给@RichardBažant 编写的代码,这样您就有了 HashPasswordForStoringInConfigFile 将返回的内容,然后您将 PBKDF2 应用于该结果!
    • 即它实际上是 Rfc2898DeriveBytes(HashPasswordForStoringInConfigFile(password)),PerUserSalt,YourIterations)
  • 版本 2,具有版本 1 哈希的用户升级到该版本。除了“版本”之外的所有列都是相同的,但是在您计算并验证版本 1 之后,您计算 Rfc2898DeriveBytes(password),PerUserSalt,YourIterations) 并将版本 1 哈希替换为版本 2 哈希(并将版本更改为 2,当然)。

第三个最好的方法是第二个最好的方法,但只有版本 1。小心,这种方法是DCC2 的疯狂之处——你不断地将旧的输出包装在更新的算法中

在这两种情况下,您都将 PBKDF2-HMAC-SHA-1 结果存储在数据库中,因此您需要:

  • 密码哈希 (BINARY(20)) - 即 PBKDF2 输出。
  • 您的盐(BINARY(16),由RNGCryptoServiceProvider 类为每个用户生成)
  • 可选:PBKDF2 迭代次数(INT,从数万开始,直到您的服务器在最大负载下受 CPU 限制;随着硬件升级而增加)
    • 这使您可以在每次用户输入他们的(正确的)密码时透明地提高安全级别。首先验证它是正确的,然后用更高的迭代次数重新散列它
  • 可选:一个“版本”(TINYINT),以便以后升级到另一种算法更容易,因为您可以同时激活多个版本。

PS 对于版本 1 或版本 2 更新的算法,Jither 创建了一个能够支持 PBKDF2-HMAC-SHA256、PBKDF2-HMAC-SHA512 等的 .NET 库;我的 Github 存储库包含它的一个变体,其中包含一组合理的测试向量。

于 2014-02-25T04:19:52.127 回答
3

为什么不能使用.Net最简单的一个

public static string HashString(string inputString, string hashName)
{
  var algorithm = HashAlgorithm.Create(hashName);
  if (algorithm == null)
     throw new ArgumentException("Unrecognized hash name", hashName);

  byte[] hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
  return Convert.ToBase64String(hash);
}
于 2016-04-30T20:34:34.553 回答
2

理查德的回答对我很有用。这是从 .NET Framework 4.5 反编译的代码。如果有人觉得它更好,请使用它。我想它可能会快一点。

        public static string BinaryToHex(byte[] data)
        {
            if (data == null)
            {
                return null;
            }
            char[] hex = new char[checked((int)data.Length * 2)];
            for (int i = 0; i < (int)data.Length; i++)
            {
                byte num = data[i];
                hex[2 * i] = NibbleToHex((byte)(num >> 4));
                hex[2 * i + 1] = NibbleToHex((byte)(num & 15));
            }
            return new string(hex);
        }

        private static char NibbleToHex(byte nibble)
        {
            int aChar = (nibble < 10 ? nibble + 48 : nibble - 10 + 65);
            return (char)aChar;
        }
于 2013-08-12T13:53:48.647 回答