The Field Level Encryption library enables encryption and decryption of JSON fields, to support FIPS-140-2 compliance.
For a high-level overview of this feature, see concept-docs:encryption.adoc.
Packaging
The Couchbase Java SDK works together with the Java Couchbase Encryption library to provide support for encryption and decryption of JSON fields. This library makes use of the cryptographic algorithms available on your platform, and provides a framework for implementing your own crypto components.
The encryption code is packaged as an optional library and is subject to the Couchbase License and Enterprise Subscription License agreements. To use the encryption library, you have to explicitly include this dependency in your project configuration. Refer to the dependencies section. |
Requirements
-
Couchbase Java SDK version
3.0.5
or later. -
Java Couchbase Encryption version
3.0.0
or later.
Maven Coordinates
<dependency>
<groupId>com.couchbase.client</groupId>
<artifactId>couchbase-encryption</artifactId>
<version>${version}</version>
</dependency>
See the GitHub repository tags for the latest version.
Optional Dependencies
To reduce the footprint of this library, some of its dependencies are optional. Using certain features requires adding additional dependencies to your project.
HashiCorp Vault Transit integration requires Spring Vault:
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>2.3.2</version>
</dependency>
Configuration
To enable Field-Level Encryption, supply a CryptoManager
when configuring the Java SDK’s ClusterEnvironment
.
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
Usage
Two modes of operation are available:
-
Transparent encryption/decryption during Jackson data binding.
-
Manual field editing using
JsonObjectCrypto
.
Data Binding Example
Sensitive fields of your data classes can be annotated with @Encrypted
.
Let’s use this class as an example:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
Now let’s create an employee record and save it to Couchbase:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
You can get the document as a JsonObject
to verify the field was encrypted:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
Because contentAsObject()
does not decrypt anything, the expected output is something like:
{
"encrypted$replicant": {
"alg": "AEAD_AES_256_CBC_HMAC_SHA512",
"ciphertext": "xwcxyUyZ.....",
"kid": "myKey"
}
}
Now let’s read the employee record using data binding:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
This prints true
.
Using a custom ObjectMapper
The code that enables encryption/decryption during data binding is packaged as a Jackson module called EncryptionModule
.
You can register this module with any Jackson ObjectMapper
.
You’ll need to do this if you want to supply your own customized ObjectMapper for the Java SDK to use when serializing documents. Here’s how to configure the cluster environment to use a custom JSON serializer backed by your own ObjectMapper with support for Field-Level Encryption:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
JsonObjectCrypto
If you need more control of which fields get decrypted, or if you prefer working with the Couchbase JsonObject
tree model,
you can use a JsonObjectCrypto
instance to read and write encrypted field values of a JsonObject
.
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
Reading Unencrypted Fields
From Java SDK 3.2.1, the @Encrypted
annotation can now be used to migrate an existing field from unencrypted to encrypted.
If you annotate a field with:
@Encrypted(migration = Encrypted.Migration.FROM_UNENCRYPTED)
then either encrypted or unencrypted values will be accepted during deserialization (without the latter causing error). See the API docs.
Encryption means that document fields have been authenticated. Enabling this feature bypasses that protection, and so this should only be used in strictly limited circumstances, such as the migration from an unencrypted to an encrypted field. |
Creating Encryption Keys
The AEAD_AES_256_CBC_HMAC_SHA512 algorithm included in this library uses encryption keys that are 64 bytes long.
Here’s an example that shows how to create a Java key store file containing a suitable encryption key:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
And here’s how to use that file to create a Keyring
for use with Couchbase Field-Level Encryption:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
Migrating from SDK 2
SDK 2 cannot read fields encrypted by SDK 3. |
It’s inadvisable to have both the old and new versions of your application active at the same time. The simplest way to migrate is to do an offline upgrade during a scheduled a maintenance window. For an online upgrade without downtime, consider a blue-green deployment.
SDK 3 requires additional configuration to read fields encrypted by SDK 2. The rest of this section describes how to configure Field-Level Encryption in SDK 3 for backwards compatibility with SDK 2.
Changing the field name prefix
In SDK 2, the default prefix for encrypted field names was __crypt_
.
This caused problems for Couchbase Sync Gateway, which does not like field names to begin with an underscore.
In SDK 3, the default prefix is encrypted$
.
For compatibility with SDK 2, you can configure the CryptoManager
to use the old __crypt_
prefix:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
Alternatively, you can rename the existing fields using a SQL++ (formerly N1QL) statement.
In SDK 2, only top-level fields could be encrypted. SDK 3 allows encrypting fields at any depth. If you decide to rename the existing fields, make sure to do so before writing any encrypted fields below the top level, otherwise it may be difficult to rename the nested fields using a generic SQL++ statement. |
Enabling decrypters for legacy algorithms
The encryption algorithms used by SDK 2 are deprecated, and are no longer used for encrypting new data.
To enable decrypting fields written by SDK 2, register the legacy decrypters when configuring the CryptoManager
:
Unresolved include directive in modules/howtos/pages/encrypting-using-sdk.adoc - include::example$EncryptingUsingSDK.java[]
The legacy decrypters require a mapping function. For AES, this function accepts an encryption key name and returns the corresponding signing key name. For RSA, this function accepts a public key name and returns the corresponding private key name. |