Although initially intended for creating better secret keys, Key derivation functions are probably better for storing hashed passwords. This post tells you more about what a Key derivation function does, how you can use it for storing hashed passwords and how you can use it in the Octopus framework.
What is it?
For the symmetric encryption you need the secret key, an array of bytes basically. A password can also be converted to a byte array so a password can be used. Then the key doesn’t need to be stored and when the user types in the password, the message can be encrypted or decrypted.
But of course, passwords are not a really good candidate as the key for an encryption.
– passwords consist of characters which have all more or less the same bit pattern. So the ‘random’ spread of bits through-out the byte array is not good.
– password are mostly short, so your key is probably too small.
So that is where the Key derivation functions come into the picture. They take a password or passphrase (a sentence used as a password) and can generate a byte array out of this. It has the following characteristics
– It needs a salt, a byte array, to be able to do his work. So salting is mandatory.
– When the same password/passphrase and salt are presented to the Key Derivation function, it results in the same byte array outcome (so it is deterministic, no random outcome)
– It is a one-way procedure. So from the output buts, the original password / passphrase can’t be reconstructed.
– The output, the number of bits generated, is configurable (important because key sizes for encryption must have some minimum lengths)
– The functions are intentionally slow to counter any brute-force attack. And iterations can be applied to make it even more difficult.
You can read more background info on this wiki page
for hashed passwords
In the previous section, you can read why those Key derivation functions are very good for generating a secret key (for symmetric encryption). The properties are designed specifically for that purpose.
But very rapidly, they saw an alternative use for these functions, create hashed passwords. The reason why are easy to see
– When offered the same input, the same output is generated
– The input can’t be reconstructed from the output.
But they have a few other properties which make it a better candidate for storing passwords then the classic hashing functions.
– The computation requires a salt, so generating an output byte array is not possible without it. This in contrast to a classic hash algorithm where we add the salt to the password to overcome the use of rainbow tables.
– The computation of Key Derivation functions are slow, those of hash algorithms are fast. And performing the hashing function multiple times is not a good idea because of the increase in collisions.
So we are now at the point that we can say that Key Derivation functions are better for storing hashed passwords. So how can we get started with it in our Java programs?
There are different algorithms developed (just like there are different hash algorithms) like argon2, sCript, bCript or PBKDF2.
Only one is available by default on the JVM, PBKDF2, although most people say argon2 is the best algorithm. So let us see how we can use that one on the JVM.
Since Key derivation functions are something completely different then hash algorithms, these aren’t available from java.security.MessageDigest class. But there is another standard java class available which give you an instance of the algorithm, javax.crypto.SecretKeyFactory
The following snippets generate the BASE64 encoded output for a certain password. It also generates a salt value.
String password = "atbash"; String keyAlgorithmName = "PBKDF2WithHmacSHA256"; SecretKeyFactory keyFactory; keyFactory = SecretKeyFactory.getInstance(keyAlgorithmName); char chars = password.toCharArray(); byte salt = new byte; new SecureRandom().nextBytes(salt); int hashIterations = 1024; int keySizeBytes = 32; byte encoded = keyFactory.generateSecret( new PBEKeySpec(chars, salt, hashIterations, keySizeBytes * 8)).getEncoded(); String hashed = Base64.getEncoder().encodeToString(encoded); System.out.println(hashed);
So using them us quite easily.
Also within Octopus, there is support added for the PBKDF2 algorithm within version 0.9.7.1. And since the way you use it is so similar then classic Hash algorithms, the only thing you need to do is set the algorithm name in the configuration file.
See also the chapter in the Octopus Cookbook
Key derivation functions are designed for creating a key which can be used in Symmetric encryption algorithms based on a password.
But it turns out that they are also very well suited to stored hashed passwords. So try them out and use them in your next project with or without the Octopus security framework.