How to hash passwords with a salt in .NET

In this post we learnt about using hashing in .NET. We also saw one of its basic functions in the same post which is message verification. In this post we saw how hashing coupled with a random key can be used for message authentication.

We also mentioned another common usage of hashing which is password storage. A password should never be stored as clear text in your system. Instead we save its hash value and when a user enters a password in a login field then we compare the hashed values instead of the plain string passwords. However, a simple one-way hash is generally still not good enough.

The two most common types of attack to guess passwords are the following:

  • Dictionary attack: the attacker goes through a list of words as possible passwords using a dictionary
  • Rainbow table attack: similar to a dictionary attack but in this case the dictionary contains the hashes of various passwords. For each different password a rainbow table can contain its SHA-1, SHA-256, SHA-516 etc. hash. If the attacker finds a matching hash then they have guessed the hashing algorithm and the original password

These databases are generally quite large and can reach several GBs. With software and processing power available today a hacker can test millions of passwords in minutes or even seconds. So it’s not like a determined malicious attacker will test the 10 most popular passwords like “password”, “passw0rd” or “holiday” and then give up. Therefore we need more than just simple hashes to increase security. This is where a password salt enters the picture.

Salt?

Imagine that you’re trying to cook some tasty soup. You add the basic ingredients to a pot of boiling water such as potatoes, noodles and carrots. If you don’t add anything else then your soup won’t have much taste, right? We need something extra, something that adds more taste such as spices: pepper, salt, paprika and the like. The soup without the spices can be viewed as the hashed password. Pretty much anyone can prepare it, i.e. it’s easy to make. However, if you start adding spices and you have some “secret” combination of them to make your soup extra tasty then it will be much harder for your guests to guess what you’ve put in it an in what quantity. The soup with spices is like a password hashed with a salt.

The concept is simple. We add a random set of bytes to the bytes that represent the password and hash that extended set of bytes instead. In effect our password won’t be “S3cret” but something like “S3cretuy8A7NQlIfNSgRbnGJrBOp6PhLs=” where “uy8A7NQlIfNSgRbnGJrBOp6PhLs=” is the base 64 string representation of the password salt. The user won’t need to know this salt, it is stored along with the hash in the database. Then when we check whether the password is correct then we need to consider the salt as well.

The extended password will be much harder to guess with the techniques mentioned above.

The blog post on hashing and this post about random number generation are enough to build a demo.

Let’s start with a container object to hold the salt and the message digest to be returned from a function:

public class HashWithSaltResult
{
	public string Salt { get; }
	public string Digest { get; set; }

	public HashWithSaltResult(string salt, string digest)
	{
		Salt = salt;
		Digest = digest;
	}
}

Here’s is our random number generator class – for details check out the link provided above:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace RandomNumberGenerator
{
    public class RNG
    {
		public string GenerateRandomCryptographicKey(int keyLength)
		{			
			return Convert.ToBase64String(GenerateRandomCryptographicBytes(keyLength));
		}

		public byte[] GenerateRandomCryptographicBytes(int keyLength)
		{
			RNGCryptoServiceProvider rngCryptoServiceProvider = new RNGCryptoServiceProvider();
			byte[] randomBytes = new byte[keyLength];
			rngCryptoServiceProvider.GetBytes(randomBytes);
			return randomBytes;
		}
	}
}

…and here comes the password hasher:

using RandomNumberGenerator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace HashingAlgos
{
	public class PasswordWithSaltHasher
	{
		public HashWithSaltResult HashWithSalt(string password, int saltLength, HashAlgorithm hashAlgo)
		{
			RNG rng = new RNG();
			byte[] saltBytes = rng.GenerateRandomCryptographicBytes(saltLength);
			byte[] passwordAsBytes = Encoding.UTF8.GetBytes(password);
			List<byte> passwordWithSaltBytes = new List<byte>();
			passwordWithSaltBytes.AddRange(passwordAsBytes);
			passwordWithSaltBytes.AddRange(saltBytes);
			byte[] digestBytes = hashAlgo.ComputeHash(passwordWithSaltBytes.ToArray());
			return new HashWithSaltResult(Convert.ToBase64String(saltBytes), Convert.ToBase64String(digestBytes));
		}
	}
}

The function accepts the password to be hashed, the number of bytes for the salt and a hashing algorithm. Within the method we first generate a salt, then combine the bytes of the password and the salt in one list of bytes. We finally let the hashing algorithm hash the complete list of bytes and return the base 64 representation of both the salt and the hashed password. In fact it will be the hash of the password and the salt together.

Here’s a demo function with a 64-bit salt and 2 different hashing algorithms, SHA-256 and SHA-512:

private static void TestPasswordHasher()
{
	PasswordWithSaltHasher pwHasher = new PasswordWithSaltHasher();
	HashWithSaltResult hashResultSha256 = pwHasher.HashWithSalt("ultra_safe_P455w0rD", 64, SHA256.Create());
	HashWithSaltResult hashResultSha512 = pwHasher.HashWithSalt("ultra_safe_P455w0rD", 64, SHA512.Create());

	Console.WriteLine(hashResultSha256.Salt);
	Console.WriteLine(hashResultSha256.Digest);
	Console.WriteLine();
	Console.WriteLine(hashResultSha512.Salt);
	Console.WriteLine(hashResultSha512.Digest);
}

Here is the output:

p5v4WmAcJ7JFdJczsX09ZqVR3VbenKedO1O/DIrDpHf8z21c4wGZZrw7UY9T8ZszbP6eij5MvkQxrAilcPqx0Q==
bvDURvxpbWEqPSvGm/FjZKJ3LunPsMg/qQ1jI9rDDxQ=

hIXqxbo5eYiKG2Vygok5NxosQATlX2JSohSSOXOjNpdIEZKJ8PNRKVegeVjqSjhPvo1544itXnX9JHlwJAQg5A==
/jsfgtSMpp4zAF8zoQcR6fSoE8SHIrnJguQSUGzdNolC2hsbyloQWyPgalkB10Sqi2dtaVMaxez3Ev8B81rQjA==

You can view the list of posts on Security and Cryptography here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

2 Responses to How to hash passwords with a salt in .NET

  1. Avinash Singh says:

    How can we verify this SHA512 with salted password?

  2. Avinash Kumar says:

    How to validate password with Salt and digest hash results

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.