В последнее время я заметил чрезвычайно низкую производительность 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 для аутентификации пароля при входе в систему потребуется слишком много времени, если счетчик итераций увеличен дотекущие стандарты.
Какой из них следует использовать?
или я что-то здесь упускаю?