Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hashing and GetString/GetBytes issue

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);
    }
}
like image 775
Jon Avatar asked Feb 09 '26 20:02

Jon


1 Answers

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:

  • Your terminology is really weird, don't call the hash 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.

  • Your iteration count is pretty low. I recommend increasing it to 10000.
like image 57
CodesInChaos Avatar answered Feb 12 '26 14:02

CodesInChaos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!