Example: Converting a password into a cryptographic key

The following code snippets are actually compiled and run during the BouncyGPG build process. This ensures that all examples are correct.

Usage with Bouncy-GPG

Cryptographic key derivation functions should be used to derive a key from a password (or any other source material). Bouncy-GPG uses SCrypt for key stretching.

The following snippet will derive a 256 bit key from a strong password. The derivation process is configured by SCryptKeyStretchingParameters.forStrongInputKeyMaterial(). This will give a very quick key derivation and is only secure because the password is very long and random. If a shorter password was used the recommendation would be to use forModeratelyStongInputKeyMaterial or forWeakInputKeyMaterial.

import java.nio.charset.StandardCharsets;

import org.bouncycastle.util.encoders.Hex;

import name.neuhalfen.projects.crypto.symmetric.keygeneration.impl.stretching.KeyStretching;
import name.neuhalfen.projects.crypto.symmetric.keygeneration.impl.stretching.SCryptKeyStretching;

// the password
// Rather obviously the password should NOT be stored as part of the source code
String password = "39akldsAFQqwmsx-+#aQcsvcu82cQd,.-ask72=cq8csf";

// The salt value should be application or installation specific.
// It is an absolute MUST that the salt is never changed! A different salt WILL result in a different
// key being generated!
// The salt value SHOULD be roughly as long as the key (e.g. 128/256 bit).
// The salt value SHOULD be random (so this salt value is actually a bad example)
final byte[] SALT = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");

// In this case we are using a 256 bit key, e.g. for AES-256.
final int DESIRED_KEY_LENGTH = 256;

// Use forSensitiveStorage to derive e.g. a masterkey at application startup.
// forSensitiveStorage is really slow. If you are very sure that you have a very
// good (22 characters random or longer) password for 128 bit keys / 44 character 
// passwords for 256 bit keys you can consider using forQuickDerivation.
final SCryptKeyStretching.SCryptKeyStretchingParameters stretchingParameters = SCryptKeyStretching.SCryptKeyStretchingParameters.forStrongInputKeyMaterial();

final KeyStretching streching = new SCryptKeyStretching(stretchingParameters);

byte[] key = streching.strengthenKey(SALT, password.getBytes(StandardCharsets.UTF_8), DESIRED_KEY_LENGTH);
return key;

The key generated by this sequence is 0x89d4a9b23d8a88d09e559aeeaa7cfa4a029673b01d969a2f523a7c1f749c330b.

Example

The password s3cret (which is way to short!) might be derived to the key 0x81d0994d0aa21b786d6b8dc45fc09f31.

Calling the derivation function with the same parameters again yields the same result (0x81d0994d0aa21b786d6b8dc45fc09f31).

Using the same password with different configuration parameters for the same password (e.g. with a different salt value) yields a different key.

Example: Key length is independent from password length

The length of the derived key is independent from the length of the password [1]:

Password Desired key length in bit derived key length of derived key
x 128 0xec8bf237a7207690e91cf6bd27746f3b 128
short password 128 0x185e58c7cf69baef9d2866b70ec1581c 128
short password 256 0x185e58c7cf69baef9d2866b70ec1581ceb8ff7c5c2169170cba1cf4adc48815a 256
a bit longer PASSWORD 128 0xb201445d3bcdc7a07c469b7d7ef8988c 128

[1] Salt and all other derivation parameters fixed for this run. Changing the salt or the parameters of the derivation function does not change the key length.

Example: Prevent pre-computation with salt

Changing the salt value will derive different keys from the same password (thus making precomputing infeasible):

Password Salt derived key
a bit longer PASSWORD 0x00000000000000000000000000000000 0x31709b5748dd11a80de4ba31571928d3
short password 0x00000000000000000000000000000000 0xefab7a10563f8d81347c8c4401553902
short password 0x00000000000000000000000000000001 0xc0c153c9b79a92af8bdb08cb5defbd11
short password 0x0123456789abcdef0123456789abcdef 0x89b4d1be23e0f1f56ba29ee36f1e52ba