SharpHash-完整的哈希库

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
是的我同意。可以强键入时为什么要键入字符串?因此,在链接的情况下,我认为将字节存储为字节并存储在单个数组中会更好。它还增加了额外的防御层。可以说,一个坏人设法获得数据库的副本,但是没有获得代码。他必须确定完整的40个字节是否是哈希密码,或者它的字节1-n是哈希,n-40是盐,反之亦然(对于n 2到39)。链接作者更加狡猾,他还可以将哈希字节和盐字节相互交错,使事情变得更加困难。
 

德马克点

成员
已加入
2016年1月20日
留言内容
9
编程经验
Beginner
谢谢回复。因为这是我第一次尝试了解PBKDF2。看来我将回答自己的问题,并根据我所看到的一些示例将一些代码拼凑在一起。但是,我总是愿意接受一些建议。例如,迭代应包含在生成的哈希中,并带有"|" or ":"以便在验证密码时可以将其分开,甚至在验证时也可以使用。

提供的代码是较小的实现。我创建了重载,这些重载之一允许自定义哈希算法,盐和迭代。

C#:
        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.ASCII.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 secure 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.
            var ByteNumber = salt.Length;
            if (ByteNumber > 3)
            {
                hashBytes = new byte[ByteNumber * 2];
            }

            // 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.ASCII.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 secure 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 Password:  Boolean tester = PBKDF2Helper.ValidatePBKDF2Password("password", GeneratedHash);
                        txtVerificationResults.Text = tester.ToString();



希望这可以对某人有所帮助。
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
什么 happens to your validate meeting when ByteSize is not 32?

什么 your generator when ByteSize is 3 or less?
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
当您的用户使用非ASCII字符作为密码时,会发生什么情况?
 

德马克点

成员
已加入
2016年1月20日
留言内容
9
编程经验
Beginner
谢谢您的意见。对于此功能,没有用于调整字节大小的设置。它将始终为32。现在,我创建了一个重载,在其中可以更改字节大小,并且需要更改字节大小的所有区域都将更改。对于非ASCII字符,我使用不同的哈希算法(例如SHA3-SHA384)测试了希伯来语(פשדד'םרג)和希腊语(πασσςορδ),并且当我删除一个字符时,验证返回true和false。

SHA3​​-SHA384
用户盐160bit
迭代次数= 10000

希伯来语结果:XCOY0qEgp7tFppjagmJCXJpiKYWbo / qM96fAqTlLz4XrBHE8tqtDcw ==
希腊文结果:e / 7jXc5Uhv7Fu1gcI0HkCkgJ4tRWIUwejWhJATjn8ammpMZye3PZhw ==

至于字节大小小于三。我认为那只是我混淆的位和字节。它应该为零,因为一个字节为八位,这是正确的格式。记住,这是我的第一次尝试。 :)

在我的设计思维帽中。想法是使验证方法与哈希密码生成方法相匹配。实际上,并不是仅创建一种能够匹配所有内容的验证方法;而是要创建一个验证方法。尽管可以做到。我认为这是供应商应该设法达到的目的,我最终只是想了解C#中PBKDF2的动态。
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
The problem is that with non-ASCII characters is that you are losing data when because of your use of Encoding.ASCII.GetBytes(). Notice the output of the following code:
C#:
using System;
using System.Linq;
using System.Text;

namespace ConsoleTest
{
    class Program
    {
        static void PrintCharsAndBytes(string text)
        {
            Console.WriteLine(text);
            foreach (var ch in text)
                Console.Write($"{(int)ch:x} ");
            Console.WriteLine();

            var bytes = Encoding.ASCII.GetBytes(text);
            foreach (var b in bytes)
                Console.Write($"{(int)b:x} ");
            Console.WriteLine();
        }

        static void Main()
        {
            PrintCharsAndBytes("פשדד'םרג");
            PrintCharsAndBytes("????'???");
        }
    }
}

输出:
C#:
????'???
5e4 5e9 5d3 5d3 27 5dd 5e8 5d2
3f 3f 3f 3f 27 3f 3f 3f
????'???
3f 3f 3f 3f 27 3f 3f 3f
3f 3f 3f 3f 27 3f 3f 3f
注意,每个希伯来语Unicode字符都被映射到ASCII问号(0x3f)。那么这意味着即使用户输入密码"פשדד'םרג",黑客可以输入"????'???"并进入,因为您的哈希计算和验证将匹配。

I suggest using Encoding.Unicode.GetBytes() because C# using Unicode strings internally.
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
对于此功能,没有用于调整字节大小的设置。它将始终为32。现在,我创建了一个重载,在其中可以更改字节大小,并且需要更改字节大小的所有区域都将更改。
而您需要更改多个位置的原因是因为您使用了 魔术数字 in ValidatePBKDF2Password() but correctly use constant identifiers in GetHashPBKDF2Password(). If you used the ByteSize in ValidatePBKDF2Password(), then all you would need to do is a one line change for the initialization of ByteSize and you are done. Avoid using magic numbers.
 

德马克点

成员
已加入
2016年1月20日
留言内容
9
编程经验
Beginner
我看...看起来像"Converters.ConvertStringToBytes"和Encoding.Unicode.GetBytes具有相同的输出,并且能够处理非ASCII字符。我注意到"Encoding.UTF8.GetBytes"还能够处理非ASCII字符,但输出不同。好的收获,谢谢。我将其更改为"Encoding.Unicode.GetBytes".

现在回答您的其他帖子。魔术数字不在思维范围内。但是我喜欢初始化一个名副其实的想法,然后就完成了。实际上看起来好像有两个名副其实的要初始化;盐和迭代。

从这一点上我看到的唯一问题是如何使哈希可选。例子:"IHash sha = HashFactory.Crypto.CreateSHA1()" or "HashFactory.Crypto.CreateSHA3_256()" etc.
 

德马克点

成员
已加入
2016年1月20日
留言内容
9
编程经验
Beginner
好吧,我已经听取了您的建议,并做了几件事。现在,我仅使用哈希密码方法和任何哈希算法的验证。我初始化了(ByteSize,SHAhashAlgorithm和Pbkdf2Iterations)。如果用户不初始化它们,则每个都有默认设置。

但是,尽管我可以设置SHA算法。这不是一种有效的方式。在游戏的这个阶段,我很难找到一种更有效的方法来处理算法选择。

例如,我创建了许多静态(IHash)实例,用户可以将它们分配给(IHash SHAhashAlgorithm)变量。但是,这不是最有效的方法。
C#:
 class
     //EXAMPLE:
     // For user use. Bwlow variables are to be used in the manual functions.
     public static Int32 UserSalt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes

        //=====================================================================
        //                   IHash SHA algorithm
        //======================================================================
        public static IHash SHA2_SHA1ForPBKDF2() // Need a more efficient way to assign the SHA algorithm.
        {
            // SHA2-SHA1
            var x = HashFactory.Crypto.CreateSHA1();
            return x;
        }

        //=====================================================================
        //                   Assignments with Default
        //======================================================================

        public static Int32 ByteSize { get; set; } = Salt256bit; // Default
        public static IHash SHAhashAlgorithm { get; set; } = SHA3_SHA256ForPBKDF2(); // Default
        public static UInt32 Pbkdf2Iterations { get; set; } = 50000; // Default

        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]);

            // Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
            IPBKDF2_HMAC pbkdf2 = HashHotel.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(SHAhashAlgorithm, Password, salt, Pbkdf2Iterations);
            byte[] hash = pbkdf2.GetBytes(ByteSize);

            // 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 for later use.
            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
                // Half the salt bytes of the salt in the GetHashPBKDF2PasswordTest method.
                byte[] salt = new byte[ByteSize];
                Array.Copy(hashBytes, 0, salt, 0, ByteSize);
                // Compute the hash on the password the user entered
                IPBKDF2_HMAC pbkdf2 = HashHotel.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(SHAhashAlgorithm, Password, salt, Pbkdf2Iterations);
                byte[] hash = pbkdf2.GetBytes(ByteSize);
                // compare the results
                for (int i = 0; i < ByteSize; i++)
                {
                    if (hashBytes[i + ByteSize] != hash[i])
                    {
                        result = false;
                    }
                }
                return result;
            }
            catch (Exception)
            {
                return false;
            }
        }

//===========================================================
            // Set custom variables:
            // PBKDF2Helper.SHAhashAlgorithm = PBKDF2Helper.SHA2_SHA1ForPBKDF2(); // Setting the SHA algorithm.
            // PBKDF2Helper.Pbkdf2Iterations = 10000; // Setting the Iteration.
            // PBKDF2Helper.ByteSize = PBKDF2Helper.UserSalt128bit; // Setting the salt value.

            // Test Password Hash.
            //txtMessageResults.Text = "";
            //txtMessageResults.Text = PBKDF2Helper.GetHashPBKDF2Password("password");
            //
            //Boolean x = PBKDF2Helper.ValidatePBKDF2Password("password", "BLAH, BLAH");
            //txtVerificationResults.Text = x.ToString();
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
我建议你研究一下"dependency injection".
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,609
地点
弗吉尼亚州切萨皮克
编程经验
10+
至于字节大小小于三。我认为那只是我混淆的位和字节。它应该为零,因为一个字节为八位,这是正确的格式。
我指的是这段代码:
C#:
// Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
var ByteNumber = salt.Length;
if (ByteNumber > 3)
{
    hashBytes = new byte[ByteNumber * 2];
}
what is hashBytes set to when ByteNumber <= 3. The reason why I was asking about ByteSize is because of this code: salt = new byte[ByteSize]
您可以在那里更改该代码以简单地:
C#:
// Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
if (ByteSize > 3)
{
    hashBytes = new byte[ByteSize * 2];
}

无论如何,看来您已在第29号帖子中的新代码中解决了该问题:
C#:
// 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];
 

德马克点

成员
已加入
2016年1月20日
留言内容
9
编程经验
Beginner
是的,更改了代码,它看起来和工作起来要好得多。现在,有了依赖项注入,我认为这将是艰巨的战斗,没有任何SharpHash示例可用于构建和参与。到目前为止,我一直在看一些YouTube的文章,并看过一些有关依赖注入的论文,它看起来很有希望,但是目前还不确定如何获得所有像这样的哈希算法"SHA2_SHA1ForPBKDF2()"在界面中,以便可以使用或引用它。实际上,我只会使用SHA2和SHA3。

因此,现在我将问您是否有一些示例,或者可以使用接口(依赖注入)提供一个代码示例来帮助我入门。这将是最有帮助的。谢谢。
 
最佳 底部