Init Git
This commit is contained in:
61
AMREZ.EOP.Infrastructures/Security/BcryptPasswordHasher.cs
Normal file
61
AMREZ.EOP.Infrastructures/Security/BcryptPasswordHasher.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using AMREZ.EOP.Abstractions.Security;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Security;
|
||||
|
||||
public sealed class BcryptPasswordHasher : IPasswordHasher
|
||||
{
|
||||
private const int SaltSizeBytes = 16; // 128-bit salt
|
||||
private const int KeySizeBytes = 32; // 256-bit key
|
||||
private const int Iterations = 200_000; // 2e5 (แนะนำ >= 150k บน .NET 8/9)
|
||||
|
||||
public bool Verify(string plain, string hash)
|
||||
{
|
||||
if (plain is null || hash is null) return false;
|
||||
|
||||
var parts = hash.Split('$', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 4 || !parts[0].Equals("pbkdf2-sha256", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[1], out var iters) || iters <= 0) return false;
|
||||
|
||||
byte[] salt, expected;
|
||||
try
|
||||
{
|
||||
salt = Convert.FromBase64String(parts[2]);
|
||||
expected = Convert.FromBase64String(parts[3]);
|
||||
}
|
||||
catch { return false; }
|
||||
|
||||
byte[] actual = Rfc2898DeriveBytes.Pbkdf2(
|
||||
password: Encoding.UTF8.GetBytes(plain),
|
||||
salt: salt,
|
||||
iterations: iters,
|
||||
hashAlgorithm: HashAlgorithmName.SHA256,
|
||||
outputLength: expected.Length
|
||||
);
|
||||
|
||||
return CryptographicOperations.FixedTimeEquals(actual, expected);
|
||||
}
|
||||
|
||||
public string Hash(string plain)
|
||||
{
|
||||
if (plain is null) throw new ArgumentNullException(nameof(plain));
|
||||
|
||||
Span<byte> salt = stackalloc byte[SaltSizeBytes];
|
||||
RandomNumberGenerator.Fill(salt);
|
||||
|
||||
byte[] key = Rfc2898DeriveBytes.Pbkdf2(
|
||||
password: Encoding.UTF8.GetBytes(plain),
|
||||
salt: salt.ToArray(),
|
||||
iterations: Iterations,
|
||||
hashAlgorithm: HashAlgorithmName.SHA256,
|
||||
outputLength: KeySizeBytes
|
||||
);
|
||||
|
||||
var saltB64 = Convert.ToBase64String(salt);
|
||||
var keyB64 = Convert.ToBase64String(key);
|
||||
return $"pbkdf2-sha256${Iterations}${saltB64}${keyB64}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user