.net C#中的散列密码Bcrypt/PBKDF 2

gpnt7bae  于 2023-01-22  发布在  .NET
关注(0)|答案(9)|浏览(145)

我查找了msdn和其他关于如何做到这一点的资源,但没有提出明确的解决方案。这是我找到的最好的http://blogs.msdn.com/b/shawnfa/archive/2004/04/14/generating-a-key-from-a-password.aspx?Redirected=true
我想使用bcrypt或PBKDF 2在C#中散列密码(这似乎与bcrypt有关)。我喜欢试验我的计算机需要多少轮来散列一个密码。然而,当每个人都在谈论散列时,似乎所有事情都是关于加密的。我想不通。我如何散列一个密码?它看起来更像PBKDF 2(Rfc 2898?)是一个随机数生成器,我使用GetBytes(amount)来选择哈希大小。
我很困惑,我到底该如何用bcrypt/PBKDF来哈希密码?

n1bvdmb6

n1bvdmb61#

PBKDF2

实际上,您已经非常接近了,您给出的链接向您展示了如何调用Rfc2898DeriveBytes函数来获取PBKDF2散列结果,但是,您被示例使用派生密钥进行加密的事实所迷惑(PBKDF1和2的最初动机是创建适合用作加密密钥的“密钥”导出函数)。当然,我们不想把输出用于加密,而是作为散列。
如果您想要PBKDF2,可以尝试使用专为此目的编写的SimpleCrypto.Net库。如果您查看实现,可以看到它实际上只是Rfc2898DeriveBytes的一个薄 Package 器(您猜对了)。

B隐窝

如果您想试验这个变体,可以尝试名为(或者其他名称)BCrypt.NET的C#实现。

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

gr8qqesn

gr8qqesn2#

首先,我强烈建议大家使用平台本身附带的 * 加密验证参考算法 *。

请勿使用第三方软件包和未经验证的OSS组件或您刚刚从互联网复制粘贴的任何其他代码。

对于.NET**,请使用PBKDF 2而不是bCrypt**,因为没有经过认证的bCrypt for .NET实现
我并不是说对任何高尚的开源开发人员(我自己就是一个)有任何不尊重,但是你永远不能确定他们的网站在10年内不会被黑客攻击,你最终会从Nuget/npm或其他软件包管理器那里得到一个恶意软件包。
有关验证的更多信息,请参见此SO答案
现在,回到PBKDF 2,下面是简单的代码

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

rxztt3cl

rxztt3cl3#

我花了forever(几天,它花了几天)到find what to actually code才让散列密码工作!!所以我把它放在这里是为了方便。
你确实需要阅读documentationtheory1theory2,然后一些或者你可能会打开安全漏洞。安全是一个非常大的主题!买家当心!
将NuGet包BCrypt.net添加到解决方案中

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

您应该将WorkFactor调整为适当的值,请参见讨论。它是log2 function
该数字为log2,因此计算机速度每增加一倍,就在默认数字上加1。
然后将散列密码存储在数据库中,作为passwordFromLocalDB,并测试传入的password,如下所示:

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

祝你好运!

up9lanfz

up9lanfz4#

编辑:正如@MikeT所指出的,我发布的原始链接和代码不再被认为是散列密码以存储在数据存储中的最佳实践。
Scott布雷迪的This article演示了如何使用PasswordHasher,以及如何使用该接口开发更强大的散列实现。
因此,您不再需要自己应用字节级散列函数,但如果您想了解如何实现,本文将探索PasswordHasher<T>实现:
Exploring the ASP.NET Core Identity PasswordHasher
文章中的一段相关代码如下所示:

private static byte[] HashPasswordV2(string password, RandomNumberGenerator rng)
{
    const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA1; // default for Rfc2898DeriveBytes
    const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes
    const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits
    const int SaltSize = 128 / 8; // 128 bits

    // Produce a version 2 text hash.
    byte[] salt = new byte[SaltSize];
    rng.GetBytes(salt);
    byte[] subkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength);

    var outputBytes = new byte[1 + SaltSize + Pbkdf2SubkeyLength];
    outputBytes[0] = 0x00; // format marker
    Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
    Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, Pbkdf2SubkeyLength);
    return outputBytes;
}

原答复:

Microsoft为任何使用.Net Core的用户提供了使用PBKDF 2的示例代码:
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=
 */
0kjbasz6

0kjbasz65#

今年早些时候,我在为我们的ASP.NETWeb窗体项目创建散列时也做了同样的事情,我想用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

6tqwzwtp

6tqwzwtp6#

我对不涉及任何图书馆的答案很感兴趣。
我读过这篇文章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);
    }
}
gojuced7

gojuced77#

对于PBKDF2,您可以使用系统。安全性。加密。
在此处查看MSDN:http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

rn0zuynd

rn0zuynd8#

PBKDF2使用HMACSHA1,如果您想要一个更现代化和可定制的解决方案,您应该看看这个使用HMACSHA256或512的API,就像PBKDF2一样具有键拉伸功能
https://sourceforge.net/projects/pwdtknet/
源代码中包含的示例GUI演示了如何从密码中获取哈希,包括创建加密随机盐......享受:)

ygya80vv

ygya80vv9#

PBKDF2
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx的示例中,当您到达行“Rfc 2898 DeriveBytes k1 = new Rfc 2898 DeriveBytes(pwd 1,salt 1,myIterations);“,k1是哈希。此示例用于加密的原因是Rfc 2898 DeriveBytes最初设计用于创建加密密钥。
如果您不提供salt,Rfc 2898 DeriveBytes将创建它自己的salt,但我不知道RNGCryptoServiceProvider在加密随机性方面是否做得更好。
根据OWASP(https://www.owasp.org/index.php/Using_Rfc2898DeriveBytes_for_PBKDF2),Rfc 2898 DeriveBytes对SHA1的底层使用意味着它只适用于长度不超过160位的散列,如果你创建了一个更长的散列,攻击者仍然只需要担心前160位,但是你已经使密码散列/认证对你自己来说更加昂贵,而没有任何好处。
下面是Rfc 2898 DeriveBytes密码散列的一些示例代码(将散列、盐和迭代存储在DB中):

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;
    }
}

相关问题