ساده سازی روشهای هش کردن اطلاعات در داتنت 9
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۲/۱۹ ۰۷:۲۳
آدرس: www.dntips.ir
var originalData = Encoding.Default.GetBytes("This is some data");
var hashedData = SHA256.HashData(originalData);var originalData = Encoding.Default.GetBytes("This is some data");
var hashedData = SHA512.HashData(originalData);csharpvar originalData = Encoding.Default.GetBytes("This is some data");
var token = CancellationToken.None;
var sha256HashedData = await CryptographicOperations.HashDataAsync(HashAlgorithmName.SHA256, originalData, token);
var sha512HashedData = await CryptographicOperations.HashDataAsync(HashAlgorithmName.SHA512, originalData, token);ReadOnlySpan<byte> را بهبود بخشیده است. این قابلیت برای سناریوهایی مانند هش کردن فایلهای بزرگ یا دادههای جریانی بسیار کاربردی است، زیرا امکان پردازش دادهها بدون نیاز به بارگذاری کامل آنها در حافظه را فراهم میکند. برای مثال، هش کردن یک فایل بزرگ با استفاده از Stream به صورت زیر انجام میشود:using var stream = File.OpenRead("largefile.txt");
var hashedData = await CryptographicOperations.HashDataAsync(HashAlgorithmName.SHA256, stream, CancellationToken.None);dotnet add package Konscious.Security.Cryptography.Argon2
public interface IPasswordHasher
{
string Hash(string plainPassword);
bool Verify(string plainPassword, string passwordHash);
}using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using Konscious.Security.Cryptography;
namespace ProjectName.Infrastructure.Authentication;
public sealed class PasswordHasher : IPasswordHasher
{
/// <summary>
/// Hash with Argon2Id
/// </summary>
/// <param name="plainPassword">Plain password</param>
/// <returns>Hashed password</returns>
public string Hash(string plainPassword)
{
ArgumentNullException.ThrowIfNull(plainPassword);
const int saltSize = 16; // 128 bits
const int hashSize = 32; // 256 bits
var parallelism = GetRecommendedParallelism(); // Number of threads to use
const int iterations = 4; // Number of iterations
const int memorySize = 1024 * 256;
var salt = new byte[saltSize];
RandomNumberGenerator.Fill(salt);
var hash = ComputeHash(plainPassword, salt, iterations, memorySize, parallelism, hashSize);
return $"$argon2id$v=19$m={memorySize},t={iterations},p={parallelism.ToString(CultureInfo.InvariantCulture)}" +
$"${Convert.ToBase64String(salt)}${Convert.ToBase64String(hash)}";
}
public bool Verify(string plainPassword, string passwordHash)
{
ArgumentNullException.ThrowIfNull(plainPassword);
var parts = passwordHash.Split('$', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 5 || !parts[0].Equals("argon2id", StringComparison.Ordinal))
{
return false;
}
var paramStr = parts[2];
var paramDict = paramStr.Split(',')
.Select(p => p.Split('='))
.ToDictionary(p => p[0], p => int.Parse(p[1], CultureInfo.InvariantCulture), StringComparer.Ordinal);
var memory = paramDict["m"];
var iterations = paramDict["t"];
var parallelism = paramDict["p"];
var salt = Convert.FromBase64String(parts[3]);
var expectedHash = Convert.FromBase64String(parts[4]);
using var argon2 = new Argon2id(Encoding.UTF8.GetBytes(plainPassword))
{
Salt = salt,
MemorySize = memory,
Iterations = iterations,
DegreeOfParallelism = parallelism
};
var hash = ComputeHash(plainPassword, salt, iterations, memory, parallelism, expectedHash.Length);
return CryptographicOperations.FixedTimeEquals(expectedHash, hash);
}
private static byte[] ComputeHash(string plainPassword, byte[] salt, int iterations, int memorySize, int parallelism, int hashSize)
{
using var argon2 = new Argon2id(Encoding.UTF8.GetBytes(plainPassword))
{
Salt = salt,
Iterations = iterations,
MemorySize = memorySize,
DegreeOfParallelism = parallelism
};
return argon2.GetBytes(hashSize);
}
private static int GetRecommendedParallelism()
{
return Environment.ProcessorCount switch
{
<= 2 => 1,
<= 4 => 2,
<= 8 => 3,
_ => Math.Min(4, Environment.ProcessorCount / 2)
};
}
}$argon2id$v=19$m=262144,t=4,p=4$Salt$Hash
Environment.ProcessorCount انتخاب شده است.MemorySize = 262144 استفاده میکنید و یک سال بعد تصمیم میگیرید آن را به 524288 افزایش دهید. اگر پارامترها کنار هش ذخیره نشده باشند، دیگر نمیتوان فهمید رمزهای قبلی با چه تنظیماتی تولید شدهاند و Verify آنها با مشکل مواجه خواهد شد.CryptographicOperations.FixedTimeEquals استفاده شده است.