Encryption plugin library for Azure Cosmos DB Java SDK for Java

The Azure Cosmos Encryption Plugin is used for encrypting data with a user-provided key before saving into Cosmos DB and decrypting it when reading back from the database.

Source code | Package (Maven) | API reference documentation | Product documentation | Samples

Getting started

Include the package

<dependency>
  <groupId>com.azure</groupId>
  <artifactId>azure-cosmos-encryption</artifactId>
  <version>2.16.0</version>
</dependency>

Refer to maven central for previous releases

Refer to javadocs for more details on the package

Prerequisites

SLF4J is only needed if you plan to use logging, please also download an SLF4J binding which will link the SLF4J API with the logging implementation of your choice. See the SLF4J user manual for more information.

The SDK provides Reactor Core-based async APIs. You can read more about Reactor Core and Flux/Mono types here

Key concepts

The Azure Cosmos Encryption Plugin is used for encrypting data with a user-provided key before saving into Cosmos DB and decrypting it when reading back from the database. Underneath it uses Azure Cosmos DB Java SDK which provides client-side logical representation to access the Azure Cosmos DB SQL API. A Cosmos DB account contains zero or more databases, a database (DB) contains zero or more containers, and a container contains zero or more items. You may read more about databases, containers, and items here. A few important properties are defined at the level of the container, among them are provisioned throughput and partition key.

Examples

The following section provides several code snippets covering some of the most common Cosmos Encryption API tasks, including:

Create Cosmos Encryption Client

// Create a new CosmosEncryptionAsyncClient
CosmosAsyncClient cosmosAsyncClient = new CosmosClientBuilder()
    .endpoint("<YOUR ENDPOINT HERE>")
    .key("<YOUR KEY HERE>")
    .buildAsyncClient();
KeyEncryptionKeyClientBuilder keyEncryptionKeyClientBuilder = new KeyEncryptionKeyClientBuilder().credential(tokenCredentials);
CosmosEncryptionAsyncClient cosmosEncryptionAsyncClient =
    new CosmosEncryptionClientBuilder().cosmosAsyncClient(cosmosAsyncClient).keyEncryptionKeyResolver(
        keyEncryptionKeyClientBuilder).keyEncryptionKeyResolverName(CosmosEncryptionClientBuilder.KEY_RESOLVER_NAME_AZURE_KEY_VAULT).buildAsyncClient();

Create Cosmos Encryption Database

You need to first create a Database and using the cosmos encryption client created in the previous example, you can create a cosmos encryption database proxy object like this:

// This will create a database with the regular cosmosAsyncClient.
CosmosEncryptionAsyncDatabase cosmosEncryptionAsyncDatabase = cosmosEncryptionAsyncClient.getCosmosAsyncClient()
    .createDatabaseIfNotExists("<YOUR DATABASE NAME>")
    // TIP: Our APIs are Reactor Core based, so try to chain your calls
    .map(databaseResponse ->
        // Get a reference to the encryption database
        // This will create a cosmos encryption database proxy object.
        cosmosEncryptionAsyncClient.getCosmosEncryptionAsyncDatabase(databaseResponse.getProperties().getId()))
    .block(); // Blocking for demo purposes (avoid doing this in production unless you must)

Create Cosmos Encryption Container

You need to first create a Container with ClientEncryptionPolicy and using the cosmos encryption database object created in the previous example, you can create a cosmos encryption container proxy object like this:

//Create Client Encryption Key
EncryptionKeyWrapMetadata metadata = new EncryptionKeyWrapMetadata(this.cosmosEncryptionAsyncClient.getKeyEncryptionKeyResolverName(), "key", "tempmetadata", EncryptionAlgorithm.RSA_OAEP.toString());
CosmosEncryptionAsyncContainer cosmosEncryptionAsyncContainer = cosmosEncryptionAsyncDatabase
    .createClientEncryptionKey("key", CosmosEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.getName(), metadata)
    // TIP: Our APIs are Reactor Core based, so try to chain your calls
    .then(Mono.defer(() -> {
        //Create Encryption Container
        ClientEncryptionIncludedPath includedPath = new ClientEncryptionIncludedPath();
        includedPath.setClientEncryptionKeyId("key");
        includedPath.setPath("/sensitiveString");
        includedPath.setEncryptionType(CosmosEncryptionType.DETERMINISTIC.toString());
        includedPath.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.getName());

        List<ClientEncryptionIncludedPath> paths = new ArrayList<>();
        paths.add(includedPath);
        ClientEncryptionPolicy clientEncryptionPolicy = new ClientEncryptionPolicy(paths);
        CosmosContainerProperties properties = new CosmosContainerProperties("<YOUR CONTAINER NAME>", "/mypk");
        properties.setClientEncryptionPolicy(clientEncryptionPolicy);
        return cosmosEncryptionAsyncDatabase.getCosmosAsyncDatabase().createContainer(properties);
    }))
    .map(containerResponse ->
        // Create a reference to the encryption container
        // This will create a cosmos encryption container proxy object.
        cosmosEncryptionAsyncDatabase.getCosmosEncryptionAsyncContainer(containerResponse.getProperties().getId()))
    .block(); // Blocking for demo purposes (avoid doing this in production unless you must)

CRUD operation on Items

// Create an item
Pojo pojo = new Pojo();
pojo.setSensitiveString("Sensitive Information need to be encrypted");
cosmosEncryptionAsyncContainer.createItem(pojo)
    .flatMap(response -> {
        System.out.println("Created item: " + response.getItem());
        // Read that item 👓
        return cosmosEncryptionAsyncContainer.readItem(response.getItem().getId(),
            new PartitionKey(response.getItem().getId()),
            Pojo.class);
    })
    .flatMap(response -> {
        System.out.println("Read item: " + response.getItem());
        // Replace that item 🔁
        Pojo p = response.getItem();
        pojo.setSensitiveString("New Sensitive Information");
        return cosmosEncryptionAsyncContainer.replaceItem(p, response.getItem().getId(),
            new PartitionKey(response.getItem().getId()));
    })
    // delete that item 💣
    .flatMap(response -> cosmosEncryptionAsyncContainer.deleteItem(response.getItem().getId(),
        new PartitionKey(response.getItem().getId())))
    .subscribe();

We have a get started sample app available here.

Troubleshooting

General

Azure Cosmos DB is a fast and flexible distributed database that scales seamlessly with guaranteed latency and throughput. You do not have to make major architecture changes or write complex code to scale your database with Azure Cosmos DB. Scaling up and down is as easy as making a single API call or SDK method call. However, because Azure Cosmos DB is accessed via network calls there are client-side optimizations you can make to achieve peak performance when using Azure Cosmos DB Java SDK v4.

  • Performance guide covers these client-side optimizations.

  • Troubleshooting Guide covers common issues, workarounds, diagnostic steps, and tools when you use Azure Cosmos DB Java SDK v4 with Azure Cosmos DB SQL API accounts.

Enable Client Logging

Azure Cosmos DB Java SDK v4 uses SLF4j as the logging facade that supports logging into popular logging frameworks such as log4j and logback.

For example, if you want to use log4j as the logging framework, add the following libs in your Java classpath.

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>${slf4j.version}</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>${log4j.version}</version>
</dependency>

Also, add a log4j config.

# this is a sample log4j configuration

# Set root logger level to INFO and its only appender to A1.
log4j.rootLogger=INFO, A1

log4j.category.com.azure.cosmos=INFO
#log4j.category.io.netty=OFF
#log4j.category.io.projectreactor=OFF
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n

Next steps

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Impressions