验证哈希密码

时间:2017-06-11 10:20:40

标签: c# winforms cryptography

我正在创建一个小型的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,所以我很确定修复它的方法(但我不知道如何)是:

  1. 将salt保存在数据库中或

  2. 使用类似用户名的内容来创建salt。这是首选方法,但我不知道如何从预定义的字符串派生盐,如用户名。

  3. 有什么想法/指示吗?谢谢!

1 个答案:

答案 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);
    }
}