Skip to content

Instantly share code, notes, and snippets.

@benlesh
Created August 22, 2012 19:08
Show Gist options
  • Save benlesh/3428461 to your computer and use it in GitHub Desktop.
Save benlesh/3428461 to your computer and use it in GitHub Desktop.
A password hashing utility for .NET and a functional test console application for it.
/// <summary>
/// A hashing utility.
/// </summary>
public static class Hasher
{
/// <summary>
/// Gets an HMAC SHA512 hash for some text.
/// </summary>
/// <param name="text">The text to hash</param>
/// <param name="salt">The salt to use in hashing.</param>
/// <param name="key">The key to use to hash the text.</param>
/// <returns>The hash.</returns>
public static byte[] GetHash(string text, byte[] salt, byte[] key)
{
using (var sha512 = new HMACSHA512(key))
{
var buffer = GetBytesToHash(text, salt);
return sha512.ComputeHash(buffer);
}
}
/// <summary>
/// Does the work of combining the text and the salt into
/// a single byte array.
/// </summary>
/// <param name="text">The text to be hashed at some point.</param>
/// <param name="salt">The bytes to salt it with.</param>
/// <returns>The combined byte array.</returns>
private static byte[] GetBytesToHash(string text, byte[] salt)
{
var buffer = new byte[text.Length + salt.Length];
var passwordBytes = Encoding.UTF8.GetBytes(text);
passwordBytes.CopyTo(buffer, 0);
Array.ConstrainedCopy(salt, 0, buffer, passwordBytes.Length, salt.Length);
return buffer;
}
/// <summary>
/// Generates a key from text input.
/// </summary>
/// <param name="text">The text to generate the key from.</param>
/// <param name="salt">The salt to use to generate a key.</param>
/// <param name="iterations">The number of iterations to use to stretch the key.</param>
/// <param name="length">The number of bytes in length the key should be.</param>
/// <returns>A key byte array.</returns>
public static byte[] GetKey(string text, byte[] salt, int iterations, int length)
{
using (var pbkdf2 = new Rfc2898DeriveBytes(text, salt, iterations))
{
return pbkdf2.GetBytes(length);
}
}
/// <summary>
/// Gets a random salt of a specified length.
/// </summary>
/// <param name="length">The length of the salt to get.</param>
/// <returns>A random array of bytes.</returns>
public static byte[] GetSalt(int length)
{
using (var rng = new RNGCryptoServiceProvider())
{
var salt = new byte[length];
rng.GetBytes(salt);
return salt;
}
}
}
class Program
{
static void Main(string[] args)
{
byte[] salt = null;
var sw = new Stopwatch();
while (true)
{
// get some text to hash.
Console.WriteLine("Enter some text to hash:");
var text = Console.ReadLine();
// start timing the process (for reference purposes)
sw.Restart();
if (salt == null)
{
// get a 512 byte salt.
salt = Hasher.GetSalt(512);
}
// get a 512 byte key based off of the password and the salt
// stretch by iterating 2000 times to slow down the hashing process.
var key = Hasher.GetKey(text, salt, 2000, 512);
// finally, generate the hash.
var hash = Hasher.GetHash(text, salt, key);
// let's check how long that took.
var ms = sw.ElapsedMilliseconds;
// display the information.
Console.WriteLine("Salt: {0}", Convert.ToBase64String(salt));
Console.WriteLine("Hash: {0}", Convert.ToBase64String(hash));
Console.WriteLine("Hash Length: {0}", hash.Length);
Console.WriteLine("Time: {0}", ms);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment