How to Hash, Salt and Store Password in C#?

Passwords are the most commonly used method for authenticating users and are therefore a primary target for attackers. One effective way to protect passwords is to hash and salt them. In this article, I will create a test Console Application that demonstrates the best practices for hashing, salting, and storing passwords in C#. This article is divided into theoretical and practical parts. If you want to move on to practice, then scroll down to the paragraph “Create Console Application that Hash, Salt and Store Passwords“.

What is Hashing?

Hashing is the process of converting a plain-text password into a unique fixed-length string of characters. This fixed-length string is referred to as a hash. The primary benefit of hashing is that it provides one-way encryption, meaning that it is nearly impossible to convert a hash back into the original plain-text password.

What is Salting?

While hashing is an effective way to secure passwords, it is vulnerable to a specific type of attack called a dictionary attack. A dictionary attack is when an attacker uses precomputed hashes of common passwords to try and guess a user’s password. This is where salting comes in. Salting is the process of adding a random value to the password before it is hashed. This random value is referred to as a salt.

Best Practices for Hashing and Salting Passwords

1. Use a Strong Hashing Algorithm

The strength of the hashing algorithm is crucial to the security of the hashed password. The most common hashing algorithms used for password storage are SHA-256 and SHA-512. These algorithms are recommended because they provide a high level of security.

2. Use a Unique Salt for Each Password

Using a unique salt for each password ensures that even if two users have the same password, their hashed passwords will be different. This makes it more difficult for an attacker to use precomputed hashes to guess passwords.

3. Generate the Salt Using a Cryptographically Secure Pseudo-Random Number Generator

It is important to use a cryptographically secure pseudo-random number generator to generate the salt. This ensures that the salt is truly random and not predictable.

4. Combine the Salt and Password before Hashing

To hash a password with a salt, the salt must be combined with the password before it is hashed. This can be done by concatenating the salt and password, or by using a more complex algorithm.

5. Use Key Stretching

Key stretching is the process of applying multiple rounds of hashing to a password. This increases the amount of time it takes for an attacker to guess the password and makes it more difficult for them to do so.

6. Store the Salt and Hashed Password Separately

To further enhance security, it is recommended to store the salt and hashed passwords separately. This makes it more difficult for an attacker to obtain both pieces of information. But in our demo project, I will deviate from this rule and keep it in one column.

Create Console Application that Hash, Salt and Store Passwords

Step 1 – Create .NET 7 Console Application

Let’s create a standard .NET 7 Console Application. Check the following screenshots:

create .net 7 console app
Choosing Console App

console app configuration
Get it name

.net 7 Framework
Select .net 7 framework

Step 2 – Prepare Database and DbContext

Create User Table

CREATE TABLE [dbo].[Users](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Email] [nvarchar](100) NOT NULL,
    [Password] [nvarchar](150) NOT NULL,
 CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

 

Prepare DbContext and map table to User class
prepare db to store hash password

You may be interested to read my article Console .Net 6.0 App with Entity Framework Core 6.0.6

Step 3 – Create MyPasswordHasher Class

MyPasswordHasher class
Code:

public class MyPasswordHasher
{
    private const int SaltSize = 16; //128 / 8, length in bytes
    private const int KeySize = 32; //256 / 8, length in bytes
    private const int Iterations = 10000;
    private static readonly HashAlgorithmName _hashAlgorithmName = HashAlgorithmName.SHA256;
    private const char SaltDelimeter = ';';
    public string Hash(string password)
    {
        var salt = RandomNumberGenerator.GetBytes(SaltSize);
        var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, _hashAlgorithmName, KeySize);
        return string.Join(SaltDelimeter, Convert.ToBase64String(salt), Convert.ToBase64String(hash));
    }
    public bool Validate(string passwordHash, string password)
    {
        var pwdElements = passwordHash.Split(SaltDelimeter);
        var salt = Convert.FromBase64String(pwdElements[0]);
        var hash = Convert.FromBase64String(pwdElements[1]);
        var hashInput = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, _hashAlgorithmName, KeySize);
        return CryptographicOperations.FixedTimeEquals(hash, hashInput);
    }
}

The MyPasswordHasher class provides methods for generating and validating a password hash using the Password-Based Key Derivation Function 2 (PBKDF2) algorithm. The Hash method generates a hash value for a given password and the Validate method validates a given password against a stored password hash.

MyPasswordHasher fields are:

  • SaltSize: A constant integer value that defines the size of the salt in bytes. In this code, it is set to 16 bytes, or 128 bits.
  • KeySize: A constant integer value that defines the size of the derived key in bytes. In this code, it is set to 32 bytes, or 256 bits.
  • Iterations: A constant integer value that defines the number of iterations of the PBKDF2 algorithm to use. In this code, it is set to 10000 iterations.
  • _hashAlgorithmName: A static readonly field that specifies the hash algorithm to use for the PBKDF2 algorithm. In this code, it is set to SHA256.
  • SaltDelimeter: A constant character value that is used to delimit the salt and hash values in the generated hash string. In this code, it is set to ‘;’.

The Hash method generates a new salt value using the RandomNumberGenerator class, which is a built-in .NET class for generating cryptographically secure random numbers. It then generates a hash value using the PBKDF2 algorithm and returns the salt and hash values concatenated together as a string using the SaltDelimeter character as a delimiter.

The Validate method takes in a stored password hash and a password to validate. It splits the stored password hash string into the salt and hash values and converts them from Base64-encoded strings to byte arrays. It then generates a new hash value for the input password using the same salt and PBKDF2 algorithm parameters as used for the stored hash. Finally, it compares the generated hash value with the stored hash value using the CryptographicOperations.FixedTimeEquals method to prevent timing attacks and returns a boolean indicating whether the password is valid or not.

This C# code provides a simple but secure way of hashing and validating passwords using the PBKDF2 algorithm with a strong salt and a fixed number of iterations.

Run Application to Hash and Store Password

Here I run the application in debug mode. You can see the test user with hashed and stored password:
Hash and store password

Run Application to Verify Password

Validate Hashed Password

Here you can see how Validate function works.

Conclusion

Hashing and salting are effective ways to protect user passwords. By using a strong hashing algorithm, a unique salt for each password, a cryptographically secure pseudo-random number generator, combining the salt and password before hashing, using key stretching, and storing the salt and hashed password separately(not in my example :) ), you can greatly increase the security of your user’s passwords.

Leave a Comment