Skip to content

Commit d1667bd

Browse files
authored
Merge pull request #13 from rustagir/DOCSP-37854-encrypt
DOCSP-37854: encryption
2 parents dcbc17a + fcb26fb commit d1667bd

File tree

2 files changed

+341
-0
lines changed

2 files changed

+341
-0
lines changed

source/tutorials.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ Tutorials
1212
/tutorials/read-ops/
1313
/tutorials/write-ops/
1414
/tutorials/aggregation/
15+
/tutorials/encrypt/
1516

source/tutorials/encrypt.txt

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
.. _javars-encrypt:
2+
3+
======================
4+
Client-Side Encryption
5+
======================
6+
7+
.. contents:: On this page
8+
:local:
9+
:backlinks: none
10+
:depth: 2
11+
:class: singlecol
12+
13+
Starting in v4.2, MongoDB supports client-side encryption. Client-side
14+
encryption allows administrators and developers to encrypt specific data
15+
fields in addition to providing other MongoDB encryption features.
16+
17+
With field-level encryption, developers can encrypt fields on the client-side
18+
without any server-side configuration or directives. Client-side field
19+
level encryption supports workloads where applications must guarantee
20+
that unauthorized parties, including server administrators, cannot
21+
read the encrypted data.
22+
23+
.. important::
24+
25+
This guide uses the ``Subscriber`` implementations, which are
26+
described in the :ref:`Quick Start Primer <javars-primer>`.
27+
28+
Installation
29+
------------
30+
31+
The recommended way to get started using field-level encryption in
32+
your project is by using a dependency management system. Field-level
33+
encryption requires additional packages in addition to the
34+
driver.
35+
36+
.. note::
37+
38+
For instructions about how to install the {+driver-short+},
39+
see the :ref:`installation guide <javars-install>`.
40+
41+
libmongocrypt
42+
~~~~~~~~~~~~~
43+
44+
There is a separate JAR file containing ``libmongocrypt`` bindings.
45+
46+
.. tabs::
47+
48+
.. tab:: Maven
49+
:tabid: maven-installation
50+
51+
If you are using `Maven <https://maven.apache.org/>`__ to manage your
52+
packages, add the following entry to your ``pom.xml`` dependencies list:
53+
54+
.. code-block:: xml
55+
56+
<dependencies>
57+
<dependency>
58+
<groupId>org.mongodb</groupId>
59+
<artifactId>mongodb-crypt</artifactId>
60+
<version>1.2.1</version>
61+
</dependency>
62+
</dependencies>
63+
64+
.. tab:: Gradle
65+
:tabid: gradle-installation
66+
67+
If you are using `Gradle <https://gradle.org/>`__ to manage your
68+
packages, add the following entry to your dependencies list:
69+
70+
.. code-block:: java
71+
72+
dependencies {
73+
implementation 'org.mongodb:mongodb-crypt:1.2.1'
74+
}
75+
76+
mongocryptd Configuration
77+
~~~~~~~~~~~~~~~~~~~~~~~~~
78+
79+
The ``libmongocrypt`` bindings require the ``mongocryptd`` daemon/process to be
80+
running. A specific daemon/process URI can be configured in the
81+
``AutoEncryptionSettings`` class by setting ``mongocryptdURI`` in the
82+
``extraOptions`` setting.
83+
84+
Examples
85+
--------
86+
87+
The following example is a sample app that assumes the key and schema have
88+
already been created in MongoDB. The example uses a local key, but you
89+
can also use the AWS/Azure/GCP Key Management Service. The data in
90+
the ``encryptedField`` field is automatically encrypted on the insert
91+
and decrypted when using find on the client side.
92+
93+
The code example is from the `ClientSideEncryptionSimpleTour.java
94+
<{+driver-source-gh+}/blob/master/driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionSimpleTour.java>`__
95+
file in the driver source code GitHub repository.
96+
97+
.. code-block:: java
98+
99+
import com.mongodb.AutoEncryptionSettings;
100+
import com.mongodb.MongoClientSettings;
101+
import com.mongodb.reactivestreams.client.MongoClient;
102+
import com.mongodb.reactivestreams.client.MongoClients;
103+
import com.mongodb.reactivestreams.client.MongoCollection;
104+
import org.bson.Document;
105+
106+
import java.security.SecureRandom;
107+
import java.util.HashMap;
108+
import java.util.Map;
109+
110+
public class ClientSideEncryptionSimpleTour {
111+
112+
public static void main(final String[] args) {
113+
114+
// This would have to be the same master key as was used to create the encryption key
115+
final byte[] localMasterKey = new byte[96];
116+
new SecureRandom().nextBytes(localMasterKey);
117+
118+
Map<String, Map<String, Object>> kmsProviders = new HashMap<String, Map<String, Object>>() {{
119+
put("local", new HashMap<String, Object>() {{
120+
put("key", localMasterKey);
121+
}});
122+
}};
123+
124+
String keyVaultNamespace = "admin.datakeys";
125+
126+
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
127+
.keyVaultNamespace(keyVaultNamespace)
128+
.kmsProviders(kmsProviders)
129+
.build();
130+
131+
MongoClientSettings clientSettings = MongoClientSettings.builder()
132+
.autoEncryptionSettings(autoEncryptionSettings)
133+
.build();
134+
135+
MongoClient mongoClient = MongoClients.create(clientSettings);
136+
MongoCollection<Document> collection = mongoClient.getDatabase("test").getCollection("coll");
137+
138+
ObservableSubscriber<Void> successSubscriber = new OperationSubscriber<>();
139+
collection.drop().subscribe(successSubscriber);
140+
successSubscriber.await();
141+
142+
successSubscriber = new OperationSubscriber<>();
143+
collection.insertOne(new Document("encryptedField", "123456789")).subscribe(successSubscriber);
144+
successSubscriber.await();
145+
146+
ObservableSubscriber<Document> documentSubscriber = new PrintDocumentSubscriber();
147+
collection.find().first().subscribe(documentSubscriber);
148+
documentSubscriber.await();
149+
}
150+
}
151+
152+
.. note::
153+
154+
Automatic encryption is an **enterprise only** feature.
155+
156+
The following example shows how to configure the
157+
``AutoEncryptionSettings`` instance to create a new key and set the
158+
JSON schema map.
159+
160+
The code example is from the `ClientSideEncryptionAutoEncryptionSettingsTour.java
161+
<{+driver-source-gh+}/blob/master/driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java>`__
162+
file in the driver source code GitHub repository.
163+
164+
.. code-block:: java
165+
166+
import com.mongodb.ClientEncryptionSettings;
167+
import com.mongodb.ConnectionString;
168+
import com.mongodb.client.model.vault.DataKeyOptions;
169+
import com.mongodb.client.vault.ClientEncryption;
170+
import com.mongodb.client.vault.ClientEncryptions;
171+
import org.bson.BsonBinary;
172+
import org.bson.BsonDocument;
173+
174+
import java.util.Base64;
175+
176+
...
177+
178+
String keyVaultNamespace = "admin.datakeys";
179+
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
180+
.keyVaultMongoClientSettings(MongoClientSettings.builder()
181+
.applyConnectionString(new ConnectionString("mongodb://localhost"))
182+
.build())
183+
.keyVaultNamespace(keyVaultNamespace)
184+
.kmsProviders(kmsProviders)
185+
.build();
186+
187+
ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings);
188+
BsonBinary dataKeyId = clientEncryption.createDataKey("local", new DataKeyOptions());
189+
final String base64DataKeyId = Base64.getEncoder().encodeToString(dataKeyId.getData());
190+
191+
final String dbName = "test";
192+
final String collName = "coll";
193+
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
194+
.keyVaultNamespace(keyVaultNamespace)
195+
.kmsProviders(kmsProviders)
196+
.schemaMap(new HashMap<String, BsonDocument>() {{
197+
put(dbName + "." + collName,
198+
// Need a schema that references the new data key
199+
BsonDocument.parse("{"
200+
+ " properties: {"
201+
+ " encryptedField: {"
202+
+ " encrypt: {"
203+
+ " keyId: [{"
204+
+ " \"$binary\": {"
205+
+ " \"base64\": \"" + base64DataKeyId + "\","
206+
+ " \"subType\": \"04\""
207+
+ " }"
208+
+ " }],"
209+
+ " bsonType: \"string\","
210+
+ " algorithm: \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\""
211+
+ " }"
212+
+ " }"
213+
+ " },"
214+
+ " \"bsonType\": \"object\""
215+
+ "}"));
216+
}}).build();
217+
218+
Explicit Encryption and Decryption
219+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220+
221+
Explicit encryption and decryption is a MongoDB community feature and
222+
does not use the ``mongocryptd`` process. Explicit encryption is
223+
provided by the ``ClientEncryption`` class.
224+
225+
The code example is from the `ClientSideEncryptionExplicitEncryptionAndDecryptionTour.java
226+
<{+driver-source-gh+}/blob/master/driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionExplicitEncryptionAndDecryptionTour.java>`__
227+
file in the driver source code GitHub repository.
228+
229+
.. code-block:: java
230+
231+
// This would have to be the same master key as was used to create the encryption key
232+
final byte[] localMasterKey = new byte[96];
233+
new SecureRandom().nextBytes(localMasterKey);
234+
235+
Map<String, Map<String, Object>> kmsProviders = new HashMap<String, Map<String, Object>>() {{
236+
put("local", new HashMap<String, Object>() {{
237+
put("key", localMasterKey);
238+
}});
239+
}};
240+
241+
MongoNamespace keyVaultNamespace = new MongoNamespace("encryption.testKeyVault");
242+
243+
MongoClientSettings clientSettings = MongoClientSettings.builder().build();
244+
MongoClient mongoClient = MongoClients.create(clientSettings);
245+
246+
// Set up the key vault for this example
247+
MongoCollection<Document> keyVaultCollection = mongoClient
248+
.getDatabase(keyVaultNamespace.getDatabaseName())
249+
.getCollection(keyVaultNamespace.getCollectionName());
250+
251+
ObservableSubscriber<Void> successSubscriber = new OperationSubscriber<>();
252+
keyVaultCollection.drop().subscribe(successSubscriber);
253+
successSubscriber.await();
254+
255+
// Ensure that two data keys cannot share the same keyAltName.
256+
ObservableSubscriber<String> indexSubscriber = new OperationSubscriber<>();
257+
keyVaultCollection.createIndex(Indexes.ascending("keyAltNames"),
258+
new IndexOptions().unique(true)
259+
.partialFilterExpression(Filters.exists("keyAltNames")))
260+
.subscribe(indexSubscriber);
261+
indexSubscriber.await();
262+
263+
MongoCollection<Document> collection = mongoClient.getDatabase("test").getCollection("coll");
264+
successSubscriber = new OperationSubscriber<>();
265+
collection.drop().subscribe(successSubscriber);
266+
successSubscriber.await();
267+
268+
// Create the ClientEncryption instance
269+
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
270+
.keyVaultMongoClientSettings(MongoClientSettings.builder()
271+
.applyConnectionString(new ConnectionString("mongodb://localhost"))
272+
.build())
273+
.keyVaultNamespace(keyVaultNamespace.getFullName())
274+
.kmsProviders(kmsProviders)
275+
.build();
276+
277+
ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings);
278+
279+
BsonBinary dataKeyId = clientEncryption.createDataKey("local", new DataKeyOptions());
280+
281+
// Explicitly encrypt a field
282+
BsonBinary encryptedFieldValue = clientEncryption.encrypt(new BsonString("123456789"),
283+
new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId));
284+
285+
ObservableSubscriber<InsertOneResult> insertOneSubscriber = new OperationSubscriber<>();
286+
collection.insertOne(new Document("encryptedField", encryptedFieldValue))
287+
.subscribe(insertOneSubscriber);
288+
insertOneSubscriber.await();
289+
290+
ObservableSubscriber<Document> documentSubscriber = new OperationSubscriber<>();
291+
collection.find().first().subscribe(documentSubscriber);
292+
293+
Document doc = documentSubscriber.get().get(0);
294+
System.out.println(doc.toJson());
295+
296+
// Explicitly decrypt the field
297+
System.out.println(
298+
clientEncryption.decrypt(new BsonBinary(doc.get("encryptedField", Binary.class).getData()))
299+
);
300+
301+
Explicit Encryption and Auto Decryption
302+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
303+
304+
Although automatic encryption requires MongoDB 4.2 enterprise or a
305+
MongoDB 4.2 Atlas cluster, automatic decryption is supported for all
306+
users. To configure automatic decryption without automatic encryption,
307+
set ``bypassAutoEncryption(true)``.
308+
309+
The code example is from the `ClientSideEncryptionExplicitEncryptionOnlyTour.java
310+
<{+driver-source-gh+}/blob/master/driver-reactive-streams/src/examples/reactivestreams/tour/ClientSideEncryptionExplicitEncryptionOnlyTour.java>`__
311+
file in the driver source code GitHub repository.
312+
313+
.. code-block:: java
314+
315+
...
316+
MongoClientSettings clientSettings = MongoClientSettings.builder()
317+
.autoEncryptionSettings(AutoEncryptionSettings.builder()
318+
.keyVaultNamespace(keyVaultNamespace.getFullName())
319+
.kmsProviders(kmsProviders)
320+
.bypassAutoEncryption(true)
321+
.build())
322+
.build();
323+
MongoClient mongoClient = MongoClients.create(clientSettings);
324+
325+
...
326+
327+
// Explicitly encrypt a field
328+
BsonBinary encryptedFieldValue = clientEncryption.encrypt(new BsonString("123456789"),
329+
new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId));
330+
331+
ObservableSubscriber<InsertOneResult> insertOneSubscriber = new OperationSubscriber<>();
332+
collection.insertOne(new Document("encryptedField", encryptedFieldValue))
333+
.subscribe(insertOneSubscriber);
334+
insertOneSubscriber.await();
335+
336+
ObservableSubscriber<Document> documentSubscriber = new OperationSubscriber<>();
337+
collection.find().first().subscribe(documentSubscriber);
338+
339+
Document doc = documentSubscriber.get().get(0);
340+
System.out.println(doc.toJson());

0 commit comments

Comments
 (0)