DEV Community

David Mendez Guardado
David Mendez Guardado

Posted on

PBKDF2 Hash a secure password

Hashing a secure password to safe storage

In this paper i'll show you how to create a secure password to storage in your DB or whatever you want, an example of the secure password we create here was:

// this pass is "SecurePassword"
hAQSI9Feddid/DX3F71SPeL7doHAGeRKgefJ7XImpY4QyOcUh9Ypew==
Enter fullscreen mode Exit fullscreen mode

Let's start with the coding challenge

Explanation about PBKDF2

by Wikipedia - PBKDF2

PBKDF2

In cryptography, PBKDF1 and PBKDF2 (Password-Based Key Derivation Function 2) are key derivation functions with a sliding computational cost, used to reduce vulnerabilities to brute force attacks.

PBKDF2 is part of RSA Laboratories' Public-Key Cryptography Standards (PKCS) series, specifically PKCS #5 v2.0, also published as Internet Engineering Task Force's RFC 2898. It supersedes PBKDF1, which could only produce derived keys up to 160 bits long. RFC 8018 (PKCS #5 v2.1), published in 2017, recommends PBKDF2 for password hashing.

Purpose and operation

PBKDF2 applies a pseudorandom function, such as hash-based message authentication code (HMAC), to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key, which can then be used as a cryptographic key in subsequent operations. The added computational work makes password cracking much more difficult, and is known as key stretching.

When the standard was written in the year 2000 the recommended minimum number of iterations was 1, 000, but the parameter is intended to be increased over time as CPU speeds increase. A Kerberos standard in 2005 recommended 4, 096 iterations; Apple reportedly used 2, 000 for iOS 3, and 10, 000 for iOS 4; while LastPass in 2011 used 5000 iterations for JavaScript clients and 100, 000 iterations for server-side hashing.

Having a salt added to the password reduces the ability to use precomputed hashes (rainbow tables) for attacks, and means that multiple passwords have to be tested individually, not all at once. The standard recommends a salt length of at least 64 bits. The US National Institute of Standards and Technology recommends a salt length of 128 bits.

Alternatives to PBKDF2

One weakness of PBKDF2 is that while its number of iterations can be adjusted to make it take an arbitrarily large amount of computing time, it can be implemented with a small circuit and very little RAM, which makes brute-force attacks using application-specific integrated circuits or graphics processing units relatively cheap. The bcrypt password hashing function requires a larger amount of RAM (but still not tunable separately, i. e.fixed for a given amount of CPU time) and is slightly stronger against such attacks, while the more modern scrypt key derivation function can use arbitrarily large amounts of memory and is therefore more resistant to ASIC and GPU attacks.

Code for the secured password generation

For create a secure password we need to follow the next steps.

One of the important thing you need to know is the password we hashed is on One-Way-Hash so this means the result string can't do a reversible hashing, so you can't obtain the password from the hash string.

1.-create a salt

we need to create a salt this will be the unique byte array that will be used to create a hash, when you need to check if the password was correct you need this array of bytes to recreate the secure password.

// 1.-Create the salt value with a cryptographic PRNG
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[20]);
Enter fullscreen mode Exit fullscreen mode

2.-Create a hash

in this part we get a hash from password using the salt created in the last step.

// 2.-Create the RFC2898DeriveBytes and get the hash value
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
Enter fullscreen mode Exit fullscreen mode

3.-Combine salt and hash password

in this step we combine the salt and the hash password in an array of bytes.

// 3.-Combine the salt and password bytes for later use
byte[] hashBytes = new byte[40];
Array.Copy(salt, 0, hashBytes, 0, 20);
Array.Copy(hash, 0, hashBytes, 20, 20);
Enter fullscreen mode Exit fullscreen mode

4.-Get the string password

here is the finish of the creating a secured password, in this step we gets the string from the password generated.

// 4.-Turn the combined salt+hash into a string for storage
hashPass = Convert.ToBase64String(hashBytes);
Enter fullscreen mode Exit fullscreen mode

Code validation for the secured password

next to password generation we need to create a hashed password validation, because we use this code in a login or something like you brain imagine.

One important thing you need to know is

to verify if the password was correct you need to follow this steps:

  • get the hashed password from your storage, i.e. Database, JSON file, etc.
  • convert to array of bytes.
  • get the salt from the array.
  • hash the password entered from user with the salt you get from the array.
  • compare if the hash you get is the same as the hashed password.

1.-Extract the bytes from stored hash

first, we need to convert into bytes the hashed password.

// Extract the bytes
byte[] hashBytes = Convert.FromBase64String(hashPass);
Enter fullscreen mode Exit fullscreen mode

2.-Get the salt

second, we need to get the salt from the bytes array.

// Get the salt
byte[] salt = new byte[20];
Array.Copy(hashBytes, 0, salt, 0, 20);
Enter fullscreen mode Exit fullscreen mode

3.-Computhe the hash

next, we need to compute the hash with the password entered by user and the salt we got.

// Compute the hash on the password the user entered
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
Enter fullscreen mode Exit fullscreen mode

4.-Compare the user password with hash stored

to finish, we need to compare the user entered password we hashed and the stored hash.

// compare the results
for (int i = 0; i < 20; i++)
{
    if (hashBytes[i + 20] != hash[i])
    {
        throw new UnauthorizedAccessException();
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

As a conclusion for this paper, the use of the Password-Based Key Derivation Function 2 is a good way to secure our data, but like wikipedia says now is unsecure, because the malintended people can use a cheap hardware to decript our hashed password.

also i think if you add a one more level of security especifically for your business maybe becomes secure.

Summary of the PasswordCryptography class

This class was wrote in C#.

public class PasswordCryptographyPbkdf2
{
    /// <summary>
    /// Get the hash of the password
    /// </summary>
    /// <param name="password">string password</param>
    /// <returns>Hash secured password</returns>
    public string GetHashPassword(string password)
    {
        string hashPass = string.Empty;

        // 1.-Create the salt value with a cryptographic PRNG
        byte[] salt;
        new RNGCryptoServiceProvider().GetBytes(salt = new byte[20]);

        // 2.-Create the RFC2898DeriveBytes and get the hash value
        var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
        byte[] hash = pbkdf2.GetBytes(20);

        // 3.-Combine the salt and password bytes for later use
        byte[] hashBytes = new byte[40];
        Array.Copy(salt, 0, hashBytes, 0, 20);
        Array.Copy(hash, 0, hashBytes, 20, 20);

        // 4.-Turn the combined salt+hash into a string for storage
        hashPass = Convert.ToBase64String(hashBytes);

        return hashPass;
    }

    /// <summary>
    /// Check if the password is valid
    /// </summary>
    /// <param name="password">Entered by user</param>
    /// <param name="hashPass">Stored password</param>
    /// <returns>True if is Valid.</returns>
    public bool IsValidPassword(string password, string hashPass)
    {
        bool result = true;

        // Extract the bytes
        byte[] hashBytes = Convert.FromBase64String(hashPass);
        // Get the salt
        byte[] salt = new byte[20];
        Array.Copy(hashBytes, 0, salt, 0, 20);
        // Compute the hash on the password the user entered
        var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
        byte[] hash = pbkdf2.GetBytes(20);
        // compare the results
        for (int i = 0; i < 20; i++)
        {
            if (hashBytes[i + 20] != hash[i])
            {
                throw new UnauthorizedAccessException();
            }
        }

        return result;
    }
}
Enter fullscreen mode Exit fullscreen mode

Oldest comments (1)

Collapse
 
zeecnla profile image
Cesar Melchor

Saludos David!! Just wanted to say thank you for the detailed article in was nicely structured and you gave a lot of information!