43

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

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

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

4

9 回答 9

37

PBKDF2

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

如果您想要 PBKDF2,您可以尝试为此目的编写的SimpleCrypto.Net库。如果您查看实现,您会发现它实际上只是(您猜对了)Rfc2898DeriveBytes的一个薄包装器。

BCrypt

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

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

于 2012-07-10T12:28:06.557 回答
13

首先,我敦促大家使用平台本身附带的经过密码验证的参考算法。

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

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

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

有关验证的更多信息可以在这个 SO 答案中找到

现在,回到 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使用这个超快转换类

于 2019-07-09T09:37:45.643 回答
11

我花了很长时间(几天花了几天)才找到实际编码的内容以使散列密码起作用!所以为了方便我把它放在这里。

您确实需要阅读文档理论 1 理论 2,然后阅读一些,否则您可能会遇到安全漏洞。安全是一个很大的话题!买家当心!

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

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

您应该将WorkFactor调整为合适的值,请参阅讨论。它是一个log2 函数

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

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

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

祝你好运!

于 2016-09-20T14:03:02.883 回答
5

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

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=
 */
于 2019-06-12T02:42:01.667 回答
3

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

我偶然发现了这个问题=> ASP.NET Identity 默认密码哈希,它是如何工作的,它是否安全? 然后我在这里找到了使用 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

于 2014-12-05T15:05:26.407 回答
2

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

我阅读了这篇文章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);
    }
}
于 2018-01-23T03:14:46.713 回答
1

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

请参阅此处的 MSDN:http: //msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

于 2012-07-10T12:23:23.573 回答
1

PBKDF2 使用 HMACSHA1,如果您想要更现代和可定制的解决方案,您应该使用 HMACSHA256 或 512 来查看这个 API,就像 PBKDF2 一样

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

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

于 2012-10-04T03:45:12.230 回答
1

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;
    }
} 
于 2017-01-27T10:03:50.333 回答