Salted Hash

Recently, I had a problem beyond the usual problems of having to go downstairs to get coffee and deciding what to eat for dinner.

My problem was with encrypting passwords. I had never done that before. It just aways turned out that when that step came, someone on my team had always created some sort of architecture to support it. It was abstracted away so I never really knew about how it worked. This time I am a team of one and the problem needs to be addressed. Doing a quick Topeka on the subject I found that there are a ton of ways to encrypt passwords in a SQL Server database. Using a combination of different strategies I was able to come up with my solution.

The solution is pretty simple to understand (even for an encryption novice like myself).

My database is just a simple column called "Password" with datatype "nvarchar(50)". For my solution, the encryption is going to happen on the application side.

To start things off, i knew I eventually had to call some System.Security.Cryptography class. I chose MD5 just for the sake of choosing MD5. Once I have an instance, all I need to do is give the MD5 instance a byte array to hash.

MD5 md5 = MD5.Create();
Byte[] computedHash = md5.ComputeHash(passwordBytes);

Since I am storing the password as a string, the hash that I calculated needs to eventually be encoded to save in the database. Again, you can choose whatever encoder you want, but I chose to use a UTF8Encoding.

UTF8Encoding encoder = new UTF8Encoding();
Byte[] passwordBytes = encoder.GetBytes(password);

and eventually return the encoded string

return encoder.GetString(computedHash);

Doing those few steps works for encoding passwords in a database. I wasn't really satisfied with this implementation though. The problem is that if I have two users that have the same password, they will have the same hash. If someone ever gets access to your Users database and can see your hash you could run the risk of unintentionally exposing user passwords.

I ultimately want a different hash result for each user. This is where a concept called salting comes in.

If I can also encrypt a unique identifier with my password, the encrypted password will be different for every user in my database and will deter security attacks looking to decrypt my hash. For my Users table, my username is always unique so I will be adding the username bytes as my salt to my delicious hash.

// Create a new salt
Byte[] saltBytes = encoder.GetBytes(this.Username);

You can add as many additional salt bytes that you like, as long as you know how to get back from it. Once I have my salt bytes I can simply added the two byte arrays together and hash the combined byte array. Here is the full method.

public string CreatePasswordHash(string value)
{ 
    // Create Byte array of password string
    UTF8Encoding encoder = new UTF8Encoding();
    Byte[] passwordBytes = encoder.GetBytes(value);

    // Create a new salt
    Byte[] saltBytes = encoder.GetBytes(this.Username);

    // append the two arrays
    Byte[] toHash = new Byte[passwordBytes.Length + saltBytes.Length];
    Array.Copy(passwordBytes, 0, toHash, 0, passwordBytes.Length);
    Array.Copy(saltBytes, 0, toHash, passwordBytes.Length, saltBytes.Length);

    MD5 md5 = MD5.Create();
    Byte[] computedHash = md5.ComputeHash(toHash);

    return encoder.GetString(computedHash);
}

Now that I have this super awesome encrypted string I thought the next step was to be able to decrypt this string into a readable password. I was wrong. Think about it. When do you ever show an unencrypted password. You don't. You only ever validate if the password supplied is equal to the password in the database. This means you only ever have to encrypt a password and never decrypt it. This actually makes me feel good that I never even store how to get to a real password in my code. Here is how the validation might look like.

if (user.Password == CreatePasswordHash(password))
{
    // Do something for this user
}