我正在创建一个小型的C#WinForms应用程序,但是在验证/验证哈希密码方面遇到了问题。
创建用户后,他们的密码将被散列并存储在数据库中。我不知道如何比较输入的密码(当用户登录时)与数据库中的哈希密码。
创建哈希的代码如下:
private static string CreateHashedPassword(string username, string plainTextPassword)
{
byte[] salt;
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
System.Security.Cryptography.Rfc2898DeriveBytes pbkdf2 = new System.Security.Cryptography.Rfc2898DeriveBytes(plainTextPassword, salt, 3000);
byte[] hash = pbkdf2.GetBytes(20);
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
return Convert.ToBase64String(hashBytes);
}
当用户尝试登录时,我再次调用此方法获取散列密码以与数据库进行比较,但它从不匹配。
我很确定这是因为每次运行该方法时都会创建一个新的salt,所以我很确定修复它的方法(但我不知道如何)是:
将salt保存在数据库中或
使用类似用户名的内容来创建salt。这是首选方法,但我不知道如何从预定义的字符串派生盐,如用户名。
有什么想法/指示吗?谢谢!
答案 0 :(得分:1)
这是因为您使用随机方法生成它的哈希方式。
RNGCryptoServiceProvider 使用加密服务提供商(CSP)提供的实现实现加密
Random Number Generator
(RNG)。这个类不能被继承。
您可以使用此方法进行哈希:
public static string GenerateKeyHash(string Password)
{
if (string.IsNullOrEmpty(Password)) return null;
if (Password.Length < 1) return null;
byte[] salt = new byte[20];
byte[] key = new byte[20];
byte[] ret = new byte[40];
try
{
using (RNGCryptoServiceProvider randomBytes = new RNGCryptoServiceProvider())
{
randomBytes.GetBytes(salt);
using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
{
key = hashBytes.GetBytes(20);
Buffer.BlockCopy(salt, 0, ret, 0, 20);
Buffer.BlockCopy(key, 0, ret, 20, 20);
}
}
// returns salt/key pair
return Convert.ToBase64String(ret);
}
finally
{
if (salt != null)
Array.Clear(salt, 0, salt.Length);
if (key != null)
Array.Clear(key, 0, key.Length);
if (ret != null)
Array.Clear(ret, 0, ret.Length);
}
}
和这种比较密码的方法:
public static bool ComparePasswords(string PasswordHash, string Password)
{
if (string.IsNullOrEmpty(PasswordHash) || string.IsNullOrEmpty(Password)) return false;
if (PasswordHash.Length < 40 || Password.Length < 1) return false;
byte[] salt = new byte[20];
byte[] key = new byte[20];
byte[] hash = Convert.FromBase64String(PasswordHash);
try
{
Buffer.BlockCopy(hash, 0, salt, 0, 20);
Buffer.BlockCopy(hash, 20, key, 0, 20);
using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
{
byte[] newKey = hashBytes.GetBytes(20);
if (newKey != null)
if (newKey.SequenceEqual(key))
return true;
}
return false;
}
finally
{
if (salt != null)
Array.Clear(salt, 0, salt.Length);
if (key != null)
Array.Clear(key, 0, key.Length);
if (hash != null)
Array.Clear(hash, 0, hash.Length);
}
}