Skip to main content

Password Hasher | Template de código | C#

  • Password Hasher | Template de código | C#
public sealed class PasswordHasher : IPasswordHasher
{
private const int SaltSize = 16;
private const int HashSize = 32;
private const int Iterations = 100000;

private static readonly HashAlgorithmName Algorithm = HashAlgorithmName.SHA512;

public string Hash(string password)
{
byte[] salt = RandomNumberGenerator.GetBytes(SaltSize);
byte[] hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, Algorithm, HashSize);

return $"{Convert.ToHexString(hash)}-{Convert.ToHexString(salt)}";
}

public bool Verify(string password, string passwordHash)
{
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password cannot be null or empty.", nameof(password));

if (string.IsNullOrWhiteSpace(passwordHash))
throw new ArgumentException("Stored password hash cannot be null or empty.", nameof(passwordHash));

string[] parts = passwordHash.Split('-');
if (parts.Length != 2)
throw new FormatException("Invalid password hash format.");

byte[] hash;
byte[] salt;

try
{
hash = Convert.FromHexString(parts[0]);
salt = Convert.FromHexString(parts[1]);
}
catch (FormatException)
{
throw new FormatException("Password hash or salt is not a valid hexadecimal string.");
}

byte[] inputHash = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
Iterations,
Algorithm,
HashSize
);

// Compare using constant-time equality check to prevent timing attacks
bool matches = CryptographicOperations.FixedTimeEquals(hash, inputHash);

// Optionally: log or handle failed attempts (without leaking info)
return matches;
}

}