RNGCryptoServiceProvider不会为相同的PWD、Salt、Iteration组合生成相同的哈希

本文关键字:哈希 组合 Iteration PWD RNGCryptoServiceProvider Salt | 更新日期: 2023-09-27 18:29:56

Oki所以我想通过在.Net中添加随机salt来散列我的密码。我为此使用的内置类是RNGCryptoServiceProvider-生成随机salt,Rfc2898DeriveBytes-散列实际密码。

但是当我为passwordString的相同组合调用Rfc2898DeriveBytes的GetBytes()函数时;迭代次数-结果不同。我正在粘贴我的代码以供参考

    public class PBKDF2Implementation
    {
        int minSaltSize = 32;
        int maxSaltSize = 64;

        public string CreateHash(string plainText , out string  salt)
        {
            Random random = new Random();
            int saltSize = random.Next(minSaltSize, maxSaltSize);
            // Allocate a byte array, which will hold the salt.
            byte[] saltBytes = new byte[saltSize];
            // Initialize a random number generator.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            // Fill the salt with cryptographically strong byte values.
            rng.GetNonZeroBytes(saltBytes);
            string strSalt = System.Text.Encoding.ASCII.GetString(saltBytes);
            //string strSalt = System.Convert.ToBase64String(saltBytes);
            salt = strSalt;
            Console.WriteLine(saltBytes.Count());
            Console.WriteLine(strSalt);
            Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes(plainText, saltBytes, 1000);
            // generate an RC2 key
            byte[] key = pwdGen.GetBytes(32);
            Console.WriteLine(System.Convert.ToBase64String(key));
            return System.Convert.ToBase64String(key);
        }
        public bool CompareHash(string plainText, string salt, string hashValue)
        {
            byte[] saltBytes = System.Text.Encoding.ASCII.GetBytes(salt);
            //byte[] saltBytes = System.Convert.FromBase64String(salt);
            Console.WriteLine(saltBytes.Count());
            Console.WriteLine(System.Text.Encoding.ASCII.GetString(saltBytes));
            Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes(plainText, saltBytes, 1000);
            // generate an RC2 key
            byte[] key = pwdGen.GetBytes(32);
            Console.WriteLine(System.Convert.ToBase64String(key));
            return System.Convert.ToBase64String(key) == hashValue;
        }
    }

在我的测试课上,我有这个函数

    [Test]
    public void shouldGenerateConsistentHash()
    {
        PBKDF2Implementation cPbkdf2Implementation = new PBKDF2Implementation();
        string salt;
        string hashValue = cPbkdf2Implementation.CreateHash("password", out salt);
        bool result = cPbkdf2Implementation.CompareHash("password", salt, hashValue);
        Assert.IsTrue(result) ;
    }

测试失败
但是如果在上面的类PBKDF2实现中,如果我替换行System.Text.Encoding.ASCII.GetString与System.Text.Eccoding.Unicode.GetString和System.Text.Encoding.ASCII.GetBytes与System.Text.Eccoding.Unicode.GetBytes

测试通过。知道为什么会发生这种事吗?我想让它与ASCII编码一起工作的原因是,存储这个哈希值的同一个DB也将被PHP应用程序使用;只有当salt编码是ASCII时,PHP的PBKDF2实现产生的哈希值才与Rfc2898DeriveBytes的哈希值匹配。

以下是相同的PHP实现http://www.php.net/manual/en/function.hash-hmac.php#101540

RNGCryptoServiceProvider不会为相同的PWD、Salt、Iteration组合生成相同的哈希

  1. 避免将字符串与crypto一起使用。使用字节数组,即byte[]
  2. 如果必须使用字符串,请非常小心编码。它们会咬你9次(满分10次)

例如

byte [] data = new byte [] { 65, 0, 65 };
var s = System.Text.Encoding.ASCII.GetString (data);

s值是多少?

答案:"A"

如果你从同一个字符串中提取byte[],你会得到:byte [1] { 65 },它与原始字符串不同,不适用于大多数加密用法。

Base64更安全,因为它可以保持每个字节的完整性。