diff --git a/src/examples/README.md b/src/examples/README.md
index 19201887b..c8ae480b7 100644
--- a/src/examples/README.md
+++ b/src/examples/README.md
@@ -54,6 +54,12 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
* How to combine AWS KMS with an offline escrow key
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/multi/AwsKmsWithEscrow.java)
* [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/multi/AwsKmsWithEscrow.java)
+* How to reuse data keys across multiple messages
+ * [with the caching cryptographic materials manager](./java/com/amazonaws/crypto/examples/cryptomaterialsmanager/caching/SimpleCache.java)
+* How to restrict algorithm suites
+ * [with a custom cryptographic materials manager](./java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/AlgorithmSuiteEnforcement.java)
+* How to require encryption context fields
+ * [with a custom cryptographic materials manager](./java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/RequiringEncryptionContextFields.java)
### Keyrings
diff --git a/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/caching/SimpleCache.java b/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/caching/SimpleCache.java
new file mode 100644
index 000000000..e7038d6c8
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/caching/SimpleCache.java
@@ -0,0 +1,122 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.amazonaws.crypto.examples.cryptomaterialsmanager.caching;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.DecryptRequest;
+import com.amazonaws.encryptionsdk.EncryptRequest;
+import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The default cryptographic materials manager (CMM)
+ * creates new encryption and decryption materials
+ * on every call.
+ * This means every encrypted message is protected by a unique data key,
+ * but it also means that you need to interact with your key management system
+ * in order to process any message.
+ * If this causes performance, operations, or cost issues for you,
+ * you might benefit from data key caching.
+ *
+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/data-key-caching.html
+ *
+ * This example shows how to configure the caching CMM
+ * to reuse data keys across multiple encrypted messages.
+ *
+ * In this example, we use an AWS KMS customer master key (CMK),
+ * but you can use other key management options with the AWS Encryption SDK.
+ * For examples that demonstrate how to use other key management configurations,
+ * see the 'keyring' and 'masterkeyprovider' directories.
+ *
+ * In this example, we use the one-step encrypt and decrypt APIs.
+ */
+public class SimpleCache {
+
+ /**
+ * Demonstrate an encrypt/decrypt cycle using the caching cryptographic materials manager.
+ *
+ * @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys
+ * @param sourcePlaintext Plaintext to encrypt
+ */
+ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) {
+ // Instantiate the AWS Encryption SDK.
+ final AwsCrypto awsEncryptionSdk = new AwsCrypto();
+
+ // Prepare your encryption context.
+ // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
+ final Map encryptionContext = new HashMap<>();
+ encryptionContext.put("encryption", "context");
+ encryptionContext.put("is not", "secret");
+ encryptionContext.put("but adds", "useful metadata");
+ encryptionContext.put("that can help you", "be confident that");
+ encryptionContext.put("the data you are handling", "is what you think it is");
+
+ // Create the keyring that determines how your data keys are protected.
+ final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
+
+ // Create the caching cryptographic materials manager using your keyring.
+ final CryptoMaterialsManager cmm = CachingCryptoMaterialsManager.newBuilder()
+ .withKeyring(keyring)
+ // The cache is where the caching CMM stores the materials.
+ //
+ // LocalCryptoMaterialsCache gives you a local, in-memory, cache.
+ .withCache(new LocalCryptoMaterialsCache(100))
+ // Max Age determines how long the caching CMM will reuse materials.
+ //
+ // This example uses two minutes.
+ // In production, always chose as small a value as possible
+ // that works for your requirements.
+ .withMaxAge(2, TimeUnit.MINUTES)
+ // Message Use Limit determines how many messages
+ // the caching CMM will protect with the same materials.
+ //
+ // In production, always choose as small a value as possible
+ // that works for your requirements.
+ .withMessageUseLimit(10)
+ .build();
+
+ // Encrypt your plaintext data.
+ final AwsCryptoResult encryptResult = awsEncryptionSdk.encrypt(
+ EncryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .encryptionContext(encryptionContext)
+ .plaintext(sourcePlaintext).build());
+ final byte[] ciphertext = encryptResult.getResult();
+
+ // Demonstrate that the ciphertext and plaintext are different.
+ assert !Arrays.equals(ciphertext, sourcePlaintext);
+
+ // Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
+ //
+ // You do not need to specify the encryption context on decrypt because
+ // the header of the encrypted message includes the encryption context.
+ final AwsCryptoResult decryptResult = awsEncryptionSdk.decrypt(
+ DecryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .ciphertext(ciphertext).build());
+ final byte[] decrypted = decryptResult.getResult();
+
+ // Demonstrate that the decrypted plaintext is identical to the original plaintext.
+ assert Arrays.equals(decrypted, sourcePlaintext);
+
+ // Verify that the encryption context used in the decrypt operation includes
+ // the encryption context that you specified when encrypting.
+ // The AWS Encryption SDK can add pairs, so don't require an exact match.
+ //
+ // In production, always use a meaningful encryption context.
+ encryptionContext.forEach((k, v) -> {
+ assert v.equals(decryptResult.getEncryptionContext().get(k));
+ });
+ }
+}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/AlgorithmSuiteEnforcement.java b/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/AlgorithmSuiteEnforcement.java
new file mode 100644
index 000000000..45328b15a
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/AlgorithmSuiteEnforcement.java
@@ -0,0 +1,183 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.amazonaws.crypto.examples.cryptomaterialsmanager.custom;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.CryptoAlgorithm;
+import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.DecryptRequest;
+import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.EncryptRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
+import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
+import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The AWS Encryption SDK supports several different algorithm suites
+ * that offer different security properties.
+ *
+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html
+ *
+ * By default, the AWS Encryption SDK will let you use any of these,
+ * but you might want to restrict that further.
+ *
+ * We recommend that you use the default algorithm suite,
+ * which uses AES-GCM with 256-bit keys, HKDF, and ECDSA message signing.
+ * If your readers and writers have the same permissions,
+ * you might want to omit the message signature for faster operation.
+ * For more information about choosing a signed or unsigned algorithm suite,
+ * see the AWS Encryption SDK developer guide:
+ *
+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html#other-algorithms
+ *
+ * This example shows how you can make a custom cryptographic materials manager (CMM)
+ * that only allows encrypt requests that either specify one of these two algorithm suites
+ * or do not specify an algorithm suite, in which case the default CMM uses the default algorithm suite.
+ */
+public class AlgorithmSuiteEnforcement {
+
+ /**
+ * Indicates that an unsupported algorithm suite was requested.
+ */
+ static class UnapprovedAlgorithmSuiteException extends RuntimeException {
+ UnapprovedAlgorithmSuiteException() {
+ super("Unapproved algorithm suite requested!");
+ }
+ }
+
+ /**
+ * Only allow encryption requests for approved algorithm suites.
+ */
+ static class RequireApprovedAlgorithmSuitesCryptoMaterialsManager implements CryptoMaterialsManager {
+
+ private final CryptoMaterialsManager cmm;
+ private final Set ALLOWED_ALGORITHM_SUITES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, // the default algorithm suite
+ CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256))); // the recommended unsigned algorithm suite
+
+ /**
+ * Set up the inner cryptographic materials manager using the provided keyring.
+ *
+ * @param keyring Keyring to use in the inner cryptographic materials manager
+ */
+ RequireApprovedAlgorithmSuitesCryptoMaterialsManager(Keyring keyring) {
+ // Wrap the provided keyring in the default cryptographic materials manager (CMM).
+ //
+ // This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
+ // do if you provide a keyring instead of a CMM.
+ cmm = new DefaultCryptoMaterialsManager(keyring);
+ }
+
+ /**
+ * Block any requests that include an unapproved algorithm suite.
+ */
+ @Override
+ public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
+ if (request.getRequestedAlgorithm() != null && !ALLOWED_ALGORITHM_SUITES.contains(request.getRequestedAlgorithm())) {
+ throw new UnapprovedAlgorithmSuiteException();
+ }
+
+ return cmm.getMaterialsForEncrypt(request);
+ }
+
+ /**
+ * Be more permissive on decrypt and just pass through.
+ */
+ @Override
+ public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
+ return cmm.decryptMaterials(request);
+ }
+ }
+
+
+ /**
+ * Demonstrate an encrypt/decrypt cycle using a custom cryptographic materials manager that filters requests.
+ *
+ * @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys
+ * @param sourcePlaintext Plaintext to encrypt
+ */
+ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) {
+ // Instantiate the AWS Encryption SDK.
+ final AwsCrypto awsEncryptionSdk = new AwsCrypto();
+
+ // Prepare your encryption context.
+ // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
+ final Map encryptionContext = new HashMap<>();
+ encryptionContext.put("encryption", "context");
+ encryptionContext.put("is not", "secret");
+ encryptionContext.put("but adds", "useful metadata");
+ encryptionContext.put("that can help you", "be confident that");
+ encryptionContext.put("the data you are handling", "is what you think it is");
+
+ // Create the keyring that determines how your data keys are protected.
+ final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
+
+ // Create the algorithm suite restricting cryptographic materials manager using your keyring.
+ final CryptoMaterialsManager cmm = new RequireApprovedAlgorithmSuitesCryptoMaterialsManager(keyring);
+
+ // Demonstrate that the algorithm suite restricting CMM will not let you use an unapproved algorithm suite.
+ awsEncryptionSdk.setEncryptionAlgorithm(CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF);
+
+ try {
+ awsEncryptionSdk.encrypt(
+ EncryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .encryptionContext(encryptionContext)
+ .plaintext(sourcePlaintext).build());
+ // The algorithm suite restricting CMM keeps this from happening.
+ throw new AssertionError("The algorithm suite restricting CMM does not let this happen!");
+ } catch (UnapprovedAlgorithmSuiteException ex) {
+ // You asked for an unapproved algorithm suite.
+ // Reaching this point means everything is working as expected.
+ }
+
+ // Set an approved algorithm suite.
+ awsEncryptionSdk.setEncryptionAlgorithm(CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384);
+
+ // Encrypt your plaintext data.
+ final AwsCryptoResult encryptResult = awsEncryptionSdk.encrypt(
+ EncryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .encryptionContext(encryptionContext)
+ .plaintext(sourcePlaintext).build());
+ final byte[] ciphertext = encryptResult.getResult();
+
+ // Demonstrate that the ciphertext and plaintext are different.
+ assert !Arrays.equals(ciphertext, sourcePlaintext);
+
+ // Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
+ //
+ // You do not need to specify the encryption context on decrypt because
+ // the header of the encrypted message includes the encryption context.
+ final AwsCryptoResult decryptResult = awsEncryptionSdk.decrypt(
+ DecryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .ciphertext(ciphertext).build());
+ final byte[] decrypted = decryptResult.getResult();
+
+ // Demonstrate that the decrypted plaintext is identical to the original plaintext.
+ assert Arrays.equals(decrypted, sourcePlaintext);
+
+ // Verify that the encryption context used in the decrypt operation includes
+ // the encryption context that you specified when encrypting.
+ // The AWS Encryption SDK can add pairs, so don't require an exact match.
+ //
+ // In production, always use a meaningful encryption context.
+ encryptionContext.forEach((k, v) -> {
+ assert v.equals(decryptResult.getEncryptionContext().get(k));
+ });
+ }
+}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/RequiringEncryptionContextFields.java b/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/RequiringEncryptionContextFields.java
new file mode 100644
index 000000000..c03a2f550
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/RequiringEncryptionContextFields.java
@@ -0,0 +1,197 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.amazonaws.crypto.examples.cryptomaterialsmanager.custom;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.DecryptRequest;
+import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.EncryptRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
+import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
+import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Encryption context is a powerful tool for access and audit controls
+ * because it lets you tie *non-secret* metadata about a plaintext value to the encrypted message.
+ * Within the AWS Encryption SDK,
+ * you can use cryptographic materials managers to analyse the encryption context
+ * to provide logical controls and additional metadata.
+ *
+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
+ *
+ * If you are using the AWS Encryption SDK with AWS KMS,
+ * you can use AWS KMS to provide additional powerful controls using the encryption context.
+ * For more information on that, see the KMS developer guide:
+ *
+ * https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context
+ *
+ * This example shows how to create a custom cryptographic materials manager (CMM)
+ * that requires a particular field in the encryption context.
+ */
+public class RequiringEncryptionContextFields {
+
+ /**
+ * Indicates that an encryption context was found that lacked a classification identifier.
+ */
+ static class MissingClassificationException extends RuntimeException {
+ MissingClassificationException() {
+ super("Encryption context does not contain classification!");
+ }
+ }
+
+ /**
+ * Only allow requests when the encryption context contains a classification identifier.
+ */
+ static class ClassificationRequiringCryptoMaterialsManager implements CryptoMaterialsManager {
+
+ private final CryptoMaterialsManager cmm;
+ private static final String CLASSIFICATION_KEY = "classification";
+
+ ClassificationRequiringCryptoMaterialsManager(Keyring keyring) {
+ // Wrap the provided keyring in the default cryptographic materials manager (CMM).
+ //
+ // This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
+ // do if you provide a keyring instead of a CMM.
+ cmm = new DefaultCryptoMaterialsManager(keyring);
+ }
+
+ /**
+ * Block any requests that do not contain a classification identifier in the encryption context.
+ */
+ @Override
+ public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
+ if (!request.getContext().containsKey(CLASSIFICATION_KEY)) {
+ throw new MissingClassificationException();
+ }
+
+ return cmm.getMaterialsForEncrypt(request);
+ }
+
+ /**
+ * Block any requests that do not contain a classification identifier in the encryption context.
+ */
+ @Override
+ public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
+ if (!request.getEncryptionContext().containsKey(CLASSIFICATION_KEY)) {
+ throw new MissingClassificationException();
+ }
+
+ return cmm.decryptMaterials(request);
+ }
+ }
+
+
+ /**
+ * Demonstrate an encrypt/decrypt cycle using a custom cryptographic materials manager that filters requests.
+ *
+ * @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys
+ * @param sourcePlaintext Plaintext to encrypt
+ */
+ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) {
+ // Instantiate the AWS Encryption SDK.
+ final AwsCrypto awsEncryptionSdk = new AwsCrypto();
+
+ // Prepare your encryption context.
+ // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
+ final Map encryptionContext = new HashMap<>();
+ encryptionContext.put("encryption", "context");
+ encryptionContext.put("is not", "secret");
+ encryptionContext.put("but adds", "useful metadata");
+ encryptionContext.put("that can help you", "be confident that");
+ encryptionContext.put("the data you are handling", "is what you think it is");
+
+ // Create the keyring that determines how your data keys are protected.
+ final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
+
+ // Create the classification requiring cryptographic materials manager using your keyring.
+ final CryptoMaterialsManager cmm = new ClassificationRequiringCryptoMaterialsManager(keyring);
+
+ // Demonstrate that the classification requiring CMM will not let you encrypt without a classification identifier.
+ try {
+ awsEncryptionSdk.encrypt(
+ EncryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .encryptionContext(encryptionContext)
+ .plaintext(sourcePlaintext).build());
+ // The classification requiring CMM keeps this from happening.
+ throw new AssertionError("The classification requiring CMM does not let this happen!");
+ } catch (MissingClassificationException ex) {
+ // Your encryption context did not contain a classification identifier.
+ // Reaching this point means everything is working as expected.
+ }
+
+ // Create an encryption context with the required classification key.
+ final Map classifiedEncryptionContext = new HashMap<>(encryptionContext);
+ classifiedEncryptionContext.put("classification", "secret");
+
+ // Encrypt your plaintext data.
+ final AwsCryptoResult encryptResult = awsEncryptionSdk.encrypt(
+ EncryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .encryptionContext(classifiedEncryptionContext)
+ .plaintext(sourcePlaintext).build());
+ final byte[] ciphertext = encryptResult.getResult();
+
+ // Demonstrate that the ciphertext and plaintext are different.
+ assert !Arrays.equals(ciphertext, sourcePlaintext);
+
+ // Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
+ //
+ // You do not need to specify the encryption context on decrypt because
+ // the header of the encrypted message includes the encryption context.
+ final AwsCryptoResult decryptResult = awsEncryptionSdk.decrypt(
+ DecryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .ciphertext(ciphertext).build());
+ final byte[] decrypted = decryptResult.getResult();
+
+ // Demonstrate that the decrypted plaintext is identical to the original plaintext.
+ assert Arrays.equals(decrypted, sourcePlaintext);
+
+ // Verify that the encryption context used in the decrypt operation includes
+ // the encryption context that you specified when encrypting.
+ // The AWS Encryption SDK can add pairs, so don't require an exact match.
+ //
+ // In production, always use a meaningful encryption context.
+ encryptionContext.forEach((k, v) -> {
+ assert v.equals(decryptResult.getEncryptionContext().get(k));
+ });
+
+ // Now demonstrate the decrypt path of the classification requiring cryptographic materials manager.
+
+ // Encrypt your plaintext using the keyring and do not include a classification identifier.
+ final AwsCryptoResult unclassifiedEncryptResult = awsEncryptionSdk.encrypt(
+ EncryptRequest.builder()
+ .keyring(keyring)
+ .encryptionContext(encryptionContext)
+ .plaintext(sourcePlaintext).build());
+ final byte[] unclassifiedCiphertext = unclassifiedEncryptResult.getResult();
+
+ assert !unclassifiedEncryptResult.getEncryptionContext().containsKey("classification");
+
+ // Demonstrate that the classification requiring CMM
+ // will not let you decrypt messages without classification identifiers.
+ try {
+ awsEncryptionSdk.decrypt(
+ DecryptRequest.builder()
+ .cryptoMaterialsManager(cmm)
+ .ciphertext(unclassifiedCiphertext).build());
+ // The classification requiring CMM keeps this from happening.
+ throw new AssertionError("The classification requiring CMM does not let this happen!");
+ } catch (MissingClassificationException ex) {
+ // Your encryption context did not contain a classification identifier.
+ // Reaching this point means everything is working as expected.
+ }
+ }
+}