Client Side Encryption is a feature that encrypts data in your application before it is sent to MongoDB. We recommend you get familiar with the concepts, ideally from the MongoDB Documentation to learn more about its capabilities and restrictions before you continue applying Encryption through Spring Data.
Note
|
Make sure to set the drivers |
Choosing CSFLE gives you full flexibility and allows you to use different keys for a single field, eg. in a one key per tenant scenario.
Please make sure to consult the MongoDB CSFLE Documentation before you continue reading.
MongoDB supports Client-Side Field Level Encryption out of the box using the MongoDB driver with its Automatic Encryption feature. Automatic Encryption requires a JSON Schema that allows to perform encrypted read and write operations without the need to provide an explicit en-/decryption step.
Please refer to the JSON Schema section for more information on defining a JSON Schema that holds encryption information.
To make use of a the MongoJsonSchema
it needs to be combined with AutoEncryptionSettings
which can be done eg. via a MongoClientSettingsBuilderCustomizer
.
@Bean
MongoClientSettingsBuilderCustomizer customizer(MappingContext mappingContext) {
return (builder) -> {
// ... keyVaultCollection, kmsProvider, ...
MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
MongoJsonSchema patientSchema = schemaCreator
.filter(MongoJsonSchemaCreator.encryptedOnly())
.createSchemaFor(Patient.class);
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
.keyVaultNamespace(keyVaultCollection)
.kmsProviders(kmsProviders)
.extraOptions(extraOpts)
.schemaMap(Collections.singletonMap("db.patient", patientSchema.schemaDocument().toBsonDocument()))
.build();
builder.autoEncryptionSettings(autoEncryptionSettings);
};
}
Explicit encryption uses the MongoDB driver’s encryption library (org.mongodb:mongodb-crypt
) to perform encryption and decryption tasks.
The @ExplicitEncrypted
annotation is a combination of the @Encrypted
annotation used for JSON Schema creation and a Property Converter.
In other words, @ExplicitEncrypted
uses existing building blocks to combine them for simplified explicit encryption support.
Note
|
Fields annotated with @ExplicitEncrypted(…)
String simpleValue; (1)
@ExplicitEncrypted(…)
Address address; (2)
@ExplicitEncrypted(…)
List<...> list; (3)
@ExplicitEncrypted(…)
Map<..., ...> mapOfString; (4)
|
Client-Side Field Level Encryption allows you to choose between a deterministic and a randomized algorithm. Depending on the chosen algorithm, different operations may be supported.
To pick a certain algorithm use @ExplicitEncrypted(algorithm)
, see EncryptionAlgorithms
for algorithm constants.
Please read the Encryption Types manual for more information on algorithms and their usage.
To perform the actual encryption we require a Data Encryption Key (DEK).
Please refer to the MongoDB Documentation for more information on how to set up key management and create a Data Encryption Key.
The DEK can be referenced directly via its id
or a defined alternative name.
The @EncryptedField
annotation only allows referencing a DEK via an alternative name.
It is possible to provide an EncryptionKeyResolver
, which will be discussed later, to any DEK.
@EncryptedField(algorithm=…, altKeyName = "secret-key") (1)
String ssn;
@EncryptedField(algorithm=…, altKeyName = "/name") (2)
String ssn;
-
Use the DEK stored with the alternative name
secret-key
. -
Uses a field reference that will read the actual field value and use that for key lookup. Always requires the full document to be present for save operations. Fields cannot be used in queries/aggregations.
By default, the @ExplicitEncrypted(value=…)
attribute references a MongoEncryptionConverter
.
It is possible to change the default implementation and exchange it with any PropertyValueConverter
implementation by providing the according type reference.
To learn more about custom PropertyValueConverters
and the required configuration, please refer to the Property Converters - Mapping specific fields section.
Choosing QE enables you to run different types of queries, like range or equality, against encrypted fields.
Please make sure to consult the MongoDB QE Documentation before you continue reading to learn more about QE features and limitations.
Queryable Encryption requires upfront declaration of certain aspects allowed within an actual query against an encrypted field. The information covers the algorithm in use as well as allowed query types along with their attributes and must be provided when creating the collection.
MongoOperations#createCollection(…)
can be used to do the initial setup for collections utilizing QE.
The configuration for QE via Spring Data uses the same building blocks (a JSON Schema creation) as CSFLE, converting the schema/properties into the configuration format required by MongoDB.
- Manual Collection Setup
-
CollectionOptions collectionOptions = CollectionOptions.encryptedCollection(options -> options .queryable(encrypted(string("ssn")).algorithm("Indexed"), equality().contention(0)) .queryable(encrypted(int32("age")).algorithm("Range"), range().contention(8).min(0).max(150)) .queryable(encrypted(int64("address.sign")).algorithm("Range"), range().contention(2).min(-10L).max(10L)) ); mongoTemplate.createCollection(Patient.class, collectionOptions); (1)
-
Using the template to create the collection may prevent capturing generated keyIds. In this case render the
Document
from the options and use thecreateEncryptedCollection(…)
method via the encryption library.
-
- Derived Collection Setup
-
class Patient { @Id String id; @Encrypted(algorithm = "Indexed") // @Queryable(queryType = "equality", contentionFactor = 0) String ssn; @RangeEncrypted(contentionFactor = 8, rangeOptions = "{ 'min' : 0, 'max' : 150 }") Integer age; Address address; } MongoJsonSchema patientSchema = MongoJsonSchemaCreator.create(mappingContext) .filter(MongoJsonSchemaCreator.encryptedOnly()) .createSchemaFor(Patient.class); CollectionOptions collectionOptions = CollectionOptions.encryptedCollection(patientSchema); mongoTemplate.createCollection(Patient.class, collectionOptions); (1)
-
Using the template to create the collection may prevent capturing generated keyIds. In this case render the
Document
from the options and use thecreateEncryptedCollection(…)
method via the encryption library.
The
Queryable
annotation allows to define allowed query types for encrypted fields.@RangeEncrypted
is a combination of@Encrypted
and@Queryable
for fields allowingrange
queries. It is possible to create custom annotations out of the provided ones. -
- MongoDB Collection Info
-
{ name: 'patient', type: 'collection', options: { encryptedFields: { escCollection: 'enxcol_.test.esc', ecocCollection: 'enxcol_.test.ecoc', fields: [ { keyId: ..., path: 'ssn', bsonType: 'string', queries: [ { queryType: 'equality', contention: Long('0') } ] }, { keyId: ..., path: 'age', bsonType: 'int', queries: [ { queryType: 'range', contention: Long('8'), min: 0, max: 150 } ] }, { keyId: ..., path: 'address.sign', bsonType: 'long', queries: [ { queryType: 'range', contention: Long('2'), min: Long('-10'), max: Long('10') } ] } ] } } }
Note
|
|
MongoDB supports Queryable Encryption out of the box using the MongoDB driver with its Automatic Encryption feature. Automatic Encryption requires a JSON Schema that allows to perform encrypted read and write operations without the need to provide an explicit en-/decryption step.
All you need to do is create the collection according to the MongoDB documentation. You may utilize techniques to create the required configuration outlined in the section above.
Explicit encryption uses the MongoDB driver’s encryption library (org.mongodb:mongodb-crypt
) to perform encryption and decryption tasks based on the meta information provided by annotation within the domain model.
Note
|
There is no official support for using Explicit Queryable Encryption.
The audacious user may combine |
The converter setup for MongoEncryptionConverter
requires a few steps as several components are involved.
The bean setup consists of the following:
-
The
ClientEncryption
engine -
A
MongoEncryptionConverter
instance configured withClientEncryption
and aEncryptionKeyResolver
. -
A
PropertyValueConverterFactory
that uses the registeredMongoEncryptionConverter
bean.
The EncryptionKeyResolver
uses an EncryptionContext
providing access to the property allowing for dynamic DEK resolution.
class Config extends AbstractMongoClientConfiguration {
@Autowired ApplicationContext appContext;
@Bean
ClientEncryption clientEncryption() { (1)
ClientEncryptionSettings encryptionSettings = ClientEncryptionSettings.builder();
// …
return ClientEncryptions.create(encryptionSettings);
}
@Bean
MongoEncryptionConverter encryptingConverter(ClientEncryption clientEncryption) {
Encryption<BsonValue, BsonBinary> encryption = MongoClientEncryption.just(clientEncryption);
EncryptionKeyResolver keyResolver = EncryptionKeyResolver.annotated((ctx) -> …); (2)
return new MongoEncryptionConverter(encryption, keyResolver); (3)
}
@Override
protected void configureConverters(MongoConverterConfigurationAdapter adapter) {
adapter
.registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(appContext)); (4)
}
}
-
Set up a
Encryption
engine usingcom.mongodb.client.vault.ClientEncryption
. The instance is stateful and must be closed after usage. Spring takes care of this becauseClientEncryption
isCloseable
. -
Set up an annotation-based
EncryptionKeyResolver
to determine theEncryptionKey
from annotations. -
Create the
MongoEncryptionConverter
. -
Enable for a
PropertyValueConverter
lookup from theBeanFactory
.