How to Hash Passwords

Hashing passwords is a way of taking a variable-length password and creating a cryptic, fixed-length password from it. You do this by generating and using a salt value. A salt value is a random value that you use to generate the hashed password.

This topic shows how to generate salt values and hash passwords. These functions are referenced in the topic How to Validate Passwords.

To hash a password

  1. Create a new function called GenerateSaltValue that returns a salt value.

  2. Type code that generates random values based on UTF-16 encoding and the system time.

  3. Create a new function called HashPassword that hashes a password by using the salt value.

  4. Type code that hashes the password, puts it into an array of bytes, and returns the salt value plus the hashed password.

Example

The following code contains two functions. One is for generating salt values and one is for hashing passwords.

private static string GenerateSaltValue()
{
    UnicodeEncoding utf16 = new UnicodeEncoding();

    if (utf16 != null)
    {
        // Create a random number object seeded from the value
        // of the last random seed value. This is done
        // interlocked because it is a static value and we want
        // it to roll forward safely.

        Random random = new Random(unchecked((int)DateTime.Now.Ticks));

        if (random != null)
        {
            // Create an array of random values.

            byte[] saltValue = new byte[SaltValueSize];

            random.NextBytes(saltValue);

            // Convert the salt value to a string. Note that the resulting string
            // will still be an array of binary values and not a printable string. 
            // Also it does not convert each byte to a double byte.

            string saltValueString = utf16.GetString(saltValue);

            // Return the salt value as a string.

            return saltValueString;
        }
    }

    return null;
}

private static string HashPassword(string clearData, string saltValue, HashAlgorithm hash)
{
    UnicodeEncoding encoding = new UnicodeEncoding();

    if (clearData != null && hash != null && encoding != null)
    {
        // If the salt string is null or the length is invalid then
        // create a new valid salt value.

        if (saltValue == null)
        {
            // Generate a salt string.
            saltValue = GenerateSaltValue();
        }

        // Convert the salt string and the password string to a single
        // array of bytes. Note that the password string is Unicode and
        // therefore may or may not have a zero in every other byte.

        byte[] binarySaltValue = new byte[SaltValueSize];

        binarySaltValue[0] = byte.Parse(saltValue.Substring(0, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
        binarySaltValue[1] = byte.Parse(saltValue.Substring(2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
        binarySaltValue[2] = byte.Parse(saltValue.Substring(4, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
        binarySaltValue[3] = byte.Parse(saltValue.Substring(6, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);

        byte[] valueToHash = new byte[SaltValueSize + encoding.GetByteCount(clearData)];
        byte[] binaryPassword = encoding.GetBytes(clearData);

        // Copy the salt value and the password to the hash buffer.

        binarySaltValue.CopyTo(valueToHash, 0);
        binaryPassword.CopyTo(valueToHash, SaltValueSize);

        byte[] hashValue = hash.ComputeHash(valueToHash);

        // The hashed password is the salt plus the hash value (as a string).

        string hashedPassword = saltValue;

        foreach (byte hexdigit in hashValue)
        {
            hashedPassword += hexdigit.ToString("X2", CultureInfo.InvariantCulture.NumberFormat);
        }

        // Return the hashed password as a string.

        return hashedPassword;
    }

    return null;
}

Compiling the Code

  • To compile the code, you need to include these namespace directives:
using System.Text;
using System.Security.Cryptography;
using System.Globalization;

See Also

Other Resources

Profiles Encryption and Decryption

How Does the Profiles System Encrypt Data?

How to Use Asymmetric Encryption with Profiles

How to Validate Passwords

Profile Key Manager