C#中的哈希密码?Bcrypt/PBKDF2

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11412882/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 17:46:35  来源:igfitidea点击:

Hash Password in C#? Bcrypt/PBKDF2

c#.nethashpasswordsbcrypt

提问by

I looked up msdn and other resources on how to do this but i came up with no clear solutions. This is the best i found http://blogs.msdn.com/b/shawnfa/archive/2004/04/14/generating-a-key-from-a-password.aspx?Redirected=true

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

I would like to hash passwords in C# using either bcrypt or PBKDF2 (which appears to be bcrypt related). I like to experiment with how many rounds it takes for my computer to hash a password. However everything seems to be about encrypting while everyone talks about hashing. I can't figure it out. How do i hash a password? It looks more like PBKDF2 (Rfc2898?) is a random number generator and i use GetBytes(amount) to choose how big my hash size is.

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

I'm confused. How exactly do i hash a password with bcrypt/PBKDF?

我糊涂了。我究竟如何使用 bcrypt/PBKDF 散列密码?

采纳答案by paracycle

PBKDF2

PBKDF2

You were really close actually. The link you have given shows you how you can call the Rfc2898DeriveBytesfunction to get PBKDF2 hash results. However, you were thrown off by the fact that the example was using the derived key for encryption purposes (the original motivation for PBKDF1 and 2 was to create "key" derivation functions suitable for using as encryption keys). Of course, we don't want to use the output for encryption but as a hash on its own.

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

You can try the SimpleCrypto.Netlibrary written for exactly this purpose if you want PBKDF2. If you look at the implementation, you can see that it is actually just a thin wrapper around (you guessed it) Rfc2898DeriveBytes.

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

BCrypt

比特币

You can try the C# implementation named (what else) BCrypt.NETif you want to experiment with this variant.

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

Disclaimer:I have not used or tested any of the libraries that I have linked to... YMMV

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

回答by Jay S

For PBKDF2, you might be able to use System.Security.Cryptography.Rfc2898DeriveBytes.

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

See MSDN here: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

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

回答by thashiznets

PBKDF2 uses HMACSHA1, if you would like a more modern and customisable solution you should look at this API using HMACSHA256 or 512 with key stretching just like PBKDF2

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

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

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

Sample GUI included in source code demonstrated how to get a hash from a password including the creation of crypto random salt.....enjoy :)

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

回答by Luke T O'Brien

Earlier this year I was looking into the same thing for creating hashes for our ASP.NET Web Forms project, I wanted to do it the same way MVC projects do it out of the box.

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

I stumbled upon this question => ASP.NET Identity default Password Hasher, how does it work and is it secure?Then I found the source with the ByteArraysEqual method here => 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

我偶然发现了这个问题 => 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

回答by JPK

It took me forever(days it took days) to find what to actually codeto get hashed passwords to work!! so I put it here for convenience.

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

You do need to read the documentationand theory1theory2and then some or you could be open to security loopholes. Security is a very big topic! Buyer Beware!

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

Add the NuGet Package BCrypt.Net to the solution

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

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

You should adjust the WorkFactorto what is appropriate see discussions. Its a log2 function

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

"The number is log2, so every time computers double in speed, add 1 to the default number."

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

Then you store the hashed password in your db as passwordFromLocalDBand to test an incoming passwordlike this:

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

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

Good Luck!

祝你好运!

回答by Phil

PBKDF2

PBKDF2

In the example in http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx, when you get to the line "Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1, myIterations);", k1 is the hash. The reason the example is for encryption is that Rfc2898DeriveBytes was originally designed to create encryption keys.

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

If you do not provide a salt, Rfc2898DeriveBytes will create it's own, but I do not know whether RNGCryptoServiceProvider does a better job of being cryptographically random.

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

According to OWASP (https://www.owasp.org/index.php/Using_Rfc2898DeriveBytes_for_PBKDF2), the underlying use of SHA1 by Rfc2898DeriveBytes means it's only good for hashes up to 160 bits in length. If you create a longer hash, an attacker still only has to worry about the first 160 bits, but you have made password hashing/authentication more expensive for yourself with no gain.

根据 OWASP ( https://www.owasp.org/index.php/Using_Rfc2898DeriveBytes_for_PBKDF2),Rfc2898DeriveBytes 对 SHA1 的底层使用意味着它只适用于长度高达 160 位的哈希。如果您创建更长的散列,攻击者仍然只需要担心前 160 位,但是您已经使密码散列/身份验证变得更加昂贵而没有任何收益。

Here's some example code for Rfc2898DeriveBytes password hashing (store the hash, salt and iterations in the DB):

以下是 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;
    }
} 

回答by CyberFox

i was interested in an answers that didn't involve any libraries.

我对不涉及任何图书馆的答案感兴趣。

I read this article https://crackstation.net/hashing-security.htmwhich links an implementation in different languages C# among them which i will link here too

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

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

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

interestingly it uses Rfc2898DeriveBytes as mentioned a few times here.

有趣的是,它使用了 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);
    }
}

回答by pcdev

Microsoft has a page up with sample code using PBKDF2 for anyone using .Net Core:

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

Hash passwords in ASP.NET Core

ASP.NET Core 中的哈希密码

From the article:

从文章:

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=
 */

回答by Alex

First of all, I urge everyone to use a cryptographically verified reference algorithmincluded with the platform itself.

首先,我敦促大家使用平台本身包含的经过加密验证的参考算法

Do not use 3rd party packages and non-verified OSS componentsor any other code you just copy-pasted from the Internet.

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

For .NET use PBKDF2and not bCryptbecause there's no certified implementation of bCrypt for .NET

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

I don't mean any disrespect for any noble open-source devs (being one myself), but you can never be sure their website won't be hacked in 10 years and you end up getting a malware package from Nuget/npm or other package managers.

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

More info about verification can be found in this SO answer

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

Now, back to PBKDF2, here's the simple code

现在,回到 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
}

If you need a string representation of the hash (not byte-array) - you can use this superfast conversion class from this answer http://stackoverflow.com/a/624379/714733

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