【问题标题】:Hash Password in C#? Bcrypt/PBKDF2C#中的哈希密码? Bcrypt/PBKDF2
【发布时间】:2012-07-09 21:41:46
【问题描述】:

我查找了有关如何执行此操作的 msdn 和其他资源,但没有找到明确的解决方案。这是我找到的最好的http://blogs.msdn.com/b/shawnfa/archive/2004/04/14/generating-a-key-from-a-password.aspx?Redirected=true

我想在 C# 中使用 bcrypt 或 PBKDF2(这似乎与 bcrypt 相关)对密码进行哈希处理。我喜欢试验我的计算机需要多少轮来散列密码。然而,当每个人都在谈论散列时,一切似乎都是关于加密的。我想不通。我如何散列密码?它看起来更像 PBKDF2 (Rfc2898?) 是一个随机数生成器,我使用 GetBytes(amount) 来选择我的哈希大小。

我很困惑。我如何使用 bcrypt/PBKDF 对密码进行哈希处理?

【问题讨论】:

  • 那篇文章正在讨论从密码生成密钥以用于加密某些内容。这与散列密码完全不同。
  • @hatchet 正是我能做到的最好的。
  • 请参阅stackoverflow.com/questions/481160/… 获取一些资源。如果你用谷歌搜索hash passwords salt .net,你会得到很多合适的点击。
  • @KendallFrey 他为什么要这么做?然后他需要自己实现一个减慢和加盐方案,而不是使用内置的 PBKDF2 实现。
  • @hatchet:是的,但他们都没有解释如何在 C# 中做到这一点。他们只是说使用它....我正在尝试(正确地)这样做

标签: c# .net hash passwords bcrypt


【解决方案1】:

对于 PBKDF2,您或许可以使用 System.Security.Cryptography.Rfc2898DeriveBytes。

在此处查看 MSDN:http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

【讨论】:

  • 我有,但它没有显示如何散列密码。它显示了如何加密。
  • 抱歉,你说得对,这主要是关于加密的,但我希望你可以利用这门课来获得你需要的东西。用paracycle的上帝的工作找到它周围的包装!
【解决方案2】:

PBKDF2

其实你们真的很亲近。您提供的链接向您展示了如何调用 Rfc2898DeriveBytes 函数来获取 PBKDF2 哈希结果。但是,您对示例使用派生密钥进行加密这一事实感到失望(PBKDF1 和 2 的最初动机是创建适合用作加密密钥的“密钥”派生函数)。当然,我们不想将输出用作加密,而是将其用作哈希。

如果您想要 PBKDF2,可以尝试为此目的编写的 SimpleCrypto.Net 库。如果你look at the implementation,你可以看到它实际上只是一个薄包装(你猜对了)Rfc2898DeriveBytes

BCrypt

如果您想试验这个变体,可以尝试名为(还有什么)BCrypt.NET 的 C# 实现。

免责声明:我没有使用或测试过我链接到的任何库... YMMV

【讨论】:

  • 看来他们的 calculateHash 只是 .GetBytes(64)。那么...我要选择我的哈希位大小并使用 GetBytes 吗?
  • 由于我不熟悉 PBKDF2 算法的细节,我不想推测。但是,正如您所说,您似乎可以为哈希选择足够安全的长度并从方法中获取那么多字节。
  • 有些人似乎仍然推荐 SHA1 和 SHA256 而不是 PBKDF2。 PBKDF2 更好吗?
  • FWIW,SimpleCrypto.NET 由于其实施似乎存在一些安全问题。在将这些类型的项目集成到您自己的项目之前,请务必查看 GitHub 问题。
  • 我为 .NET 创建了一个简单的库,它使用 Microsoft 的 PBKDF2 (Rfc2898DerivedBytes) 实现,但在类似于 BCrypt.NET 的基础上提供了一个极其简单的接口,因此您通常需要调用 Compute (),将结果保存到 db 并稍后通过调用验证进行验证。它叫做 SimpleHashing.Net:github.com/ilya-git/SimpleHashing.Net,在 Nuget 上也有一个同名的包。再说一次,它不是一个自我实现,而是在 MS 实现之上的一个薄包装,以便于使用。
【解决方案3】:

PBKDF2 使用 HMACSHA1,如果您想要更现代和可定制的解决方案,您应该查看使用 HMACSHA256 或 512 的 API,与 PBKDF2 类似

https://sourceforge.net/projects/pwdtknet/

源代码中包含的示例 GUI 演示了如何从密码中获取哈希值,包括创建加密随机盐.....享受吧:)

【讨论】:

    【解决方案4】:

    今年早些时候,我正在研究为我们的 ASP.NET Web 窗体项目创建哈希值的相同方法,我想以与 MVC 项目开箱即用的方式相同的方式进行。

    我偶然发现了这个问题 => ASP.NET Identity default Password Hasher, how does it work and is it secure? 然后我在这里找到了使用 ByteArraysEqual 方法的源 => http://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-rtm-140327/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/Crypto.cs?ImageName=Microsoft.AspNet.Identity.Core

    【讨论】:

    • 对于任何其他后来者来说,上述链接作为普通代码而不是解构实现很有帮助。 (不确定编辑/重新发布 SO 代码或其他代码是否有用或策略,所以..)这 3 个函数是:HashPasswordVerifyHashedPassword link 1,然后是 ByteArraysEqual @ 987654324@.
    • 非常有帮助。另一个链接是单声道源代码(应该是相同且稳定的链接):github.com/mono/aspnetwebstack/blob/master/src/…
    【解决方案5】:

    我花了forever(花了几天时间)到find what to actually code 才让散列密码工作!所以为了方便我把它放在这里。

    您确实需要阅读documentationtheory1 theory2,然后阅读一些内容,否则您可能会遇到安全漏洞。安全是一个很大的话题!买家小心!

    将 NuGet 包 BCrypt.Net 添加到解决方案中

    const int WorkFactor = 14;
    var HashedPassword = BCrypt.Net.BCrypt.HashPassword(Password, WorkFactor); 
    

    您应该将WorkFactor 调整为合适的值,请参阅discussions。它是log2 function

    “数字是log2,所以每次计算机速度翻倍,默认数字加1。”

    然后您将散列密码作为passwordFromLocalDB 存储在您的数据库中,并像这样测试传入的password

    if (BCrypt.Net.BCrypt.Verify(password, passwordFromLocalDB) == true)
    

    祝你好运!

    【讨论】:

    • 你应该在前面加盐!非常重要!
    【解决方案6】:

    PBKDF2

    http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx 的示例中,当您到达“Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1, myIterations);", k1 是哈希值。示例用于加密的原因是 Rfc2898DeriveBytes 最初是为创建加密密钥而设计的。

    如果您不提供盐,Rfc2898DeriveBytes 将创建它自己的,但我不知道 RNGCryptoServiceProvider 在加密随机方面是否做得更好。

    根据 OWASP (https://www.owasp.org/index.php/Using_Rfc2898DeriveBytes_for_PBKDF2),Rfc2898DeriveBytes 对 SHA1 的底层使用意味着它仅适用于长度不超过 160 位的哈希。如果您创建更长的散列,攻击者仍然只需要担心前 160 位,但您让密码散列/身份验证对您自己来说更加昂贵,却没有任何收获。

    以下是 Rfc2898DeriveBytes 密码哈希的一些示例代码(将哈希、盐和迭代存储在数据库中):

    public class Rfc2898PasswordEncoder
    {
        private int _byteLength = 160 / 8; // 160 bit hash length
    
        public class EncodedPassword
        {
            public byte[] Hash { get; set; }
            public byte[] Salt { get; set; }
            public int Iterations { get; set; }
        }
    
        public EncodedPassword EncodePassword(string password, int iterations)
        {
            var populatedPassword = new EncodedPassword
            {
                Salt = CreateSalt(),
                Iterations = iterations
            };
    
            // Add Hash
            populatedPassword.Hash = CreateHash(password, populatedPassword.Salt, iterations);
    
            return populatedPassword;
        }
    
        public bool ValidatePassword(string password, EncodedPassword encodedPassword)
        {
            // Create Hash
            var testHash = CreateHash(password, encodedPassword.Salt, encodedPassword.Iterations);
    
            return testHash == encodedPassword.Hash;
        }
    
        public byte[] CreateSalt()
        {
            var salt = new byte[_byteLength]; // Salt should be same length as hash
    
            using (var saltGenerator = new RNGCryptoServiceProvider())
            {
                saltGenerator.GetBytes(salt);
            }
    
            return salt;
        }
    
        private byte[] CreateHash(string password, byte[] salt, long iterations)
        {
            byte[] hash;
            using (var hashGenerator = new Rfc2898DeriveBytes(password, salt, (int)iterations))
            {
                hash = hashGenerator.GetBytes(_byteLength);
            }
    
            return hash;
        }
    } 
    

    【讨论】:

      【解决方案7】:

      我对不涉及任何库的答案感兴趣。

      我阅读了这篇文章https://crackstation.net/hashing-security.htm,其中链接了不同语言 C# 的实现,我也会在这里链接

      https://github.com/defuse/password-hashing/blob/master/PasswordStorage.cs

      有趣的是,它使用了这里多次提到的 Rfc2898DeriveBytes。

      private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes){
          using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt)) {
              pbkdf2.IterationCount = iterations;
              return pbkdf2.GetBytes(outputBytes);
          }
      }
      

      【讨论】:

      • 这对我来说实际上是最好的答案,没有额外的库,它在System.Security.Cryptography 中,甚至哈希算法也是可选的。非常好,第一次尝试就成功了! (顺便说一句,当这个问题被问到时,这门课很可能不在 .net 中)
      【解决方案8】:

      Microsoft 为使用 .Net Core 的任何人提供了使用 PBKDF2 的示例代码页面:

      Hash passwords in ASP.NET Core

      来自文章:

      using System;
      using System.Security.Cryptography;
      using Microsoft.AspNetCore.Cryptography.KeyDerivation;
      
      public class Program
      {
          public static void Main(string[] args)
          {
              Console.Write("Enter a password: ");
              string password = Console.ReadLine();
      
              // generate a 128-bit salt using a secure PRNG
              byte[] salt = new byte[128 / 8];
              using (var rng = RandomNumberGenerator.Create())
              {
                  rng.GetBytes(salt);
              }
              Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
      
              // derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
              string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
                  password: password,
                  salt: salt,
                  prf: KeyDerivationPrf.HMACSHA1,
                  iterationCount: 10000,
                  numBytesRequested: 256 / 8));
              Console.WriteLine($"Hashed: {hashed}");
          }
      }
      
      /*
       * SAMPLE OUTPUT
       *
       * Enter a password: Xtw9NMgx
       * Salt: NZsP6NnmfBuYeJrrAKNuVQ==
       * Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
       */
      

      【讨论】:

        【解决方案9】:

        首先,我敦促大家使用平台自带的加密验证参考算法

        请勿使用 3rd 方包和未经验证的 OSS 组件或您刚刚从 Internet 复制粘贴的任何其他代码。

        对于 .NET,使用 PBKDF2不使用 bCrypt,因为 .NET 没有经过认证的 bCrypt 实现

        我并不是对任何高尚的开源开发者(我自己)有任何不尊重,但你永远无法确定他们的网站在 10 年内不会被黑客入侵,而你最终会从 Nuget/ 获得恶意软件包npm 或其他包管理器。

        更多关于验证的信息可以在this SO answer找到

        现在,回到 PBKDF2,这是简单的代码

        public static byte[] PBKDF2Hash(string input, byte[] salt)
        {
            // Generate the hash
            Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(input, salt, iterations: 5000);
            return pbkdf2.GetBytes(20); //20 bytes length is 160 bits
        }
        

        如果您需要哈希的字符串表示形式(不是字节数组) - 您可以使用此答案 http://stackoverflow.com/a/624379/714733 中的这个超快转换类

        【讨论】:

          猜你喜欢
          • 2011-05-24
          • 2016-01-13
          • 1970-01-01
          • 2017-09-17
          • 2016-04-29
          • 1970-01-01
          • 2012-07-28
          • 1970-01-01
          • 2017-12-13
          相关资源
          最近更新 更多