I have the below code to hash/store/retrieve data for passwords but my first unit test and it fails.
I beleive its the Encoding causing the problem because when GetBytes is called it returns byte[38], byte[36] when it should be 20 I think.
I have to convert to string as I'm storing it in a database.
Any ideas? Thanks
[Fact]
public void EncryptDecryptPasswordShouldMatch()
{
string password = "password";
string passwordKey = string.Empty;
string passwordSalt = string.Empty;
Helpers.CreatePasswordHash(password, out passwordSalt, out passwordKey);
Assert.True(Helpers.PasswordsMatch(passwordSalt, passwordKey, password));
}
public static bool PasswordsMatch(string passwordSalt, string passwordKey, string password)
{
byte[] salt = Encoding.UTF8.GetBytes(passwordSalt);
byte[] key = Encoding.UTF8.GetBytes(passwordKey);
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(20); // derive a 20-byte key
if (!newKey.SequenceEqual(key))
return false;
}
return true;
}
public static void CreatePasswordHash(string password, out string passwordSalt, out string passwordKey)
{
// specify that we want to randomly generate a 20-byte salt
using (var deriveBytes = new Rfc2898DeriveBytes(password, 20))
{
byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key
passwordSalt = Encoding.UTF8.GetString(salt);
passwordKey = Encoding.UTF8.GetString(key);
}
}
Use Base64 to encode binary values to string, it can deal with arbitrary byte sequences. UTF-8 is for transforming between unicode text and bytes and not every valid sequence of bytes is valid for UTF-8. Use Utf-8 to turn the password(which is text) to bytes, but use Base64 for salt and hash.
Convert.ToBase64String and Convert.FromBase64String should do the trick.
Some additional notes:
key, call it hash.I'd concatenate the hash and salt in your CreatePasswordHash function, so the caller doesn't have to bother with having two separate values.
Something like return Base64Encode(salt)+"$"+Base64Encode(hash) then use string.Split in the verification function.
It's recommended to use a constant time comparison to verify, but it seems unlikely your timing side-channel can actually be exploited.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With