RNGCryptoServiceProvider не создает одинаковый хеш для одной и той же комбинации PWD, Salt, Iteration.

Итак, я хочу хешировать свой пароль, добавляя случайную соль в .Net. Встроенные классы, которые я использую для этой цели, — это RNGCryptoServiceProvider — для генерации случайной соли и Rfc2898DeriveBytes — для хеширования фактического пароля.

Но когда я вызываю функцию GetBytes() Rfc2898DeriveBytes для той же комбинации passwordString, SaltBytes и счетчика итераций, результат будет другим. Я вставляю свой код для справки

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

Тест не пройден.
Но если в приведенном выше классе PBKDF2Implementation заменить строки System.Text.Encoding.ASCII.GetString на System.Text.Encoding.Unicode.GetString и System.Text.Encoding.ASCII.GetBytes на System .Text.Encoding.Unicode.GetBytes

Тест проходит. есть идеи, почему это происходит? Причина, по которой я хочу заставить его работать с кодировкой ASCII, заключается в том, что та же БД, в которой хранится это хэш-значение, также будет использоваться приложением PHP, а хэш-значение, созданное реализацией PBKDF2 PHP, соответствует хеш-значению Rfc2898DeriveBytes, только если кодировка соли ASCII.

Вот реализация PHP того же http://www.php.net/manual/en/function.hash-hmac.php#101540


person Vipresh    schedule 04.04.2012    source источник


Ответы (1)


  1. Избегайте использования строк с криптографией. Используйте байтовые массивы, т.е. byte[]
  2. Если вы должны использовать строки, будьте очень осторожны в кодировке. Они укусят вас 9 раз из 10;

E.g.

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

что такое s значение?

Ответ: "А"

Если вы спросите byte[] из той же строки, вы получите: byte [1] { 65 }, что не совпадает с оригиналом и не будет работать для большинства криптографических применений.

Base64 безопаснее, поскольку сохраняет каждый байт нетронутым.

person poupou    schedule 04.04.2012
comment
Значение s не должно быть A A, и если я получу массив байтов из s, это будет {65,0,65} . код, который я пробовал static void Main(string[] args) { byte[] data = new byte[] { 65, 0, 65 }; var s = System.Text.Encoding.ASCII.GetString(данные); Console.WriteLine(S= + s); byte[] newData = System.Text.Encoding.ASCII.GetBytes(s); Console.WriteLine(newData.Count()); Console.WriteLine(Печать байтов); newData.ToList().ForEach(byt => { Console.WriteLine(byt); } ); } Вывод S=A A 3 Printing Bytes 65 0 65 - person Vipresh; 05.04.2012
comment
Я пробовал с Моно. В любом случае, главное заключается в том, что классы Encoding не гарантируют идентичные круговые пути, т.е. непечатаемые символы ASCII. Как таковые, они проблематичны для любого использования, такого как криптография, которая зависит от получения идентичных значений. - person poupou; 05.04.2012
comment
@poupou Вероятно, это была ошибка Mono. Моно 3.0.2 работает корректно. echo "Console.WriteLine(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(new byte [] { 65, 0, 65 })).Length);" | csharp дает 3 .Net не обманет \0. ASCII по-прежнему не лучшая идея, но не НАСТОЛЬКО плохая. Я думаю, вы должны удалить неверную информацию из своего ответа. - person Ark-kun; 19.11.2013
comment
Еще одна большая проблема с Encoding.ASCII заключается в том, что он молча заменяет каждый байт выше 127 знаком вопроса — это примерно половина из них. - person CodesInChaos; 18.09.2014