- * Encrypts and then decrypts data using an AWS KMS customer master key.
+ * Encrypts and then decrypts data using an AWS Key Management Service (AWS KMS) customer master key.
*
*
* Arguments:
@@ -49,9 +49,9 @@ static void encryptAndDecrypt(final AwsKmsCmkId keyArn) {
// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();
- // 2. Instantiate an AWS KMS Keyring, supplying the key ARN as the generator for generating a data key. While
- // using a key ARN is a best practice, for encryption operations it is also acceptable to use a CMK alias or
- // an alias ARN.
+ // 2. Instantiate a KMS keyring. Supply the key ARN for the generator key
+ // that generates a data key. While using a key ARN is a best practice,
+ // for encryption operations you can also use an alias name or alias ARN.
final Keyring keyring = StandardKeyrings.awsKms(keyArn);
// 3. Create an encryption context
@@ -71,27 +71,25 @@ static void encryptAndDecrypt(final AwsKmsCmkId keyArn) {
.plaintext(EXAMPLE_DATA).build());
final byte[] ciphertext = encryptResult.getResult();
- // 5. Decrypt the data. The same keyring may be used to encrypt and decrypt, but for decryption
+ // 5. Decrypt the data. You can use the same keyring to encrypt and decrypt, but for decryption
// the key IDs must be in the key ARN format.
final AwsCryptoResult decryptResult = crypto.decrypt(
DecryptRequest.builder()
.keyring(keyring)
.ciphertext(ciphertext).build());
- // 6. Before verifying the plaintext, inspect the Keyring Trace to verify that the CMK used
- // to decrypt the encrypted data key was the CMK in the encryption keyring.
+ // 6. To verify the CMK that was actually used in the decrypt operation, inspect the keyring trace.
if(!decryptResult.getKeyringTrace().getEntries().get(0).getKeyName().equals(keyArn.toString())) {
throw new IllegalStateException("Wrong key ID!");
}
- // 7. Also, verify that the encryption context in the result contains the
- // encryption context supplied to the encryptData method. Because the
- // SDK can add values to the encryption context, don't require that
- // the entire context matches.
- if (!encryptionContext.entrySet().stream()
- .allMatch(e -> e.getValue().equals(decryptResult.getEncryptionContext().get(e.getKey())))) {
- throw new IllegalStateException("Wrong Encryption Context!");
- }
+ // 7. To verify that the encryption context used to decrypt the data was the encryption context you expected,
+ // examine the encryption context in the result. This helps to ensure that you decrypted the ciphertext that
+ // you intended.
+ //
+ // When verifying, test that your expected encryption context is a subset of the actual encryption context,
+ // not an exact match. The Encryption SDK adds the signing key to the encryption context when appropriate.
+ assert decryptResult.getEncryptionContext().get("ExampleContextKey").equals("ExampleContextValue");
// 8. Verify that the decrypted plaintext matches the original plaintext
assert Arrays.equals(decryptResult.getResult(), EXAMPLE_DATA);
diff --git a/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java b/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java
index ef4f17dca..32be46198 100644
--- a/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java
+++ b/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java
@@ -84,7 +84,7 @@ private static byte[] standardEncrypt(final AwsKmsCmkId kmsArn, final PublicKey
// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();
- // 2. Instantiate an AWS KMS Keyring, supplying the keyArn as the generator for generating a data key.
+ // 2. Instantiate a KMS keyring, supplying the keyArn as the generator for generating a data key.
final Keyring kmsKeyring = StandardKeyrings.awsKms(kmsArn);
// 3. Instantiate a RawRsaKeyring
@@ -115,7 +115,7 @@ private static byte[] standardDecrypt(final AwsKmsCmkId kmsArn, final byte[] cip
// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();
- // 2. Instantiate an AWS KMS Keyring, supplying the keyArn as the generator for generating a data key.
+ // 2. Instantiate a KMS keyring, supplying the keyArn as the generator for generating a data key.
final Keyring kmsKeyring = StandardKeyrings.awsKms(kmsArn);
// 4. Decrypt the data with the keyring.
diff --git a/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java b/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java
index f7b8d3436..94cd38853 100644
--- a/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java
+++ b/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java
@@ -63,9 +63,9 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina
// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();
- // 2. Retrieve an encryption key. In this example, we generate a random key.
- // In practice, you would get a key from an existing store
- final SecretKey cryptoKey = retrieveEncryptionKey();
+ // 2. Get an encryption key. In this example, we generate a random key.
+ // In practice, you would get a key from an existing key store.
+ final SecretKey cryptoKey = generateEncryptKey();
// 3. Instantiate a RawAesKeyring using the random key
final Keyring keyring = StandardKeyrings.rawAesBuilder()
@@ -104,11 +104,12 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina
.keyring(keyring)
.inputStream(new FileInputStream(encryptedFile)).build())) {
- // 8. Verify that the encryption context in the result contains the
- // encryption context supplied to the createEncryptingStream method.
- if (!"FileStreaming".equals(decryptingStream.getAwsCryptoResult().getEncryptionContext().get("Example"))) {
- throw new IllegalStateException("Bad encryption context");
- }
+ // 8. Verify that the encryption context that was used to decrypt the data is the one that you expect.
+ // This helps to ensure that the ciphertext that you decrypted was the one that you intended.
+ //
+ // When verifying, test that your expected encryption context is a subset of the actual encryption context,
+ // not an exact match. When appropriate, the Encryption SDK adds the signing key to the encryption context.
+ assert "FileStreaming".equals(decryptingStream.getAwsCryptoResult().getEncryptionContext().get("Example"));
// 9. Copy the plaintext data to a file
try (FileOutputStream out = new FileOutputStream(decryptedFile)) {
@@ -122,9 +123,9 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina
/**
* In practice, this key would be saved in a secure location.
- * For this demo, we generate a new random key for each operation.
+ * In this example, we generate a new random key for each operation.
*/
- private static SecretKey retrieveEncryptionKey() {
+ private static SecretKey generateEncryptKey() {
SecureRandom rnd = new SecureRandom();
byte[] rawKey = new byte[16]; // 128 bits
rnd.nextBytes(rawKey);
diff --git a/src/examples/java/com/amazonaws/crypto/examples/RawAesKeyringExample.java b/src/examples/java/com/amazonaws/crypto/examples/RawAesKeyringExample.java
new file mode 100644
index 000000000..9af19ca54
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/RawAesKeyringExample.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
+ * in compliance with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.amazonaws.crypto.examples;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.DecryptRequest;
+import com.amazonaws.encryptionsdk.EncryptRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ *
+ * Encrypts and then decrypts data using the Raw AES keyring.
+ */
+public class RawAesKeyringExample {
+
+ private static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8);
+
+ public static void main(final String[] args) {
+ encryptAndDecrypt();
+ }
+
+ static void encryptAndDecrypt() {
+ // 1. Instantiate the SDK
+ final AwsCrypto crypto = new AwsCrypto();
+
+ // 2. Get an encryption key. In this example, we generate a random key.
+ // In practice, you would get a key from an existing key store
+ final SecretKey cryptoKey = generateEncryptKey();
+
+ // 3. Instantiate a Raw AES keyring with the encryption key
+ final Keyring keyring = StandardKeyrings.rawAesBuilder()
+ .keyNamespace("ExampleKeyNamespace")
+ .keyName("ExampleKeyName")
+ .wrappingKey(cryptoKey).build();
+
+ // 4. Create an encryption context
+ //
+ // Most encrypted data should have an associated encryption context
+ // to protect integrity. This sample uses placeholder values.
+ //
+ // For more information see:
+ // blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
+ final Map encryptionContext = Collections.singletonMap("ExampleContextKey", "ExampleContextValue");
+
+ // 5. Encrypt the data with the keyring and encryption context
+ final AwsCryptoResult encryptResult = crypto.encrypt(EncryptRequest.builder()
+ .keyring(keyring)
+ .encryptionContext(encryptionContext)
+ .plaintext(EXAMPLE_DATA).build());
+ final byte[] ciphertext = encryptResult.getResult();
+
+ // 6. Decrypt the data
+ final AwsCryptoResult decryptResult = crypto.decrypt(DecryptRequest.builder()
+ .keyring(keyring)
+ .ciphertext(ciphertext).build());
+
+ // 7. Verify that the encryption context that was used to decrypt the data is the one that you expect.
+ // This helps to ensure that the ciphertext that you decrypted was the one that you intended.
+ //
+ // When verifying, test that your expected encryption context is a subset of the actual encryption context,
+ // not an exact match. When appropriate, the Encryption SDK adds the signing key to the encryption context.
+ assert decryptResult.getEncryptionContext().get("ExampleContextKey").equals("ExampleContextValue");
+
+ // 8. Verify that the decrypted plaintext matches the original plaintext
+ assert Arrays.equals(decryptResult.getResult(), EXAMPLE_DATA);
+ }
+
+ /**
+ * In practice, this key would be saved in a secure location.
+ * For this demo, we generate a new random key for each operation.
+ */
+ private static SecretKey generateEncryptKey() {
+ SecureRandom rnd = new SecureRandom();
+ byte[] rawKey = new byte[16]; // 128 bits
+ rnd.nextBytes(rawKey);
+ return new SecretKeySpec(rawKey, "AES");
+ }
+}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExample.java b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExample.java
new file mode 100644
index 000000000..a5f3ba488
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExample.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
+ * in compliance with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.amazonaws.crypto.examples;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.DecryptRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+
+import java.security.KeyPair;
+
+/**
+ *
+ * Decrypts data using the Raw RSA keyring.
+ */
+public class RawRsaKeyringDecryptExample {
+
+ public static byte[] decrypt(byte[] ciphertext, KeyPair keyPair) {
+ // 1. Instantiate the SDK
+ final AwsCrypto crypto = new AwsCrypto();
+
+ // 2. Instantiate a Raw RSA keyring with the private key
+ final Keyring keyring = StandardKeyrings.rawRsaBuilder()
+ .keyNamespace("ExampleKeyNamespace")
+ .keyName("ExampleKeyName")
+ .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding")
+ .privateKey(keyPair.getPrivate()).build();
+
+ // 3. Decrypt the ciphertext with the keyring
+ final AwsCryptoResult decryptResult = crypto.decrypt(DecryptRequest.builder()
+ .keyring(keyring)
+ .ciphertext(ciphertext).build());
+
+ // 4. Verify that the encryption context that was used to decrypt the data is the one that you expect.
+ // This helps to ensure that the ciphertext that you decrypted was the one that you intended.
+ //
+ // When verifying, test that your expected encryption context is a subset of the actual encryption context,
+ // not an exact match. When appropriate, the Encryption SDK adds the signing key to the encryption context.
+ assert decryptResult.getEncryptionContext().get("ExampleContextKey").equals("ExampleContextValue");
+
+ // 5. Return the decrypted byte array result
+ return decryptResult.getResult();
+ }
+}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExample.java b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExample.java
new file mode 100644
index 000000000..480ac0092
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExample.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
+ * in compliance with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.amazonaws.crypto.examples;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.EncryptRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+
+import java.nio.charset.StandardCharsets;
+import java.security.PublicKey;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Encrypts data using the Raw RSA keyring.
+ */
+public class RawRsaKeyringEncryptExample {
+
+ static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8);
+
+ public static byte[] encrypt(PublicKey publicKey) {
+ // 1. Instantiate the SDK
+ final AwsCrypto crypto = new AwsCrypto();
+
+ // 2. Instantiate a Raw RSA keyring with the public key
+ final Keyring keyring = StandardKeyrings.rawRsaBuilder()
+ .keyNamespace("ExampleKeyNamespace")
+ .keyName("ExampleKeyName")
+ .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding")
+ .publicKey(publicKey).build();
+
+ // 3. Create an encryption context
+ //
+ // Most encrypted data should have an associated encryption context
+ // to protect integrity. This sample uses placeholder values.
+ //
+ // For more information see:
+ // blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
+ final Map encryptionContext = Collections.singletonMap("ExampleContextKey", "ExampleContextValue");
+
+ // 4. Encrypt the data with the keyring and encryption context
+ final AwsCryptoResult encryptResult = crypto.encrypt(EncryptRequest.builder()
+ .keyring(keyring)
+ .encryptionContext(encryptionContext)
+ .plaintext(EXAMPLE_DATA).build());
+
+ // 5. Return the encrypted byte array result
+ return encryptResult.getResult();
+ }
+}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/LambdaDecryptAndWriteExample.java b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/LambdaDecryptAndWriteExample.java
new file mode 100644
index 000000000..6cc926d26
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/LambdaDecryptAndWriteExample.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
+ * in compliance with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.amazonaws.crypto.examples.datakeycaching;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+import com.amazonaws.encryptionsdk.DecryptRequest;
+import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
+import com.amazonaws.services.dynamodbv2.document.DynamoDB;
+import com.amazonaws.services.dynamodbv2.document.Item;
+import com.amazonaws.services.dynamodbv2.document.Table;
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
+import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord;
+import com.amazonaws.util.BinaryUtils;
+
+/**
+ * Decrypts all incoming Kinesis records and writes records to DynamoDB.
+ */
+public class LambdaDecryptAndWriteExample implements RequestHandler {
+ private static final long MAX_ENTRY_AGE_MILLISECONDS = 600000;
+ private static final int MAX_CACHE_ENTRIES = 100;
+ private final CachingCryptoMaterialsManager cachingMaterialsManager_;
+ private final AwsCrypto crypto_;
+ private final Table table_;
+
+ /**
+ * This code doesn't set the max bytes or max message security thresholds that are enforced
+ * only on data keys used for encryption.
+ *
+ * @param cmkArn The AWS KMS customer master key to use for decryption
+ * @param tableName The name of the DynamoDB table name that stores decrypted messages
+ */
+ public LambdaDecryptAndWriteExample(final String cmkArn, final String tableName) {
+ cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder()
+ .withKeyring(StandardKeyrings.awsKms(AwsKmsCmkId.fromString(cmkArn)))
+ .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES))
+ .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS)
+ .build();
+ crypto_ = new AwsCrypto();
+ table_ = new DynamoDB(AmazonDynamoDBClientBuilder.defaultClient()).getTable(tableName);
+ }
+
+ /**
+ * Decrypts Kinesis events and writes the data to DynamoDB
+ *
+ * @param event The KinesisEvent to decrypt
+ * @param context The lambda context
+ */
+ @Override
+ public Void handleRequest(KinesisEvent event, Context context) {
+ for (KinesisEventRecord record : event.getRecords()) {
+ ByteBuffer ciphertextBuffer = record.getKinesis().getData();
+ byte[] ciphertext = BinaryUtils.copyAllBytesFrom(ciphertextBuffer);
+
+ // Decrypt and unpack record
+ AwsCryptoResult plaintextResult = crypto_.decrypt(
+ DecryptRequest.builder()
+ .cryptoMaterialsManager(cachingMaterialsManager_)
+ .ciphertext(ciphertext).build());
+
+ // Verify the encryption context value
+ String streamArn = record.getEventSourceARN();
+ String streamName = streamArn.substring(streamArn.indexOf("/") + 1);
+ if (!streamName.equals(plaintextResult.getEncryptionContext().get("stream"))) {
+ throw new IllegalStateException("Wrong Encryption Context!");
+ }
+
+ // Write record to DynamoDB
+ String jsonItem = new String(plaintextResult.getResult(), StandardCharsets.UTF_8);
+ table_.putItem(Item.fromJSON(jsonItem));
+ }
+
+ return null;
+ }
+}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/MultiRegionRecordPusherExample.java b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/MultiRegionRecordPusherExample.java
new file mode 100644
index 000000000..d7d7e071f
--- /dev/null
+++ b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/MultiRegionRecordPusherExample.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
+ * in compliance with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.amazonaws.crypto.examples.datakeycaching;
+
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoResult;
+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.AwsKmsClientSupplier;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
+import com.amazonaws.regions.Region;
+import com.amazonaws.services.kinesis.AmazonKinesis;
+import com.amazonaws.services.kinesis.AmazonKinesisClientBuilder;
+import com.amazonaws.util.json.Jackson;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * Pushes data to Kinesis Streams in multiple Regions.
+ */
+public class MultiRegionRecordPusherExample {
+ private static final long MAX_ENTRY_AGE_MILLISECONDS = 300000;
+ private static final long MAX_ENTRY_USES = 100;
+ private static final int MAX_CACHE_ENTRIES = 100;
+ private final String streamName_;
+ private final ArrayList kinesisClients_;
+ private final CachingCryptoMaterialsManager cachingMaterialsManager_;
+ private final AwsCrypto crypto_;
+
+ /**
+ * Creates an instance of this object with Kinesis clients for all target Regions
+ * and a cached key provider containing AWS KMS master keys in all target Regions.
+ */
+ public MultiRegionRecordPusherExample(final Region[] regions, final String kmsAliasName, final String streamName) {
+ streamName_ = streamName;
+ crypto_ = new AwsCrypto();
+ kinesisClients_ = new ArrayList<>();
+
+ final DefaultAWSCredentialsProviderChain credentialsProvider = new DefaultAWSCredentialsProviderChain();
+
+ // Build AwsKmsKeyring and AmazonKinesisClient objects for each target Region
+ final List keyrings = new ArrayList<>();
+
+ for (Region region : regions) {
+ kinesisClients_.add(AmazonKinesisClientBuilder.standard()
+ .withCredentials(credentialsProvider)
+ .withRegion(region.getName())
+ .build());
+
+ keyrings.add(StandardKeyrings.awsKmsBuilder()
+ .awsKmsClientSupplier(AwsKmsClientSupplier.builder()
+ .credentialsProvider(credentialsProvider)
+ .allowedRegions(Collections.singleton(region.getName()))
+ .build())
+ .generatorKeyId(AwsKmsCmkId.fromString(kmsAliasName)).build());
+ }
+
+ // Collect keyrings into a single multi-keyring and add cache. In this example, the keyring for the
+ // first region is used as the generatorKeyring to generate a data key.
+ final List childrenKeyrings = keyrings.size() > 1 ? keyrings.subList(1, keyrings.size()) : emptyList();
+ final Keyring keyring = StandardKeyrings.multi(keyrings.get(0), childrenKeyrings);
+
+ cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder()
+ .withKeyring(keyring)
+ .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES))
+ .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS)
+ .withMessageUseLimit(MAX_ENTRY_USES)
+ .build();
+ }
+
+ /**
+ * JSON serializes and encrypts the received record data and pushes it to all target streams.
+ */
+ public void putRecord(final Map