The following code snippets are actually compiled and run during the BouncyGPG build process. This ensures that all examples are correct.
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.
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.
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.
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 |