using System.Security.Cryptography; namespace MikrocopApi.Services; public sealed class PasswordHashingService : IPasswordHashingService { private const int SaltSizeBytes = 16; private const int HashSizeBytes = 32; private const int Iterations = 100_000; public (string Hash, string Salt) HashPassword(string password) { var salt = RandomNumberGenerator.GetBytes(SaltSizeBytes); var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, HashSizeBytes); return (Convert.ToBase64String(hash), Convert.ToBase64String(salt)); } public bool VerifyPassword(string password, string hashBase64, string saltBase64) { byte[] expectedHash; byte[] salt; try { expectedHash = Convert.FromBase64String(hashBase64); salt = Convert.FromBase64String(saltBase64); } catch (FormatException) { return false; } var actualHash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, expectedHash.Length); return CryptographicOperations.FixedTimeEquals(actualHash, expectedHash); } }