When in doubt, encrypt. When not in doubt, be in doubt.
- key versioning (to help migrating to new key without need to convert data)
- uses 256-bit AES by default
- supports any encryption available in Java (via Java Cryptography Architecture or JCA)
- simple
- no dependencies
Add dependency:
<dependency>
<groupId>com.bol</groupId>
<artifactId>cryptvault</artifactId>
<version>3-2.0.0</version>
</dependency>
And add the following to your application.yml
:
cryptvault:
keys:
- version: 1
key: hqHKBLV83LpCqzKpf8OvutbCs+O5wX5BPu3btWpEvXA=
And you're done!
Example usage:
@Autowired CryptVault cryptVault;
// encrypt
byte[] encrypted = cryptVault.encrypt("rock".getBytes());
// decrypt
byte[] decrypted = cryptVault.decrypt(encrypted);
new String(decrypted).equals("rock"); // true
This library uses the encryption keys specified in the configuration directly. Notably, it does not use any key-derivation. That means that you are responsible for providing a key from a high-entropy source.
The length of the key depends on the algorithm specified. When using AES-256, you need to provide a key that is 256 bits/32 bytes long. (For comparison, the weak DES uses 64-bit keys.)
To generate a key suitable for AES-256 bit, you can use the following command:
$ dd if=/dev/urandom bs=1 count=32 | base64
It is advisable to rotate your keys every now and then. To do so, define a new
key version in application.yml
:
cryptvault:
keys:
- version: 1
key: hqHKBLV83LpCqzKpf8OvutbCs+O5wX5BPu3btWpEvXA=
- version: 2
key: ge2L+MA9jLA8UiUJ4z5fUoK+Lgj2yddlL6EzYIBqb1Q=
CryptVault automatically uses the highest versioned key for encryption by default, but supports decryption using any of the keys. This allows you to deploy a new key, and either let old data slowly get phased out, or run a nightly load+save batch job to force key migration. Once all old keys are phased out, you may remove the old key from the configuration.
You can use
cryptvault:
default-key: 1
to override which version of the defined keys is considered default.
Instead of using the default AES-256 in CBC mode, you can specify the algorithm, mode of operation and padding scheme directly in the configuration:
cryptvault:
keys:
version: 1
key: Ifw/+pLuWBjn7a1mjuToQ8hpIh8DV0WLf9b4z7iinGs=
transformation: AES/GCM/NoPadding
You can use all the algorithms specified by JCA. Other valid transformations are, for example, "DES/CTR/NoPadding" and "ChaCha20-Poly1305". For a comprehensive list, see Java Security Standard Algorithm Names.
The YAML key is called "transformation" because it signifies more than just an algorithm, but rather a set of operations performed on an input to produce some output. Naming it this way is consistent with JCA parlance.
The encrypted blobs look like (numbers are bits):
0 8 16 24
+---------+---------+---------+--------------------+--------------------+
|proto |key |param |params |ciphertext |
|version |version |length | ... | ... |
|8 |8 |8 |[0,255] |[16,inf) |
+---------+---------+---------+--------------------+--------------------+
proto version
is the protocol version of this blob. Having a version allows making improvements to this blob over time without having to decrypt all the old encryptions and encrypt it under a new (versionless) version.key version
is the user-controlled version of the key that was used to encrypt the data in this blob.param length
is the length of next field, the algorithm parametersparams
are the algorithms parameters that that need to be known in order to decrypt the blob successfully. For example, when using AES/CBC/PKCS5Padding, this will (among some overhead) contain the 16-byte IV. Seejava.security.AlgorithmParameters#getEncoded
for more information.ciphertext
contains the output of applying the specified transformation under the specified key to the input.
Depending on the cipher, whether an IV or tag are used and the padding scheme you must expect some overhead for encryption. The default cipher, AES-256-CBC with PKCS #5 padding, requires an extra [22, 37] bytes: proto version (1) + key version (1) + param length (1) + algorithm parameters (18) + padding (best case: 1, worst case: 16).
- Add
legacy: true
to keys that were in use under version 1. - Create a new key version that will be used for new encryptions.
cryptvault:
keys:
# the legacy key version (can only decrypt!)
- version: 1
key: yaF4Gi13Gp+gF5Tm+jMkYbQKMO3c6KYZbQmMqXQyid0=
legacy: true
# the new version (can encrypt/decrypt as usual)
- version: 2
key: CqeKXVZuDbeMk0/h1zZrBG0Mul4qMnqShaGjkxWrlQ0=
Version 2 introduced a new format of the binary blob. This provides certain benefits (see under Format of the encrypted blob, above). However, the old encrypted blobs have become incompatible as a result of this breaking change. You can still decrypt the blobs, however. Encrypting with these legacy key versions is not supported, however.
To migrate:
- Add
legacy: true
to the legacy key version(s) in the config. - Create a new key version that will be used for new encryptions.
Old encrypted blobs will not be updated automatically since this library does not handle persistence. There is little harm in keeping them around as they are still secure. However, should you wish to upgrade the stored blobs, decrypt them and then overwrite them with a fresh encrypted version under the new key version.