如何在C#/Bouncy Castle中创建PBKDF2-SHA256密码哈希 [英] How to create a PBKDF2-SHA256 password hash in C# / Bouncy Castle

查看:130
本文介绍了如何在C#/Bouncy Castle中创建PBKDF2-SHA256密码哈希的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要创建PBKDF2-SHA256密码哈希,但是遇到了一些麻烦.

I need to create a PBKDF2-SHA256 password hash, but am having some trouble.

我下载了 Bouncy Castle 回购,但是发现我在找什么有点卡住了在单元测试中.

I downloaded the Bouncy Castle repo, but got a bit stuck finding what I was looking for in the Unit Tests.

此处找到了一些示例代码,但这仅对SHA1有效.代码的关键部分是:

Found some sample code here, but this only does SHA1. The key bit of code is:

/// <summary>
/// Computes the PBKDF2-SHA1 hash of a password.
/// </summary>
/// <param name="password">The password to hash.</param>
/// <param name="salt">The salt.</param>
/// <param name="iterations">The PBKDF2 iteration count.</param>
/// <param name="outputBytes">The length of the hash to generate, in bytes.</param>
/// <returns>A hash of the password.</returns>
private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
{
    var pdb = new Pkcs5S2ParametersGenerator();
    pdb.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt,
                 iterations);
    var key = (KeyParameter)pdb.GenerateDerivedMacParameters(outputBytes * 8);
    return key.GetKey();
}

我需要将其从SHA1更改为SHA256.

I need to change this from SHA1 to SHA256.

从Java文档和这篇文章来看,可能会出现以下情况,但不会造成重载C#库中的构造函数.

From the Java documentation and this post, it looked like the following would be possible, but there is no overload on the constructor in the C# library.

var pdb = new Pkcs5S2ParametersGenerator(new Sha256Derived());

找到有关堆栈溢出的另一篇文章,我认为可能可行,但SHA哈希算法不在查找列表,因此以下内容将不起作用.

Finding another article on stack overflow, i thought the following might be possible, but the SHA hashing algorithms are not in the lookup list ,so the following will not work.

var bcparam = (KeyParameter)pdb.GenerateDerivedParameters("sha256", outputBytes * 8);

我该怎么做才能使它正常工作?

What do I need to do to get this working please?

注意:如果您阅读了此书,但不知道Bouncy Castle的使用方法,但确实知道另一种方法,我仍然感谢您的帮助.

Note: If you read this and don't know how in Bouncy Castle, but do know another way, I'd still appreciate your help.

推荐答案

编辑(为简洁起见,删除了以前的回答历史记录)

EDIT (Previous answer history removed for brevity)

现在有一个 Bouncy Castle Crypto API NuGet可以使用的包装.另外,您也可以直接从GitHub获取,它将起作用.我从NuGet获得了标准的Bouncy Castle,在撰写本文时尚未将其更新为1.8.1.

There is now a Bouncy Castle Crypto API NuGet package that can be used. Alternatively, you can get the source directly from GitHub, which will work. I had got the standard Bouncy Castle from NuGet, which had not been updated to 1.8.1 at the time of writing.

为了使搜索者受益,这是一个用于哈希处理的C#帮助器类.已经在多个线程上进行了测试,看起来还不错.

For the benefit of searchers, here is a C# helper class for hashing. Have tested on multiple threads and seems fine.

注意:此类也适用于库的.NET Core版本 BouncyCastle.NetCore

NOTE: This class also works for the .NET Core version of the library BouncyCastle.NetCore

/// <summary>
/// Contains the relevant Bouncy Castle Methods required to encrypt a password.
/// References NuGet Package BouncyCastle.Crypto.dll
/// </summary>
public class BouncyCastleHashing
{
    private SecureRandom _cryptoRandom;

    public BouncyCastleHashing()
    {
        _cryptoRandom = new SecureRandom();
    }

    /// <summary>
    /// Random Salt Creation
    /// </summary>
    /// <param name="size">The size of the salt in bytes</param>
    /// <returns>A random salt of the required size.</returns>
    public byte[] CreateSalt(int size)
    {
        byte[] salt = new byte[size];
        _cryptoRandom.NextBytes(salt);
        return salt;
    }

    /// <summary>
    /// Gets a PBKDF2_SHA256 Hash  (Overload)
    /// </summary>
    /// <param name="password">The password as a plain text string</param>
    /// <param name="saltAsBase64String">The salt for the password</param>
    /// <param name="iterations">The number of times to encrypt the password</param>
    /// <param name="hashByteSize">The byte size of the final hash</param>
    /// <returns>A base64 string of the hash.</returns>
    public string PBKDF2_SHA256_GetHash(string password, string saltAsBase64String, int iterations, int hashByteSize)
    {
        var saltBytes = Convert.FromBase64String(saltAsBase64String);

        var hash = PBKDF2_SHA256_GetHash(password, saltBytes, iterations, hashByteSize);

        return Convert.ToBase64String(hash);
    }

    /// <summary>
    /// Gets a PBKDF2_SHA256 Hash (CORE METHOD)
    /// </summary>
    /// <param name="password">The password as a plain text string</param>
    /// <param name="salt">The salt as a byte array</param>
    /// <param name="iterations">The number of times to encrypt the password</param>
    /// <param name="hashByteSize">The byte size of the final hash</param>
    /// <returns>A the hash as a byte array.</returns>
    public byte[] PBKDF2_SHA256_GetHash(string password, byte[] salt, int iterations, int hashByteSize)
    {
        var pdb = new Pkcs5S2ParametersGenerator(new Org.BouncyCastle.Crypto.Digests.Sha256Digest());
        pdb.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt,
                     iterations);
        var key = (KeyParameter)pdb.GenerateDerivedMacParameters(hashByteSize * 8);
        return key.GetKey();
    }

    /// <summary>
    /// Validates a password given a hash of the correct one. (OVERLOAD)
    /// </summary>
    /// <param name="password">The original password to hash</param>
    /// <param name="salt">The salt that was used when hashing the password</param>
    /// <param name="iterations">The number of times it was encrypted</param>
    /// <param name="hashByteSize">The byte size of the final hash</param>
    /// <param name="hashAsBase64String">The hash the password previously provided as a base64 string</param>
    /// <returns>True if the hashes match</returns>
    public bool ValidatePassword(string password, string salt, int iterations, int hashByteSize, string hashAsBase64String)
    {
        byte[] saltBytes = Convert.FromBase64String(salt);
        byte[] actualHashBytes = Convert.FromBase64String(hashAsBase64String);
        return ValidatePassword(password, saltBytes, iterations, hashByteSize, actualHashBytes);
    }

    /// <summary>
    /// Validates a password given a hash of the correct one (MAIN METHOD).
    /// </summary>
    /// <param name="password">The password to check.</param>
    /// <param name="correctHash">A hash of the correct password.</param>
    /// <returns>True if the password is correct. False otherwise.</returns>
    public bool ValidatePassword(string password, byte[] saltBytes, int iterations, int hashByteSize, byte[] actualGainedHasAsByteArray)
    {
        byte[] testHash = PBKDF2_SHA256_GetHash(password, saltBytes, iterations, hashByteSize);
        return SlowEquals(actualGainedHasAsByteArray, testHash);
    }

    /// <summary>
    /// Compares two byte arrays in length-constant time. This comparison
    /// method is used so that password hashes cannot be extracted from
    /// on-line systems using a timing attack and then attacked off-line.
    /// </summary>
    /// <param name="a">The first byte array.</param>
    /// <param name="b">The second byte array.</param>
    /// <returns>True if both byte arrays are equal. False otherwise.</returns>
    private bool SlowEquals(byte[] a, byte[] b)
    {
        uint diff = (uint)a.Length ^ (uint)b.Length;
        for (int i = 0; i < a.Length && i < b.Length; i++)
            diff |= (uint)(a[i] ^ b[i]);
        return diff == 0;
    }

}

用法示例

public void CreatePasswordHash_Single()
{
    int iterations = 100000; // The number of times to encrypt the password - change this
    int saltByteSize = 64; // the salt size - change this
    int hashByteSize = 128; // the final hash - change this

    BouncyCastleHashing mainHashingLib = new BouncyCastleHashing();

    var password = "password"; // That's really secure! :)

    byte[] saltBytes = mainHashingLib.CreateSalt(saltByteSize);
    string saltString = Convert.ToBase64String(saltBytes);

    string pwdHash = mainHashingLib.PBKDF2_SHA256_GetHash(password, saltString, iterations, hashByteSize);

    var isValid = mainHashingLib.ValidatePassword(password, saltBytes, iterations, hashByteSize, Convert.FromBase64String(pwdHash));

}

这篇关于如何在C#/Bouncy Castle中创建PBKDF2-SHA256密码哈希的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆