Provided methods allow to get a rainbow tables-proof password hash and to check a password against that hash.
Usage example:
static void Main(string[] args)
{
var pwd = "11122334455";
Console.WriteLine("Password: " + pwd);
var hash = GetPasswordHash(pwd, null);
Console.WriteLine(hash);
Console.WriteLine(CheckPassword(hash, "11122334455"));
Console.WriteLine(CheckPassword(hash, "1112233445"));
Console.ReadKey();
}
Methods themselves:
private static string GetPasswordHash(string password, byte[] salt)
{
Rfc2898DeriveBytes generator = new Rfc2898DeriveBytes(password, 32);
generator.IterationCount = 16384;
if (salt != null)
{
generator.Salt = salt;
}
byte[] hashBytes = generator.GetBytes(32);
byte[] saltBytes = generator.Salt;
return Convert.ToBase64String(saltBytes) + "|" + Convert.ToBase64String(hashBytes);
}
private static bool CheckPassword(string hash, string password)
{
// Get salt value from the provided password hash
string[] hashParts = hash.Split(new string[] { "|" }, StringSplitOptions.None);
byte[] hashBytes = Convert.FromBase64String(hashParts[0]);
// Get hash of the provided password with the salt previuously used to hash the correct password
string passwordHash = GetPasswordHash(password, hashBytes);
// Compare (in cryptographically safe way) the storead and calculated password hashes
return Equals(System.Text.Encoding.ASCII.GetBytes(hash), System.Text.Encoding.ASCII.GetBytes(passwordHash));
}
private static bool Equals(byte[] value1, byte[] value2)
{
uint diff = (uint)value1.Length ^ (uint)value2.Length;
for (int i = 0; i < value1.Length && i < value2.Length; i++)
{
diff |= (uint)(value1[i] ^ value2[i]);
}
return diff == 0;
}