需要帮助在 C# 中为 PBKDF2 创建 IsValidPassword

Need help creating IsValidPassword for PBKDF2 in C#

在尝试创建 PBKDF2 有效密码检查程序时遇到了最困难的情况。 PBKDF2代码来自一个SharpHash项目; https://github.com/ron4fun/SharpHash。 Class 是:SharpHash/SharpHash.Tests/KDF/PBKDF2_HMACTests.cs

该示例展示了如何实现它,但没有任何关于之后如何验证哈希的示例。我设法尝试了几种不同的方法 "IsValidPassword" 是其中一种方法,但其中 none 似乎有效。无论我向 PBKDF2 或 IsValidPassword 方法添加什么值,它们中的每一个结果都是错误的。我也尝试更改为十六进制和 base64,但得到了相同的结果;失败了。

我什至替换了 Rfc2898DeriveBytes。

有没有人有过 PBKDF2 密码验证的经验。这将基于应用程序,而不是基于网站。 IDE 环境 Visual Studios 2019 - C#。

谢谢。

    public void TestOne()
    {
        IPBKDF2_HMAC PBKDF2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash, Password, Salt, 100000);
        byte[] Key = PBKDF2.GetBytes(64);
        PBKDF2.Clear();

        string ActualString = Converters.ConvertBytesToHexString(Key, false);

        Assert.AreEqual(ExpectedString, ActualString);
    }



public bool IsValidPassword(string password, string hashPass)
    {
        bool result = false;

        // Extract the bytes
        byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
        // Get the salt
        byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
        Array.Copy(hashBytes, 0, salt, 0, 20);// Doesn't matter what values and here; same issue… False
        // Compute the hash on the password the user entered
        var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
        byte[] hash = pbkdf2.GetBytes(64);
        // compare the results
        for (int i = 0; i < 20; i++) // If I go to 64 I get an error
        {
            if (hashBytes[i + 20] != hash[i])
            {
                return false;
            }
        }

        return true;
    }


// Replaced Rfc2898DeriveBytes
        public bool IsValidPassword(string password, string hashPass)
    {
        bool result = false;
        IHash hash1 = HashFactory.Crypto.CreateSHA1();
        // Extract the bytes
        byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
        byte[] Password = Encoding.ASCII.GetBytes(password);
        // Get the salt
        byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
        Array.Copy(hashBytes, 0, salt, 0, 20); // Doesn't matter what values and here; same issue… False
        // Compute the hash on the password the user entered
        var pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, 100000); // Replaced Rfc2898DeriveBytes
        byte[] Key = pbkdf2.GetBytes(64);
        pbkdf2.Clear();
        string test = Converters.ConvertBytesToHexString(Key, false); // Taking a peek
        string test2 = Encoding.ASCII.GetString(hashBytes); // Taking a peek
        // compare the results
        for (int i = 0; i < 20; i++)
        {
            if (hashBytes[i + 20] != Key[i])
            {
                return false;
            }
        }

        return true;
    }

快速看一下,您遇到的问题只是对所涉及数据内容的错误陈述。 password 是基于 base256 的数据,而 hashPass 是基于 base16 的数据。 将 hashPass 转换为 byte[] 时,您必须使用适当的转换例程。 为此,也只需使用转换器 class 中的 this 方法。

byte[] hashBytes = Converters.ConvertHexStringToBytes(hashPass);

请注意,我假设您的密码在 base256 中(因为您没有在问题中指定),所以您可以保留原样。 您需要做的唯一更改是我在上面描述的更改。

好吧,我想这会对以后的其他人有所帮助。我不得不进行一些认真的搜索并将我的头撞到墙上,因为这是我在 PBKDF2 中第一次尝试提出一种功能验证方法,该方法不包含在 SharpHash 项目中。只是为了让任何阅读本文的人都能理解我的问题所在。该代码生成的密码没有任何问题。但是,代码项目中并没有实际使用salt、生成密码和迭代验证密码的函数。

提供的代码是简单版本,因为我还为不需要的方法添加了重载 post。此方法具有默认设置,而其中一个重载允许完全自定义哈希算法、salt 和迭代。我已经测试了其中的每一个,并且它们按预期工作。

希望这对某人有所帮助。 :-)

private static Int32 Salt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes
        public static string GetHashPBKDF2Password(string password)
    {
        // Notes:
        // Create a 32-byte secure salt and the same size of the key. This 32-byte produces 256 bits key output.
        // Add the same 32-byte size to the pbkdf2.GetBytes.
        // KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC hashes these values using the crypto SHA presented.
        // Double the hashBytes bytes then add the salt and pbkdf2.GetBytes value.
        // Copy each of the 32-bytes to the hashBytes bytes from the hashed salt and hashed value.
        //
        byte[] Password = Encoding.Unicode.GetBytes(password);
        string hashPass = string.Empty;

        // Create the salt value with a cryptographic PRNG
        byte[] salt;
        new RNGCryptoServiceProvider().GetBytes(salt = new byte[ByteSize]); // 32 Bytes = 256 bits.

        // Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
        // I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
        IHash sha = HashFactory.Crypto.CreateSHA1();
        IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(sha, Password, salt, Pbkdf2Iterations);
        byte[] hash = pbkdf2.GetBytes(ByteSize); // 32 Bytes = 256 bits.

        // Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
        Int32 g = hash.Length + salt.Length;
        byte[] hashBytes = new byte[g];

        // Combine the salt and password bytes.
        Array.Copy(salt, 0, hashBytes, 0, ByteSize);
        Array.Copy(hash, 0, hashBytes, ByteSize, ByteSize);

        // Turn the combined salt+hash into a string for storage
        hashPass = Convert.ToBase64String(hashBytes);

        return hashPass;
    }

    public static bool ValidatePBKDF2Password(string password, string hashPass)
    {
        try
        {
            byte[] Password = Encoding.Unicode.GetBytes(password);
            bool result = true;

            // Extract the bytes
            byte[] hashBytes = Convert.FromBase64String(hashPass);
            // Get the salt
            byte[] salt = new byte[32];
            Array.Copy(hashBytes, 0, salt, 0, 32);
            // Compute the hash on the password that user entered.
            // I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
            IHash hash1 = HashFactory.Crypto.CreateSHA1();
            IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, Pbkdf2Iterations);
            byte[] hash = pbkdf2.GetBytes(32);
            // compare the results
            for (int i = 0; i < 32; i++)
            {
                if (hashBytes[i + 32] != hash[i])
                {
                    result = false;
                }
            }
            return result;
        }
        catch (Exception)
        {
            return false;
        }
    }


    How to use:     string GeneratedHash = PBKDF2Helper.GetHashPBKDF2Password("password");
    Results: hv6t8N4rrVSKYFm80cCoVUEiUk2o11xLBc6lJb5kBXKTcwcKwl9dZwSdce01X0bi8BBhJY/QGGnNVAcR7ZhSvQ==

    Verify Paword:  Boolean tester = PBKDF2Helper.ValidatePBKDF2Password("password", GeneratedHash);
                    txtVerificationResults.Text = tester.ToString();