Docs Menu
Docs Home
/ / /
Scala
/

Client-Side Encryption

On this page

  • Installation
  • libmongocrypt
  • mongocryptd Configuration
  • Examples
  • Auto Encryption and Decryption
  • Specify a Data Encryption Key and Encrypted Fields
  • Explicit Encryption and Decryption
  • Explicit Encryption and Auto Decryption

Starting in v4.2, MongoDB supports client-side encryption. Client-side encryption allows administrators and developers to encrypt specific data fields in addition to providing other MongoDB encryption features.

With field-level encryption, developers can encrypt fields on the client-side without any server-side configuration or directives. Client-side field level encryption supports workloads where applications must guarantee that unauthorized parties, including server administrators, cannot read the encrypted data.

Note

This guide uses the Observable implicits as covered in the Quick Start Primer.

The recommended way to get started using field-level encryption in your project is by using a dependency management system. Field-level encryption requires additional packages in addition to the driver.

Note

For instructions about how to install the Scala driver, see the Installation guide.

There is a separate JAR file containing libmongocrypt bindings.

If you are using Maven to manage your packages, add the following entry to your pom.xml dependencies list:

<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-crypt</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>

If you are using Gradle to manage your packages, add the following entry to your dependencies list:

dependencies {
implementation 'org.mongodb:mongodb-crypt:1.2.1'
}

The libmongocrypt bindings require the mongocryptd daemon/process to be running. A specific daemon/process URI can be configured in the AutoEncryptionSettings class by setting mongocryptdURI in the extraOptions setting.

The following example is a sample app that assumes the key and schema have already been created in MongoDB. The example uses a local key, but you can also use the AWS/Azure/GCP Key Management Service. The data in the encryptedField field is automatically encrypted on the insert and decrypted when using find on the client side.

The code example is from the ClientSideEncryptionSimpleTour.scala file in the driver source code GitHub repository.

import java.security.SecureRandom
import org.mongodb.scala.{AutoEncryptionSettings, Document, MongoClient, MongoClientSettings}
import tour.Helpers._
import scala.collection.JavaConverters._
object ClientSideEncryptionSimpleTour {
def main(args: Array[String]): Unit = {
val localMasterKey = new Array[Byte](96)
new SecureRandom().nextBytes(localMasterKey)
val kmsProviders = Map("local" -> Map[String, AnyRef]("key" -> localMasterKey).asJava).asJava
val keyVaultNamespace = "admin.datakeys"
val autoEncryptionSettings = AutoEncryptionSettings.builder()
.keyVaultNamespace(keyVaultNamespace).kmsProviders(kmsProviders).build()
val clientSettings = MongoClientSettings.builder()
.autoEncryptionSettings(autoEncryptionSettings).build()
val mongoClient = MongoClient(clientSettings)
val collection = mongoClient.getDatabase("test").getCollection("coll")
collection.drop().headResult()
collection.insertOne(Document("encryptedField" -> "123456789")).headResult()
collection.find().first().printHeadResult()
// release resources
mongoClient.close()
}
}

Note

Automatic encryption is an enterprise only feature.

The following example shows how to configure the AutoEncryptionSettings instance to create a new key and set the JSON schema map.

The code example is from the ClientSideEncryptionAutoEncryptionSettingsTour.scala file in the driver source code GitHub repository.

import java.security.SecureRandom
import java.util.Base64
import scala.collection.JavaConverters._
import org.mongodb.scala._
import org.mongodb.scala.bson.BsonDocument
import org.mongodb.scala.model.vault.DataKeyOptions
import org.mongodb.scala.vault.ClientEncryptions
import tour.Helpers._
...
val keyVaultNamespace = "admin.datakeys"
val clientEncryptionSettings = ClientEncryptionSettings.builder()
.keyVaultMongoClientSettings(
MongoClientSettings.builder().applyConnectionString(ConnectionString("mongodb://localhost")).build())
.keyVaultNamespace(keyVaultNamespace).kmsProviders(kmsProviders).build()
val clientEncryption = ClientEncryptions.create(clientEncryptionSettings)
val dataKey = clientEncryption.createDataKey("local", DataKeyOptions()).headResult()
val base64DataKeyId = Base64.getEncoder.encodeToString(dataKey.getData)
val dbName = "test"
val collName = "coll"
val autoEncryptionSettings = AutoEncryptionSettings.builder()
.keyVaultNamespace(keyVaultNamespace)
.kmsProviders(kmsProviders)
.schemaMap(Map(s"$dbName.$collName" -> BsonDocument(
s"""{
properties: {
encryptedField: {
encrypt: {
keyId: [{
"$$binary": {
"base64": "$base64DataKeyId",
"subType": "04"
}
}],
bsonType: "string",
algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
}
}
},
bsonType: "object"
}""")).asJava)
.build()

Explicit encryption and decryption is a MongoDB community feature and does not use the mongocryptd process. Explicit encryption is provided by the ClientEncryption class.

The code example is from the ClientSideEncryptionExplicitEncryptionAndDecryptionTour.scala file in the driver source code GitHub repository.

// This would have to be the same master key as was used to create the encryption key
val localMasterKey = new Array[Byte](96)
new SecureRandom().nextBytes(localMasterKey)
val kmsProviders = Map("local" -> Map[String, AnyRef]("key" -> localMasterKey).asJava).asJava
val keyVaultNamespace = new MongoNamespace("encryption.testKeyVault")
val clientSettings = MongoClientSettings.builder().build()
val mongoClient = MongoClient(clientSettings)
// Set up the key vault for this example
val keyVaultCollection = mongoClient.getDatabase(keyVaultNamespace.getDatabaseName)
.getCollection(keyVaultNamespace.getCollectionName)
keyVaultCollection.drop().headResult()
// Ensure that two data keys cannot share the same keyAltName.
keyVaultCollection.createIndex(Indexes.ascending("keyAltNames"), new IndexOptions().unique(true)
.partialFilterExpression(Filters.exists("keyAltNames")))
val collection = mongoClient.getDatabase("test").getCollection("coll")
collection.drop().headResult()
// Create the ClientEncryption instance
val clientEncryptionSettings = ClientEncryptionSettings
.builder()
.keyVaultMongoClientSettings(
MongoClientSettings.builder()
.applyConnectionString(ConnectionString("mongodb://localhost")).build()
)
.keyVaultNamespace(keyVaultNamespace.getFullName)
.kmsProviders(kmsProviders)
.build()
val clientEncryption = ClientEncryptions.create(clientEncryptionSettings)
val dataKeyId = clientEncryption.createDataKey("local", DataKeyOptions()).headResult()
// Explicitly encrypt a field
val encryptedFieldValue = clientEncryption.encrypt(BsonString("123456789"),
EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId))
.headResult()
collection.insertOne(Document("encryptedField" -> encryptedFieldValue)).headResult()
val doc = collection.find.first().headResult()
println(doc.toJson())
// Explicitly decrypt the field
println(
clientEncryption.decrypt(doc.get[BsonBinary]("encryptedField").get).headResult()
)

Although automatic encryption requires MongoDB 4.2 enterprise or a MongoDB 4.2 Atlas cluster, automatic decryption is supported for all users. To configure automatic decryption without automatic encryption, set bypassAutoEncryption(true).

The code example is from the ClientSideEncryptionExplicitEncryptionOnlyTour.scala file in the driver source code GitHub repository.

...
val clientSettings = MongoClientSettings.builder()
.autoEncryptionSettings(AutoEncryptionSettings.builder()
.keyVaultNamespace(keyVaultNamespace.getFullName)
.kmsProviders(kmsProviders)
.bypassAutoEncryption(true)
.build())
.build()
val mongoClient = MongoClient(clientSettings)
...
// Explicitly encrypt a field
val encryptedFieldValue = clientEncryption.encrypt(BsonString("123456789"),
EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId))
.headResult()
collection.insertOne(Document("encryptedField" -> encryptedFieldValue)).headResult()
val doc = collection.find.first().headResult()
println(doc.toJson())

Back

Read Operations

Next

Write Operations