Hashing和GetString / GetBytes问题

时间:2013-05-02 15:48:04

标签: c# .net encryption hash salt

我有以下代码来哈希/存储/检索密码数据,但我的第一次单元测试却失败了。

我相信它的编码会导致问题,因为当调用GetBytes时,它会返回byte[38]byte[36],当它应该是20时我认为。

我必须将其转换为字符串,因为我将其存储在数据库中。

有什么想法吗?感谢

[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);
    }
}

3 个答案:

答案 0 :(得分:4)

使用Base64将二进制值编码为字符串,它可以处理任意字节序列。 UTF-8用于在unicode文本和字节之间进行转换,并非每个有效的字节序列对UTF-8都有效。使用Utf-8将密码(即文本)转换为字节,但使用Base64进行salt和hash。

Convert.ToBase64StringConvert.FromBase64String可以解决问题。


一些补充说明:

  • 您的术语非常奇怪,请勿调用哈希key,将其称为hash
  • 我会在你的CreatePasswordHash函数中连接哈希和salt,因此调用者不必为两个单独的值而烦恼。

    类似return Base64Encode(salt)+"$"+Base64Encode(hash)之类的内容,然后在验证功能中使用string.Split

  • 建议使用恒定时间比较来验证,但似乎不太可能实时利用时间旁路。

  • 您的迭代计数非常低。我建议将它增加到10000。

答案 1 :(得分:3)

修改代码以使用Convert.FromBase64String方法:

byte[] salt = Convert.FromBase64String(passwordSalt);
byte[] key = Convert.FromBase64String(passwordKey);

修改代码以使用Convert.ToBase64String方法:

passwordSalt = Convert.ToBase64String(salt);
passwordKey = Convert.ToBase64String(key);

答案 2 :(得分:1)

UTF8不是将任何随机字节转换为字符串的方法。它用于编码文本;不只是任何字节都是有效的UTF8编码值。您可以使用Base64 tofrom次转化。请注意,base64编码的字符串将占原始字节字符的~4 / 3倍。这是一个例子:

byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20);  // derive a 20-byte key

passwordSalt = Convert.ToBase64String(salt);
passwordKey = Convert.ToBase64String(key);

后来:

byte[] salt = Convert.FromBase64String(passwordSalt);
byte[] key = Convert.FromBase64String(passwordKey);