Skip to content

Instantly share code, notes, and snippets.

@bricef
Last active October 3, 2016 00:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bricef/31f4440858556aa08cf02cac9acc5086 to your computer and use it in GitHub Desktop.
Save bricef/31f4440858556aa08cf02cac9acc5086 to your computer and use it in GitHub Desktop.
Salting and Hashing passwords correctly in .NET
/**
* From http://lockmedown.com/hash-right-implementing-pbkdf2-net/
*/
public class Hash
{
private const int SaltByteLength = 32;
private const int DerivedKeyLength = 32;
private const int IterationCount = 48000;
private const char separator = '|';
private static RNGCryptoServiceProvider CRNG = new RNGCryptoServiceProvider();
public static string Encode(string password)
{
var salt = GenerateSalt();
return EncodeWithSaltAndHash(password, salt, IterationCount);
}
public static Boolean Compare(string password, string encodedPassword)
{
var salt = SaltFromEncoded(encodedPassword);
var iterations = IterationsFromEncoded(encodedPassword);
var encoded = EncodeWithSaltAndHash(password, salt, iterations);
return ConstantTimeComparison(encoded, encodedPassword);
}
private static bool ConstantTimeComparison(string passwordGuess, string actualPassword)
{
uint difference = (uint)passwordGuess.Length ^ (uint)actualPassword.Length;
for (var i = 0; i < passwordGuess.Length && i < actualPassword.Length; i++)
{
difference |= (uint)(passwordGuess[i] ^ actualPassword[i]);
}
return difference == 0;
}
private static byte[] SaltFromEncoded(string encoded)
{
string[] parts = encoded.Split(separator);
return Convert.FromBase64String(parts[0]);
}
private static int IterationsFromEncoded(string encoded)
{
string[] parts = encoded.Split(separator);
return Convert.ToInt32(parts[2]);
}
private static string EncodeWithSaltAndHash(string password, byte[] salt, int iterations)
{
var hash = GenerateHash(password, salt, iterations);
var b64salt = Convert.ToBase64String(salt);
var b64hash = Convert.ToBase64String(hash);
return b64salt + separator + b64hash + separator + Convert.ToString(IterationCount);
}
private static byte[] GenerateSalt()
{
byte[] bytes = new byte[8];
CRNG.GetBytes(bytes);
return bytes;
}
private static byte[] GenerateHash(string password, byte[] salt, int iterationCount)
{
byte[] hashValue;
var valueToHash = string.IsNullOrEmpty(password) ? string.Empty : password;
using (var pbkdf2 = new Rfc2898DeriveBytes(valueToHash, salt, iterationCount))
{
hashValue = pbkdf2.GetBytes(DerivedKeyLength);
}
return hashValue;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment