Skip to content

chore: Add examples for MRKs #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazonaws.examples;

import java.security.GeneralSecurityException;
import java.util.Arrays;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.TableNameOverride;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.DirectKmsMaterialProvider;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;

/**
* Example showing use of AWS KMS CMP with an AWS KMS Multi-Region Key. We encrypt a record with a key in one region,
* then decrypt the ciphertext with the same key replicated to another region.
*
* This example assumes that you have a DDB Global Table replicated to two regions, and an AWS KMS Multi-Region Key
* replicated to the same regions.
*/
public class AwsKmsMultiRegionKey {

public static void main(String[] args) throws GeneralSecurityException {
final String tableName = args[0];
final String cmkArn1 = args[1];
final String cmkArn2 = args[2];

encryptRecord(tableName, cmkArn1, cmkArn2);
}

public static void encryptRecord(final String tableName, final String cmkArnEncrypt, final String cmkArnDecrypt) throws GeneralSecurityException {
AWSKMS kmsDecrypt = null;
AWSKMS kmsEncrypt = null;
AmazonDynamoDB ddbEncrypt = null;
AmazonDynamoDB ddbDecrypt = null;
try {
// Sample object to be encrypted
AwsKmsEncryptedObject.DataPoJo record = new AwsKmsEncryptedObject.DataPoJo();
record.setPartitionAttribute("is this");
record.setSortAttribute(42);
record.setExample("data");
record.setSomeNumbers(99);
record.setSomeBinary(new byte[]{0x00, 0x01, 0x02});
record.setLeaveMe("alone");

// Set up clients and configuration in the first region. All of this is thread-safe and can be reused
// across calls
final String encryptRegion = cmkArnEncrypt.split(":")[3];
kmsEncrypt = AWSKMSClientBuilder.standard().withRegion(encryptRegion).build();
ddbEncrypt = AmazonDynamoDBClientBuilder.standard().withRegion(encryptRegion).build();
final DirectKmsMaterialProvider cmpEncrypt = new DirectKmsMaterialProvider(kmsEncrypt, cmkArnEncrypt);
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmpEncrypt);

// Mapper Creation
// Please note the use of SaveBehavior.PUT (SaveBehavior.CLOBBER works as well).
// Omitting this can result in data-corruption.
DynamoDBMapperConfig mapperConfig = DynamoDBMapperConfig.builder()
.withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.PUT)
.withTableNameOverride(TableNameOverride.withTableNameReplacement(tableName))
.build();
DynamoDBMapper encryptMapper = new DynamoDBMapper(ddbEncrypt, mapperConfig, new AttributeEncryptor(encryptor));

System.out.println("Plaintext Record: " + record);
// Save the item to the DynamoDB table
encryptMapper.save(record);

// DDB Global Table replication takes some time. Sleep for a moment to give the item a chance to replicate
// to the second region
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}

// Set up clients and configuration in the second region
final String decryptRegion = cmkArnDecrypt.split(":")[3];
kmsDecrypt = AWSKMSClientBuilder.standard().withRegion(decryptRegion).build();
ddbDecrypt = AmazonDynamoDBClientBuilder.standard().withRegion(decryptRegion).build();
final DirectKmsMaterialProvider cmpDecrypt = new DirectKmsMaterialProvider(kmsDecrypt, cmkArnDecrypt);
final DynamoDBEncryptor decryptor = DynamoDBEncryptor.getInstance(cmpDecrypt);

DynamoDBMapper decryptMapper = new DynamoDBMapper(ddbDecrypt, mapperConfig, new AttributeEncryptor(decryptor));

// Retrieve (and decrypt) it in the second region. This allows you to avoid a cross-region KMS call to the
// first region if your application is running in the second region
AwsKmsEncryptedObject.DataPoJo decryptedRecord = decryptMapper.load(AwsKmsEncryptedObject.DataPoJo.class, "is this", 42);
System.out.println("Decrypted Record: " + decryptedRecord);

// The decrypted fields match the original fields before encryption
assert record.getExample().equals(decryptedRecord.getExample());
assert record.getSomeNumbers() == decryptedRecord.getSomeNumbers();
assert Arrays.equals(record.getSomeBinary(), decryptedRecord.getSomeBinary());
} finally {
if (kmsDecrypt != null) {
kmsDecrypt.shutdown();
}
if (kmsEncrypt != null) {
kmsEncrypt.shutdown();
}
if (ddbEncrypt != null) {
ddbEncrypt.shutdown();
}
if (ddbDecrypt != null) {
ddbDecrypt.shutdown();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.examples;

import org.testng.annotations.Test;

import java.security.GeneralSecurityException;

import static com.amazonaws.examples.TestUtils.US_EAST_1_MRK_KEY_ID;
import static com.amazonaws.examples.TestUtils.US_WEST_2_MRK_KEY_ID;

public class AwsKmsMultiRegionKeyIT {
private static final String TABLE_NAME = "ddbec-mrk-testing";

@Test
public void testEncryptAndDecrypt() throws GeneralSecurityException {
AwsKmsMultiRegionKey.encryptRecord(TABLE_NAME, US_EAST_1_MRK_KEY_ID, US_WEST_2_MRK_KEY_ID);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ private TestUtils() {
*/
public static final String US_WEST_2_KEY_ID = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f";
public static final String US_WEST_2 = "us-west-2";
public static final String US_EAST_1_MRK_KEY_ID = "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7";
public static final String US_WEST_2_MRK_KEY_ID = "arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7";

public static void createDDBTable(AmazonDynamoDB ddb, String tableName, String partitionName, String sortName) {
ArrayList<AttributeDefinition> attrDef = new ArrayList<AttributeDefinition>();
Expand Down