I assume that the best way to handle passwords for a website is I create a hash of the password and save that hash in my database. Then when someone tries to login, I do a hash of the password they entered and compare.
Is this correct?
And, is there sample code anywhere showing how to do this in C#?
And should I add some standard text to each password so the text being hashed is always at least 20 chars long?
Update: The other links don't have code so here's what I implemented for this question. I don't include the username because that can change, so just salt:password. the : works because uuencode does not include a :.
public static class EncryptionUtilities
{
private const int SALT_SIZE = 8;
private const int NUM_ITERATIONS = 1000;
private static readonly RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
/// <summary>
/// Creates a signature for a password.
/// </summary>
/// <param name="password">The password to hash.</param>
/// <returns>the "salt:hash" for the password.</returns>
public static string CreatePasswordSalt(string password)
{
byte[] buf = new byte[SALT_SIZE];
rng.GetBytes(buf);
string salt = Convert.ToBase64String(buf);
Rfc2898DeriveBytes deriver2898 = new Rfc2898DeriveBytes(password.Trim(), buf, NUM_ITERATIONS);
string hash = Convert.ToBase64String(deriver2898.GetBytes(16));
return salt + ':' + hash;
}
/// <summary>
/// Validate if a password will generate the passed in salt:hash.
/// </summary>
/// <param name="password">The password to validate.</param>
/// <param name="saltHash">The "salt:hash" this password should generate.</param>
/// <returns>true if we have a match.</returns>
public static bool IsPasswordValid(string password, string saltHash)
{
string[] parts = saltHash.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
return false;
byte[] buf = Convert.FromBase64String(parts[0]);
Rfc2898DeriveBytes deriver2898 = new Rfc2898DeriveBytes(password.Trim(), buf, NUM_ITERATIONS);
string computedHash = Convert.ToBase64String(deriver2898.GetBytes(16));
return parts[1].Equals(computedHash);
}
}
ps - I considered adding "The World Wonders" to the text hashed because it's the most famous salt of all time. However, I figured it didn't add anything and encryption is not the place to bring in historical strings.