using System.Security.Cryptography; namespace Evergreen.Health.Application.Health.Security; internal static class HealthPasswordHasher { private const int Iterations = 120_000; private const int SaltSize = 16; private const int KeySize = 32; private const string Algorithm = "pbkdf2-sha256"; public static string Hash(string password) { var salt = RandomNumberGenerator.GetBytes(SaltSize); var key = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, KeySize); return "$" + Algorithm + "$" + Iterations + "$" + Convert.ToBase64String(salt) + "$" + Convert.ToBase64String(key); } public static bool Verify(string password, string encodedHash) { try { var parts = encodedHash.Split('$', StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 4 || parts[0] != Algorithm || !int.TryParse(parts[1], out var iterations)) return false; var salt = Convert.FromBase64String(parts[2]); var expected = Convert.FromBase64String(parts[3]); var actual = Rfc2898DeriveBytes.Pbkdf2(password, salt, iterations, HashAlgorithmName.SHA256, expected.Length); return CryptographicOperations.FixedTimeEquals(actual, expected); } catch (FormatException) { return false; } } }