The Konfigyr Crypto library defines instructs how should a Spring Application perform crypto operations, generate cryptographic material and manage its lifecycle. It attempts to define an API that best describes cryptography best practices how should protect your data and protect the encryption keys that protect your data.
Konfigyr Crypto does not implement nor provides any direct cryptographic implementations, its goal is to provide an API how should those libraries be incorporated into an application. We recommend using well established cryptography libraries to perform cryptographic operations, such as Google Tink or BouncyCastle.
Library enforces a two-tier approach, a recommended industry standard, to encrypting data. In a two-tier approach there are two types of encryption keys. First is the key you used to encrypt data, usually referred to as a Data Encryption Key (DEK). The second key that is only used to encrypt the DEKs, referred to as a Master Key or Key Encryption Key (KEK), that generates the Encrypted Data Encryption Key (eDEK) which can than safely be stored in a persistent storage like a database or a file system.
Where possible, Key Encryption Keys should be stored in a separate location from Encrypted Data Encryption Key. For example, if the DEK is stored in a database, the KEK should be stored in the filesystem. This means that if an attacker only has access to one of these (for example through directory traversal or SQL injection), they cannot access both the keys and the data.
It is recommended that your Key Encryption Keys are managed by an external Key Management Service where wrapping and unwrapping of the DEKs occurs on the KMS servers . This way the private key material of the KEK is not known to your application making your system more resilient to attackers.
The goal of this library is not re-implement the wheel when it comes to cryptography, but rather to define a Java API how should a client application encrypt data and manage the keys that are used to encrypt it.
Let's break down the library into couple of most used types and services:
Keyset
- represents the Data Encryption Key (DEK)EncryptedKeyset
- represents the encrypted Data Encryption Key (eDEK)KeyEncryptionKey
- well, the name says itKeysetFactory
- generates the keysets used to encrypt the dataKeysetStore
- used to generate, read and manipulate keysets or DEKsKeysetRepository
- used to read and store the encrypted DEKs
The Keyset
is the focal point when working with this library. They represent a collection of keys which are performing certain cryptographic operations that is defined by its Algorithm
.
Here is an implementation example of Spring BytesEncryptor
interface that uses a Keyset
:
public class KeysetBytesEncryptor {
private final Keyset keyset;
@Override
public byte[] encrypt(byte[] byteArray) {
return keyset.encrypt(new ByteArray(byteArray)).array();
}
@Override
public byte[] decrypt(byte[] encryptedByteArray) {
return keyset.decrypt(new ByteArray(encryptedByteArray)).array();
}
}
The implementation of the Keyset
and how the cryptographic operations are performed is the job of the KeysetFactory
. This interface bridges the gap between the Konfigyr Crypto API and the actual cryptography library that would generate the key material and define how should it be used.
Factories should be able to:
- generate new keysets based upon the
Algorithm
that they define and support - wrap, or encrypt, the keysets before they are stored by the repository
- unwrap, or decrypt, the encrypted keysets before they can be used
Konfigyr Crypto comes with the following implementations of the KeysetFactory
which you can use:
The KeyEncryptionKey
is provided by the KeyEncryptionKeyProvider
, there needs to be at least one provider with at least one KEK in order to use this library to generate the Keyset
.
Here is an example how you can define a KeyEncryptionKeyProvider
as Spring Bean which uses a randomly generated Tink based KeyEncryptionKey
:
class KeyEncryptionKeyProviderConfiguration {
@Bean
KeyEncryptionKeyProvider myKeyEncryptionKeyProvider() {
return KeyEncryptionKeyProvider.of("my-kek-provider", List.of(
TinkKeyEncryptionKey.builder("my-kek-provider").generate("my-kek")
));
}
}
When using the konfigyr-crypto-tink
, it is recommended to use a KmsClient
with envelope encryption as your KeyEncryptionKey
. Tink comes with Google and AWS KMS client implementations by you can easily create your own implementation of the KmsClient
. Please refer to the Google Tink Documentation how they are used or implemented.
Here is an example of using AWS KMS to declare a KeyEncryptionKey
:
class KeyEncryptionKeyProviderConfiguration {
@Bean
KeyEncryptionKeyProvider myKeyEncryptionKeyProvider() {
return KeyEncryptionKeyProvider.of("my-kek-provider", List.of(
TinkKeyEncryptionKey.builder("my-kek-provider").generate("my-kek")
));
}
@Bean
KeyEncryptionKeyProvider kmsKeyEncryptionKeyProvider() {
return KeyEncryptionKeyProvider.of("kms-provider", List.of(
TinkKeyEncryptionKey.builder("kms-provider").kms(
"aws-kms://arn:aws:kms:us-west-2:account-id:key/key-id", // KEK ID is the same as the key ARN
"AES256_GCM" // algorithm used to create the DEK for the Keyset
)
));
}
}
Store is a Spring Bean which the application developers would use to interact with their Data Encryption Keys or DEKs. It bridges the actual cryptography and storage implementations in one place.
When you are retrieving a Keyset
the store would retrieve the EncryptedKeyset
, find which KeyEncryptionKey
was used to wrap it and unwrap and construct it using the responsible KeysetFactory
.
Here is an example how to create a Keyset
based BytesEncryptor
implementation using the KeysetStore
class KeysetBytesEncryptorFactory {
private final KeysetStore store;
public KeysetBytesEncryptor create(String keysetName) {
return new KeysetBytesEncryptor(store.read(keysetName));
}
}
The reversed process is applied when you wish to generate or update the Keyset
, it would wrap the keys using the responsible KeyEncryptionKey
and store the KeyEncryptionKey
using the defined KeysetRepository
implementation.
Here is an example how a new Tink keyset is created, rotated or removed:
import java.time.Duration;
class TinkExample {
private final KeysetStore store;
public Keyset create() {
return store.create("my-kek-provider", "my-kek", KeysetDefinition.of(
"my-dek", // give a name to your DEK
TinkAlgorithm.AES256_GCM, // define the Tink algorithm to the DEK
Duration.of(90) // define the rotation frequency for your DEK
));
}
public Keyset createWithKek() {
final KeyEncryptionKey kek = store.kek("my-kek-provider", "my-kek");
return store.create(kek, KeysetDefinition.of(
"my-dek", // give a name to your DEK
TinkAlgorithm.AES256_GCM, // define the Tink algorithm to the DEK
Duration.of(90) // define the rotation frequency for your DEK
));
}
public Keyset rotate() {
return store.rotate("my-dek");
}
public Keyset remove() {
return store.remove("my-dek");
}
}
Keyset repository is a simple interface which goal is to implement how should an EncryptedKeyset
be stored, retrieved or removed.
Konfigyr Crypto comes with the following implementations of the KeysetRepository
which you can use:
Konfigyr Crypto uses a Gradle-based build system. In the instructions below, ./gradlew
is invoked from the root of the source tree and serves as a cross-platform, self-contained bootstrap mechanism for the build.
Git and the JDK 17 build.
git clone git@github.com:konfigyr/konfigyr-crypto.git
./gradlew publishToMavenLocal
./gradlew build
Discover more commands with ./gradlew tasks
.
Try reaching out to the maintainers in our Gitter chat. Commercial support is available too.
Pull requests are more than welcome; see the contributor guidelines for details.
Konfigyr Crypto library is Open Source software released under the Apache 2.0 license.