The Proper Way To Hash A Password, Or Derive a Key From a Password: Meet PBKDF2

All those charts that show you how long it will take to crack a hashed version of a password are defined wrong. Most will take the cracking…

Photo by regularguy.eth on Unsplash

The Proper Way To Hash A Password, Or Derive a Key From a Password: Meet PBKDF2

All those charts that show you how long it will take to crack a hashed version of a password are defined wrong. Most will take the cracking speed of a fast hashing method and use that. With a proper KDF (Key Derivation Function), we normally slow down the whole process, and can even get to a point that we can only hash just a few passwords per second, and not billions. Thus, if we select our hashing method properly, we can make it extremely difficult for someone to discover the original password. This is the same for the generation of encryption keys from passwords.

Introduction

When we say that we store a hashed version of a password, we often do not mean that we use the standard hashing methods. This includes MD5, SHA-1 and SHA-256. The reason for this, is that these hashing methods are fast, and where an adversary could try billions of passwords every second and try the hashed version of these. This includes using the salt value used.

The method we normally use is to use a key derivation function (KDF). For this, one of the most widely used methods is PBKDF2 (Password-Based Key Derivation Function 2). It is defined in RFC 2898 and generates a salted hash. We also add a number of iterations, and which slows down the hashing process, while also disabling the usage of GPUs.

Often this is used to create an encryption key from a defined password, and where it is not possible to reverse the password from the hashed value. PBKDF2 is used in TrueCrypt to generate the key required to read the header information of the encrypted drive and which stores the encryption keys. In WPA-2 we use 4,096 iterations.

Within PowerShell, we have a method of PBKDF2:

$keyder=[Security.Cryptography.Rfc2898DeriveBytes]::Pbkdf2($password,$saltBytes,$iterations,$hash,$size)

and which requires a password, a salt value (as bytes), the number of iterations, the hash type (MD5, SHA-1 or SHA-512), and the number of bytes used for the output. For a 128-bit key to be derived, we require 16 bytes, but for a 256-bit key we need 32 bytes.

The following is the coding using PowerShell [here]:

$password = $Args[0]
$salt = $Args[1]
$iterations = [int]$Args[2]
$hash = $Args[3]
$size=$Args[4]
$saltBytes = [Text.Encoding]::UTF8.GetBytes($salt)
$keyder=[Security.Cryptography.Rfc2898DeriveBytes]::Pbkdf2($password,$saltBytes,$iterations,$hash,$size)
"Password: "+$password
"Salt: "+$salt
"Iterations: "+$iterations
"Hash method: "+$hash
"Size: "+$size
"`nKey derivation (Hex): "+[System.Convert]::ToHexString($keyder)
"Key derivation (Hex): "+[System.Convert]::ToBase64String($keyder)

A sample run shows [here]:

Password: qwerty
Salt: test123
Iterations: 1500
Hash method: SHA384
Size: 32
Key derivation (Hex): 7A1DE374983CB727A2E37AB8324FA5FA2FA6A90DE30841F16919159119E4F292
Key derivation (Hex): eh3jdJg8tyei43q4Mk+l+i+mqQ3jCEHxaRkVkRnk8pI=

A sample run is here:

https://asecuritysite.com/powershell/ps_pbkdf2

Conclusions

Don’t hash a password to an encryption key using our standard hashing methods, as there are security issues and the hashing is fast. An improved method is to use PBKDF2, and which slows down the whole of the hashing process.