I need to create a PBKDF2-SHA256 password hash, but am having some trouble.
I downloaded the Bouncy Castle repo, but got a bit stuck finding what I was looking for in t
EDIT (Previous answer history removed for brevity)
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.
For the benefit of searchers, here is a C# helper class for hashing. Have tested on multiple threads and seems fine.
NOTE: This class also works for the .NET Core version of the library BouncyCastle.NetCore
///
/// 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;
}
}
Usage Example
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));
}