public class PBKDF2Hash { public const int SaltByteSize = 24; public const int HashByteSize = 20; // to match the size of the PBKDF2-HMAC-SHA-1 hash public const int PBKDF2Iterations = 1000; public const int IterationIndex = 0; public const int SaltIndex = 1; public const int PBKDF2Index = 2; public static string HashPassword(string password) { var cryptoProvider = new System.Security.Cryptography.RNGCryptoServiceProvider(); byte[] salt = new byte[SaltByteSize]; cryptoProvider.GetBytes(salt); var hash = GetPBKDF2Bytes(password, salt, PBKDF2Iterations, HashByteSize); return $"{PBKDF2Iterations}:{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}"; } public static bool ValidatePassword(string password, string correctHash) { char[] delimiter = { ':' }; var split = correctHash.Split(delimiter); var iterations = Int32.Parse(split[IterationIndex]); var salt = Convert.FromBase64String(split[SaltIndex]); var hash = Convert.FromBase64String(split[PBKDF2Index]); var testHash = GetPBKDF2Bytes(password, salt, iterations, hash.Length); return SlowEquals(hash, testHash); } private static bool SlowEquals(byte[] a, byte[] b) { var diff = (uint)a.Length ^ (uint)b.Length; for (int i = 0; i < a.Length && i < b.Length; i++) diff |= (uint)(a[i] ^ b[i]); return diff == 0; } private static byte[] GetPBKDF2Bytes(string password, byte[] salt, int iterations, int outputBytes) { var PBKDF2 = new System.Security.Cryptography.Rfc2898DeriveBytes(password, salt); PBKDF2.IterationCount = iterations; return PBKDF2.GetBytes(outputBytes); } }