2017-06-11 99 views
0

我正在创建一个小的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); 
    } 

当用户试图登录,我再次调用此方法来获取哈希密码进行比较的数据库,但它永远不会匹配。

我敢肯定,这是因为新的盐是每个方法运行时创建的,所以我敢肯定的方式来解决它(但我不知道如何)是两种:

  1. 保存在数据库中的盐或

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

任何想法/指针?谢谢!

+0

你在做从头定制认证/授权或您使用的一些图书馆为此目的? –

+0

那么你实施了哪一个,你遇到了什么问题? –

+0

下面是一个链接,以便我使用pbkdf2为密码散列/比较器创建完整解决方案:[使用Pbkdf2的完整密码散列解决方案](https://stackoverflow.com/documentation/c%23/2774/哈希函数/ 15470 /完整密码哈希解决方案使用pbkdf2#t = 20170611103527142945) – Igor

回答

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); 
    } 
} 
+0

@Benny你测试过它吗? –