我使用以下代码为用户密码生成哈希值和盐:
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
}
user.passwordHash = hashed;
user.passwordSalt = System.Text.Encoding.Default.GetString(salt);
然后为了验证我正在执行以下操作:
string hash = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: passwordToValidate,
salt: Encoding.UTF8.GetBytes(passwordSalt),
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
return passwordHash == hash;
我从 this section of the asp.net core documentation 获得此代码.
- 盐通常是按照我的方式存储在自己的字段中,还是有一种方法将其全部存储在一个字段中,然后在验证时将其分开?
- 在保存到数据库时,我是否正确地将盐字节数组转换为字符串,并在验证时将盐字节数组正确转换回字节数组?
最佳答案
1) Salt 通常存储在其自己的字段中,但这不是必需的 - 如果需要,您可以将密码和 salt 一起存储在一个字段中。例如,您的代码 salt 具有固定大小(16 字节),并且您的哈希值也是固定大小(32 字节)。因此,您可以将它们一起存储在一个 48 字节的列中,并且始终可以毫无歧义地拆分它们。当然,你应该永远记住先放什么:盐还是 key 。如果您将它们存储在 Base-64 的字符串列中,就像您现在所做的那样,您可以添加分隔符(不是必需的) - 一些不能出现在 Base64 中的字符,例如分号。然后你可以存储“password_hash:salt”。我建议存储在二进制列中。
2)您转换错误。您将其视为 UTF-16 编码的字符串,但事实并非如此 - salt 是随机的字节集,而不是任何编码中的字符串。因此,请使用 base-64,就像您已经对哈希所做的那样(如果您坚持将其存储为字符串):
user.passwordSalt = Convert.ToBase64String(salt);
关于c# - 当为密码创建哈希+盐时,有没有办法避免将盐保存为数据库中的单独字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49336613/