如何在C#/ Bouncy Castle中创建PBKDF2-SHA256密码哈希

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

我下载了Bouncy Castle的回购,但是在unit testing中找到了我想要的东西。

在这里找到了一些示例代码,但这只是SHA1。 代码的关键位是:

///  /// Computes the PBKDF2-SHA1 hash of a password. ///  /// The password to hash. /// The salt. /// The PBKDF2 iteration count. /// The length of the hash to generate, in bytes. /// A hash of the password. 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。

从Java文档和这篇文章看 ,它看起来可能如下,但C#库中的构造函数没有重载。

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

找到关于堆栈溢出的另一篇文章 ,我认为以下可能是可能的,但SHA散列算法不在查找列表中,因此以下内容不起作用。

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

我需要做些什么才能让它正常工作?

注意:如果你读过这篇文章并且不知道如何在Bouncy Castle中,但确实知道另一种方式,我仍然感谢你的帮助。

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

现在有一个可以使用的Bouncy Castle Crypto API NuGet包。 或者,您可以直接从GitHub获取源代码 ,这将起作用。 我从NuGet获得了标准的Bouncy Castle,在撰写本文时尚未更新为1.8.1。

为了搜索者的利益,这里有一个用于散列的C#帮助程序类。 已经在multithreading上测试过,看起来很好。

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

 ///  /// Contains the relevant Bouncy Castle Methods required to encrypt a password. /// References NuGet Package BouncyCastle.Crypto.dll ///  public class BouncyCastleHashing { private SecureRandom _cryptoRandom; public BouncyCastleHashing() { _cryptoRandom = new SecureRandom(); } ///  /// Random Salt Creation ///  /// The size of the salt in bytes /// A random salt of the required size. public byte[] CreateSalt(int size) { byte[] salt = new byte[size]; _cryptoRandom.NextBytes(salt); return salt; } ///  /// Gets a PBKDF2_SHA256 Hash (Overload) ///  /// The password as a plain text string /// The salt for the password /// The number of times to encrypt the password /// The byte size of the final hash /// A base64 string of the hash. 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); } ///  /// Gets a PBKDF2_SHA256 Hash (CORE METHOD) ///  /// The password as a plain text string /// The salt as a byte array /// The number of times to encrypt the password /// The byte size of the final hash /// A the hash as a byte array. 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(); } ///  /// Validates a password given a hash of the correct one. (OVERLOAD) ///  /// The original password to hash /// The salt that was used when hashing the password /// The number of times it was encrypted /// The byte size of the final hash /// The hash the password previously provided as a base64 string /// True if the hashes match 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); } ///  /// Validates a password given a hash of the correct one (MAIN METHOD). ///  /// The password to check. /// A hash of the correct password. /// True if the password is correct. False otherwise. 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); } ///  /// 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. ///  /// The first byte array. /// The second byte array. /// True if both byte arrays are equal. False otherwise. 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)); }