using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
public static byte[] RandomSalt()
return RandomNumberGenerator.GetBytes(16);
public static byte[] ComputeHash(string password, byte[] salt, int iterations)
return KeyDerivation.Pbkdf2(password, salt, KeyDerivationPrf.HMACSHA512, iterations, 36);
public static bool SlowEquals(byte[] a, byte[] b)
uint diff = (uint)a.Length ^ (uint)b.Length;
for (int i = 0; i < a.Length && i < b.Length; i++)
diff |= (uint)(a[i] ^ b[i]);
public static bool ComparePassword(string password, byte[] salt, int iterations, byte[] savedHash)
var computedHash = ComputeHash(password, salt, iterations);
if (SlowEquals(savedHash, computedHash))
computedHash = ComputeHash(password.Substring(1), salt, iterations);
if (SlowEquals(savedHash, computedHash))
computedHash = ComputeHash(password.Substring(0, password.Length - 1), salt, iterations);
if (SlowEquals(savedHash, computedHash))
if (char.IsUpper(password[0]))
computedHash = ComputeHash(char.ToLower(password[0]) + password.Substring(1), salt, iterations);
if (SlowEquals(savedHash, computedHash))
var invertCasePassword = new string(password.Select(c => char.IsLetter(c) ? (char.IsUpper(c) ? char.ToLower(c) : char.ToUpper(c)) : c).ToArray());
computedHash = ComputeHash(invertCasePassword, salt, iterations);
if (SlowEquals(savedHash, computedHash))
public static void Main()
string originalPassword = "P@ssw0rd1";
byte[] salt = RandomSalt();
byte[] computedHash = ComputeHash(originalPassword, salt, iterations);
string testPassword1 = "1dr0wss@P";
string testPassword2 = "P@ssw0rd11";
string testPassword3 = "PP@ssw0rd1";
string testPassword4 = "p@SSW0RD1";
string testPassword5 = "P@ssw0rd5";
string testPassword6 = "P@ssw0rd123";
Console.WriteLine("Checking password {0}, is it valid? {1}", originalPassword, ComparePassword(originalPassword, salt, iterations, computedHash));
Console.WriteLine("Checking password {0}, is it valid? {1}", testPassword1, ComparePassword(testPassword1, salt, iterations, computedHash));
Console.WriteLine("Checking password {0}, is it valid? {1}", testPassword2, ComparePassword(testPassword2, salt, iterations, computedHash));
Console.WriteLine("Checking password {0}, is it valid? {1}", testPassword3, ComparePassword(testPassword3, salt, iterations, computedHash));
Console.WriteLine("Checking password {0}, is it valid? {1}", testPassword4, ComparePassword(testPassword4, salt, iterations, computedHash));
Console.WriteLine("Checking password {0}, is it valid? {1}", testPassword5, ComparePassword(testPassword5, salt, iterations, computedHash));
Console.WriteLine("Checking password {0}, is it valid? {1}", testPassword6, ComparePassword(testPassword6, salt, iterations, computedHash));