Microsoft.AspNet.Cryptography.KeyDerivation vs System.Security.Cryptography - PullRequest
0 голосов
/ 03 декабря 2018

В последнее время я заметил чрезвычайно низкую производительность System.Security.Cryptography.

Чтобы создать хэш для пароля с 100000 итераций, потребовалось ~ 3 секунды.

Аналогичный код с использованием Microsoft.AspNet.Cryptography для той же конфигурации для хэша занял примерно в 10 раз меньше времени.

Код, который я использовал для тестирования, равен

using System;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNet.Cryptography.KeyDerivation;

namespace CryptoTest
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var watch = System.Diagnostics.Stopwatch.StartNew();
            Crypto crypto = new Crypto();
            Console.WriteLine($"Iterations: {test.HashIterations}, KeySize: {test.KeySize}, SaltSize: {test.DefaultSaltSize}");
            var salt = test.GenerateSalt();
            string password = "Very C0mplicated-AnD_L0ng.P@ssw0rd!";
            var saltedHashOld = crypto.GenerateSaltedHash(password, salt);
            watch.Stop();
            Console.WriteLine($"Time elapsed: {watch.ElapsedMilliseconds}ms");
            Console.WriteLine(saltedHashOld);

            watch.Reset();
            watch.Start();
            var prf = KeyDerivationPrf.HMACSHA1;
            var saltedHash = crypto.GenerateSaltedHash(password, salt, prf);
            watch.Stop();
            Console.WriteLine($"using {prf}");
            Console.WriteLine($"Time elapsed: {watch.ElapsedMilliseconds}ms");
            Console.WriteLine(saltedHash);


            watch.Reset();
            watch.Start();
            prf = KeyDerivationPrf.HMACSHA256;
            saltedHash = crypto.GenerateSaltedHash(password, salt, prf);
            watch.Stop();
            Console.WriteLine($"using {prf}");
            Console.WriteLine($"Time elapsed: {watch.ElapsedMilliseconds}ms");
            Console.WriteLine(saltedHash);

            watch.Reset();
            watch.Start();
            prf = KeyDerivationPrf.HMACSHA512;
            saltedHash = crypto.GenerateSaltedHash(password, salt, prf);
            watch.Stop();
            Console.WriteLine($"using {prf}");
            Console.WriteLine($"Time elapsed: {watch.ElapsedMilliseconds}ms");
            Console.WriteLine(saltedHash);
            Console.WriteLine("finished");
        }
    }
    public class Crypto
    {
        public int HashIterations => 100000;
        public int DefaultSaltSize => 96;
        public int KeySize => 96;
        public KeyDerivationPrf Prf => KeyDerivationPrf.HMACSHA1;

        public string GenerateSaltedHash(string password, string salt)
        {
            return ComputeHash(HashIterations, password, salt, KeySize);
        }

        public string GenerateSaltedHash(string password, string salt, KeyDerivationPrf prf)
        {
            return ComputeHash(HashIterations, password, salt, KeySize, prf);
        }

        public string GenerateSalt()
        {
            var rand = RandomNumberGenerator.Create();
            var ret = new byte[DefaultSaltSize];
            rand.GetBytes(ret);
            return Convert.ToBase64String(ret);
        }

        private string ComputeHash(int iterationsNo, string plainText, string salt, int keySize)
        {
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
            var pbkdf2 = new Rfc2898DeriveBytes(plainText, saltBytes, iterationsNo);
            var key = pbkdf2.GetBytes(keySize);
            return Convert.ToBase64String(key);
        }

        private string ComputeHash(int iterationsNo, string plainText, string salt, int keySize, KeyDerivationPrf prf)
        {
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
            var key = KeyDerivation.Pbkdf2(plainText, saltBytes, prf, iterationsNo, keySize);
            return Convert.ToBase64String(key);
        }
    }
}

Если сравнить снастройки по умолчанию для входа в систему Python Django, он выполняется на 300000 итераций и все еще выполняет хэширование пароля менее чем за секунду, при использовании System.Security.Cryptography для аутентификации пароля при входе в систему потребуется слишком много времени, если счетчик итераций увеличен дотекущие стандарты.

Какой из них следует использовать?

или я что-то здесь упускаю?

...