using System.Security.Cryptography; namespace LingAdmin.API.Services; public class PasswordHasher : IPasswordHasher { private const int SaltSize = 16; // 128 bit private const int KeySize = 32; // 256 bit private const int Iterations = 10000; private static readonly HashAlgorithmName Algorithm = HashAlgorithmName.SHA256; public string HashPassword(string password) { var salt = RandomNumberGenerator.GetBytes(SaltSize); var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, Algorithm, KeySize); // Combine salt and hash var result = new byte[SaltSize + KeySize]; Buffer.BlockCopy(salt, 0, result, 0, SaltSize); Buffer.BlockCopy(hash, 0, result, SaltSize, KeySize); return Convert.ToBase64String(result); } public bool VerifyPassword(string password, string passwordHash) { var hashBytes = Convert.FromBase64String(passwordHash); // Extract salt var salt = new byte[SaltSize]; Buffer.BlockCopy(hashBytes, 0, salt, 0, SaltSize); // Extract hash var hash = new byte[KeySize]; Buffer.BlockCopy(hashBytes, SaltSize, hash, 0, KeySize); // Compute hash with the extracted salt var computedHash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, Algorithm, KeySize); // Compare hashes return CryptographicOperations.FixedTimeEquals(hash, computedHash); } }