From fd6b20ed0912cf00ead90f5771e8e78d71cf02f5 Mon Sep 17 00:00:00 2001
From: lizroth <30636882+lizroth@users.noreply.github.com>
Date: Fri, 5 Mar 2021 10:58:50 -0800
Subject: [PATCH 001/105] fix: Data model migration doc breadcrumb.
Merge pull request #139 from lizroth/migration-doc-note
---
.gitignore | 1 +
README.md | 12 ++++++++++++
.../dynamodbv2/datamodeling/AttributeEncryptor.java | 10 +++++++---
.../datamodeling/encryption/DoNotEncrypt.java | 6 +++++-
.../datamodeling/encryption/DoNotTouch.java | 4 ++++
.../datamodeling/encryption/DynamoDBEncryptor.java | 6 +++++-
.../datamodeling/encryption/DynamoDBSigner.java | 4 ++++
.../encryption/HandleUnknownAttributes.java | 6 +++++-
.../datamodeling/encryption/TableAadOverride.java | 4 ++++
.../datamodeling/TransformerHolisticIT.java | 2 +-
10 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/.gitignore b/.gitignore
index 97c18da1..c2525ee2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ release.sh
target
.idea/
*.iml
+.DS_Store
diff --git a/README.md b/README.md
index af9f85ac..22d00cde 100644
--- a/README.md
+++ b/README.md
@@ -111,6 +111,18 @@ Note that by default all attributes except the primary keys are both encrypted a
There is a variety of existing [EncryptionMaterialsProvider][materialprovider] implementations that you can use to provide the encryption material, including [KeyStoreMaterialsProvider][keystoreprovider] which makes use of a Java keystore. Alternatively, you can also plug in your own custom implementation.
+### Changing Your Data Model
+
+Every time you encrypt or decrypt an item, you need to provide attribute actions that tell the DynamoDB Encryption
+Client which attributes to encrypt and sign, which attributes to sign (but not encrypt), and which to ignore. Attribute
+actions are not saved in the encrypted item and the DynamoDB Encryption Client does not update your attribute actions
+automatically.
+
+Whenever you change your data model, that is, when you add or remove attributes from your table items, you need to take
+additional steps to safely migrate the client-side encryption configuration.
+
+For guidance on this process, please see the developer guide on [Changing Your Data Model](https://docs.aws.amazon.com/dynamodb-encryption-client/latest/devguide/data-model.html).
+
### Downloads
You can download the [latest snapshot release][download] or pick it up from Maven:
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java
index 146e77d2..1727a140 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java
@@ -38,9 +38,13 @@
/**
* Encrypts all non-key fields prior to storing them in DynamoDB.
- * This must be used with @{link SaveBehavior#PUT} or @{link SaveBehavior#CLOBBER}.
- *
- * @author Greg Rubin
+ * This must be used with {@link SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}.
+ *
+ *
For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
+ *
+ * @author Greg Rubin
*/
public class AttributeEncryptor implements AttributeTransformer {
private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class);
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java
index 501bc642..fcf067a8 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java
@@ -23,7 +23,11 @@
/**
* Prevents the associated item (class or attribute) from being encrypted.
- *
+ *
+ * For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
+ *
* @author Greg Rubin
*/
@DynamoDB
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java
index d2a817fd..ee2be7ec 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java
@@ -23,6 +23,10 @@
/**
* Prevents the associated item from being encrypted or signed.
+ *
+ * For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
*
* @author Greg Rubin
*/
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java
index 7a70291c..678b0e40 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java
@@ -50,7 +50,11 @@
/**
* The low-level API used by {@link AttributeEncryptor} to perform crypto
* operations on the record attributes.
- *
+ *
+ * For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
+ *
* @author Greg Rubin
*/
public class DynamoDBEncryptor {
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java
index cdded8fd..f4a13905 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java
@@ -43,6 +43,10 @@
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
/**
+ * For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
+ *
* @author Greg Rubin
*/
// NOTE: This class must remain thread-safe.
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java
index b067ac55..fafa85b5 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java
@@ -30,8 +30,12 @@
* attributes will only be included in the signature calculation, and if it's
* added to a class with default encryption behavior, the unknown attributes
* will be signed and decrypted.
+ *
+ * For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
*
- * @author Dan Cavallaro
+ * @author Dan Cavallaro
*/
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java
index 9bd58a0c..eb9c15db 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java
@@ -24,6 +24,10 @@
* {@code tableName} instead. This can be useful when multiple tables are
* used interchangably and data should be able to be copied or moved
* between them without needing to be reencrypted.
+ *
+ * For guidance on performing a safe data model change procedure, please see
+ *
+ * DynamoDB Encryption Client Developer Guide: Changing your data model
*
* @author Greg Rubin
*/
diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java
index e982121f..521d908a 100644
--- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java
+++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java
@@ -397,7 +397,7 @@ public void simpleSaveLoad() {
/**
* This test ensures that optimistic locking can be successfully done through the {@link DynamoDBMapper} when
- * combined with the @{link AttributeEncryptor}. Specifically it checks that {@link SaveBehavior#PUT} properly
+ * combined with the {@link AttributeEncryptor}. Specifically it checks that {@link SaveBehavior#PUT} properly
* enforces versioning and will result in a {@link ConditionalCheckFailedException} when optimistic locking should
* prevent a write. Finally, it checks that {@link SaveBehavior#CLOBBER} properly ignores optimistic locking and
* overwrites the old value.
From bf72702e171ff820220f437735d28f01fe92773c Mon Sep 17 00:00:00 2001
From: Ben Farley <47006790+farleyb-amazon@users.noreply.github.com>
Date: Thu, 18 Mar 2021 11:49:41 -0600
Subject: [PATCH 002/105] fix: Update user agent string to correctly reflect
version (#141)
* fix: Update user agent string to correctly reflect version
---
sdk1/pom.xml | 7 +++++++
.../providers/DirectKmsMaterialProvider.java | 7 +++----
.../dynamodbv2/datamodeling/internal/Utils.java | 16 ++++++++++++++++
sdk1/src/main/resources/project.properties | 1 +
.../providers/DirectKmsMaterialProviderTest.java | 14 ++++++++++++++
5 files changed, 41 insertions(+), 4 deletions(-)
create mode 100644 sdk1/src/main/resources/project.properties
diff --git a/sdk1/pom.xml b/sdk1/pom.xml
index 2c0aa5f9..8620ff72 100644
--- a/sdk1/pom.xml
+++ b/sdk1/pom.xml
@@ -272,6 +272,13 @@
+
+
+ src/main/resources
+ true
+
+
+
org.apache.maven.plugins
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java
index bee842ba..a655fa86 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProvider.java
@@ -30,7 +30,6 @@
import com.amazonaws.services.kms.model.GenerateDataKeyRequest;
import com.amazonaws.services.kms.model.GenerateDataKeyResult;
import com.amazonaws.util.StringUtils;
-import com.amazonaws.util.VersionInfoUtils;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@@ -43,6 +42,7 @@
import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.CONTENT_KEY_ALGORITHM;
import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.ENVELOPE_KEY;
import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.WrappedRawMaterials.KEY_WRAPPING_ALGORITHM;
+import static com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils.loadVersion;
/**
* Generates a unique data key for each record in DynamoDB and protects that key
@@ -53,9 +53,8 @@
* @see KMS Encryption Context
*/
public class DirectKmsMaterialProvider implements EncryptionMaterialsProvider {
- private static final String VERSION_STRING = "1.0";
- private static final String USER_AGENT = DirectKmsMaterialProvider.class.getName()
- + "/" + VERSION_STRING + "/" + VersionInfoUtils.getVersion();
+ static final String USER_AGENT_PREFIX = "DynamodbEncryptionSdkJava/";
+ private static final String USER_AGENT = USER_AGENT_PREFIX + loadVersion();
private static final String COVERED_ATTR_CTX_KEY = "aws-kms-ec-attr";
private static final String SIGNING_KEY_ALGORITHM = "amzn-ddb-sig-alg";
private static final String TABLE_NAME_EC_KEY = "*aws-kms-table*";
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java
index 5ee04895..86e6604a 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/Utils.java
@@ -14,7 +14,9 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.internal;
+import java.io.IOException;
import java.security.SecureRandom;
+import java.util.Properties;
public class Utils {
private static final ThreadLocal RND = new ThreadLocal() {
@@ -47,4 +49,18 @@ public static V checkNotNull(final V ref, final String errMsg) {
return ref;
}
}
+
+ /*
+ * Loads the version of the library
+ */
+ public static String loadVersion() {
+ try {
+ final Properties properties = new Properties();
+ properties.load(ClassLoader.getSystemResourceAsStream("project.properties"));
+ return properties.getProperty("version");
+ } catch (final IOException ex) {
+ return "unknown";
+ }
+ }
+
}
diff --git a/sdk1/src/main/resources/project.properties b/sdk1/src/main/resources/project.properties
new file mode 100644
index 00000000..defbd482
--- /dev/null
+++ b/sdk1/src/main/resources/project.properties
@@ -0,0 +1 @@
+version=${project.version}
diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java
index 8eb71f73..de4b60b5 100644
--- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java
+++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/DirectKmsMaterialProviderTest.java
@@ -12,6 +12,7 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers;
+import com.amazonaws.RequestClientOptions;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials;
@@ -337,6 +338,19 @@ public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) {
assertTrue(gdkCalled.get());
}
+ @Test
+ public void userAgentIsAdded() {
+ AWSKMS kmsSpy = new FakeKMS() {
+ @Override
+ public GenerateDataKeyResult generateDataKey(GenerateDataKeyRequest r) {
+ assertTrue(r.getRequestClientOptions().getClientMarker(RequestClientOptions.Marker.USER_AGENT)
+ .contains(DirectKmsMaterialProvider.USER_AGENT_PREFIX));
+ return super.generateDataKey(r);
+ }
+ };
+ new DirectKmsMaterialProvider(kmsSpy, keyId).getEncryptionMaterials(ctx);
+ }
+
private static class ExtendedKmsMaterialProvider extends DirectKmsMaterialProvider {
private final String encryptionKeyIdAttributeName;
From 07fb66b447eb5a76b225f2368081ace6b3915334 Mon Sep 17 00:00:00 2001
From: Robin Salkeld
Date: Tue, 25 May 2021 12:52:48 -0700
Subject: [PATCH 003/105] chore: Add repo-sync actions (#143)
See https://github.com/repo-sync/repo-sync
---
.github/workflows/repo-sync.yml | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 .github/workflows/repo-sync.yml
diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml
new file mode 100644
index 00000000..b7605354
--- /dev/null
+++ b/.github/workflows/repo-sync.yml
@@ -0,0 +1,25 @@
+name: Repo Sync
+
+on:
+ workflow_dispatch: # allows triggering this manually through the Actions UI
+
+jobs:
+ repo-sync:
+ name: Repo Sync
+ environment: repo-sync
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: repo-sync/github-sync@v2
+ name: Sync repo to branch
+ with:
+ source_repo: ${{ secrets.SOURCE_REPO }}
+ source_branch: master
+ destination_branch: ${{ secrets.INTERMEDIATE_BRANCH }}
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ - uses: repo-sync/pull-request@v2
+ name: Create pull request
+ with:
+ source_branch: ${{ secrets.INTERMEDIATE_BRANCH }}
+ destination_branch: master
+ github_token: ${{ secrets.GITHUB_TOKEN }}
From bafd6fb7a3fe25752b5e9a50b85db68869055469 Mon Sep 17 00:00:00 2001
From: Alex Chew
Date: Tue, 1 Jun 2021 14:47:45 -0700
Subject: [PATCH 004/105] chore: add issue template (#142)
Co-authored-by: Robin Salkeld
---
...amazon-dynamodb-encryption-client-issue.md | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md
diff --git a/.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md b/.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md
new file mode 100644
index 00000000..d5b0433d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md
@@ -0,0 +1,26 @@
+---
+name: Amazon DynamoDB Encryption Client Issue
+about: Amazon DynamoDB Encryption Client Issue
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+### Security issue notifications
+
+If you discover a potential security issue in the Amazon DynamoDB Encryption Client we ask that you notify AWS Security via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue.
+
+### Problem:
+
+A short description of what the problem is and why we need to fix it. Add reproduction steps if necessary.
+
+### Solution:
+
+A description of the possible solution in terms of DynamoDB Encryption Client architecture.
+
+### Out of scope:
+
+Is there anything the solution will intentionally NOT address?
+
+[//]: # (NOTE: If you believe this might be a security issue, please email aws-security@amazon.com instead of creating a GitHub issue. For more details, see the AWS Vulnerability Reporting Guide: https://aws.amazon.com/security/vulnerability-reporting/ )
From 731e880b3858a1dff21ff7746308cd8fb7a5fe1e Mon Sep 17 00:00:00 2001
From: Ben Farley <47006790+farleyb-amazon@users.noreply.github.com>
Date: Thu, 17 Jun 2021 16:12:29 -0600
Subject: [PATCH 005/105] chore: Add examples for MRKs (#145)
---
.../examples/AwsKmsMultiRegionKey.java | 111 ++++++++++++++++++
.../examples/AwsKmsMultiRegionKeyIT.java | 20 ++++
.../com/amazonaws/examples/TestUtils.java | 2 +
3 files changed, 133 insertions(+)
create mode 100644 examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java
create mode 100644 examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java
diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java
new file mode 100644
index 00000000..5ba27db0
--- /dev/null
+++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java
@@ -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();
+ }
+ }
+ }
+}
diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java
new file mode 100644
index 00000000..28ba4f34
--- /dev/null
+++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java
@@ -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);
+ }
+}
diff --git a/examples/src/test/java/com/amazonaws/examples/TestUtils.java b/examples/src/test/java/com/amazonaws/examples/TestUtils.java
index 15030b50..349e1a41 100644
--- a/examples/src/test/java/com/amazonaws/examples/TestUtils.java
+++ b/examples/src/test/java/com/amazonaws/examples/TestUtils.java
@@ -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 attrDef = new ArrayList();
From 873b7a725f4df7c0fea0a337fffea8ba82bb717f Mon Sep 17 00:00:00 2001
From: lavaleri <49660121+lavaleri@users.noreply.github.com>
Date: Tue, 22 Jun 2021 10:34:01 -0700
Subject: [PATCH 006/105] chore: Add format check to CI and format (#146)
---
buildspec.yml | 5 +
codebuild/static-analysis.yml | 9 +
.../examples/AsymmetricEncryptedItem.java | 84 +-
.../examples/AwsKmsEncryptedItem.java | 83 +-
.../examples/AwsKmsEncryptedObject.java | 71 +-
.../examples/AwsKmsMultiRegionKey.java | 169 +-
...ionContextOverridesWithDynamoDBMapper.java | 301 +--
.../examples/MostRecentEncryptedItem.java | 102 +-
.../examples/SymmetricEncryptedItem.java | 92 +-
.../examples/AsymmetricEncryptedItemTest.java | 21 +-
.../examples/AwsKmsEncryptedItemIT.java | 21 +-
.../examples/AwsKmsEncryptedObjectIT.java | 28 +-
.../examples/AwsKmsMultiRegionKeyIT.java | 17 +-
...nContextOverridesWithDynamoDBMapperIT.java | 35 +-
.../examples/MostRecentEncryptedItemIT.java | 38 +-
.../examples/SymmetricEncryptedItemTest.java | 31 +-
.../com/amazonaws/examples/TestUtils.java | 82 +-
pom.xml | 10 +
.../datamodeling/AttributeEncryptor.java | 450 +++--
.../datamodeling/encryption/DelegatedKey.java | 203 +-
.../datamodeling/encryption/DoNotEncrypt.java | 15 +-
.../datamodeling/encryption/DoNotTouch.java | 17 +-
.../encryption/DynamoDBEncryptor.java | 1067 +++++------
.../encryption/DynamoDBSigner.java | 387 ++--
.../encryption/EncryptionContext.java | 357 ++--
.../encryption/EncryptionFlags.java | 8 +-
.../encryption/HandleUnknownAttributes.java | 27 +-
.../encryption/TableAadOverride.java | 19 +-
.../materials/AbstractRawMaterials.java | 77 +-
.../materials/AsymmetricRawMaterials.java | 43 +-
.../materials/CryptographicMaterials.java | 6 +-
.../materials/DecryptionMaterials.java | 10 +-
.../materials/EncryptionMaterials.java | 10 +-
.../materials/SymmetricRawMaterials.java | 69 +-
.../materials/WrappedRawMaterials.java | 325 ++--
.../providers/AsymmetricStaticProvider.java | 41 +-
.../providers/CachingMostRecentProvider.java | 315 ++--
.../providers/DirectKmsMaterialProvider.java | 545 +++---
.../EncryptionMaterialsProvider.java | 78 +-
.../providers/KeyStoreMaterialsProvider.java | 309 ++--
.../providers/SymmetricStaticProvider.java | 182 +-
.../providers/WrappedMaterialsProvider.java | 244 ++-
.../encryption/providers/store/MetaStore.java | 689 +++----
.../providers/store/ProviderStore.java | 107 +-
.../utils/EncryptionContextOperators.java | 112 +-
.../internal/AttributeValueMarshaller.java | 486 +++--
.../datamodeling/internal/Base64.java | 46 +-
.../internal/ByteBufferInputStream.java | 60 +-
.../datamodeling/internal/Hkdf.java | 475 +++--
.../datamodeling/internal/LRUCache.java | 113 +-
.../datamodeling/internal/MsClock.java | 4 +-
.../datamodeling/internal/TTLCache.java | 415 ++---
.../datamodeling/internal/Utils.java | 68 +-
.../datamodeling/AttributeEncryptorTest.java | 1200 ++++++------
.../datamodeling/TransformerHolisticIT.java | 1569 ++++++++--------
.../encryption/DelegatedEncryptionTest.java | 476 ++---
.../DelegatedEnvelopeEncryptionTest.java | 476 ++---
.../encryption/DynamoDBEncryptorTest.java | 965 +++++-----
.../encryption/DynamoDBSignerTest.java | 888 +++++----
.../materials/AsymmetricRawMaterialsTest.java | 213 +--
.../materials/SymmetricRawMaterialsTest.java | 137 +-
.../AsymmetricStaticProviderTest.java | 190 +-
.../CachingMostRecentProviderTests.java | 1126 ++++++------
.../DirectKmsMaterialProviderTest.java | 751 ++++----
.../KeyStoreMaterialsProviderTest.java | 529 +++---
.../SymmetricStaticProviderTest.java | 287 +--
.../WrappedMaterialsProviderTest.java | 745 ++++----
.../providers/store/MetaStoreTests.java | 609 +++---
.../utils/EncryptionContextOperatorsTest.java | 281 +--
.../AttributeValueMarshallerTest.java | 689 +++----
.../datamodeling/internal/Base64Tests.java | 145 +-
.../internal/ByteBufferInputStreamTest.java | 107 +-
.../internal/ConcurrentTTLCacheTest.java | 397 ++--
.../datamodeling/internal/HkdfTests.java | 336 ++--
.../datamodeling/internal/LRUCacheTest.java | 183 +-
.../datamodeling/internal/TTLCacheTest.java | 697 +++----
.../BinaryAttributeByteArrayTestClass.java | 115 +-
.../BinaryAttributeByteBufferTestClass.java | 115 +-
.../CrossSDKVerificationTestClass.java | 755 ++++----
.../encryption/IndexRangeKeyTestClass.java | 303 ++-
.../MapperQueryExpressionCryptoTest.java | 1005 +++++-----
.../encryption/NoSuchTableTestClass.java | 17 +-
.../encryption/NumberAttributeTestClass.java | 584 +++---
.../NumberSetAttributeTestClass.java | 404 ++--
.../mapper/encryption/RangeKeyTestClass.java | 277 ++-
.../encryption/StringAttributeTestClass.java | 114 +-
.../StringSetAttributeTestClass.java | 116 +-
.../encryption/TestDynamoDBMapperFactory.java | 23 +-
.../TestEncryptionMaterialsProvider.java | 48 +-
.../integration/AutoGeneratedKeysITCase.java | 1632 ++++++++---------
.../mapper/integration/BatchWriteITCase.java | 826 +++++----
.../integration/BinaryAttributesITCase.java | 476 ++---
.../mapper/integration/ComplexTypeITCase.java | 566 +++---
.../integration/ConfigurationITCase.java | 389 ++--
.../DynamoDBCryptoIntegrationTestBase.java | 343 ++--
...namoDBMapperCryptoIntegrationTestBase.java | 40 +-
.../mapper/integration/DynamoDBTestBase.java | 107 +-
.../integration/ExceptionHandlingITCase.java | 807 ++++----
.../HashKeyOnlyTableWithGSIITCase.java | 217 ++-
.../IndexRangeKeyAttributesITCase.java | 719 ++++----
.../mapper/integration/InheritanceITCase.java | 482 +++--
.../mapper/integration/KeyOnlyPutITCase.java | 103 +-
.../MapperLoadingStrategyConfigITCase.java | 566 +++---
...erSaveConfigCryptoIntegrationTestBase.java | 315 ++--
.../integration/MapperSaveConfigITCase.java | 998 +++++-----
.../NumericSetAttributesITCase.java | 292 +--
.../mapper/integration/QueryITCase.java | 320 ++--
.../integration/RangeKeyAttributesITCase.java | 282 +--
.../mapper/integration/ScanITCase.java | 425 ++---
.../SimpleNumericAttributesITCase.java | 485 ++---
.../SimpleStringAttributesITCase.java | 335 ++--
.../StringSetAttributesITCase.java | 242 +--
.../VersionAttributeUpdateITCase.java | 1134 ++++++------
.../dynamodbv2/testing/AttrMatcher.java | 171 +-
.../testing/AttributeValueDeserializer.java | 77 +-
.../testing/AttributeValueMatcher.java | 154 +-
.../testing/AttributeValueSerializer.java | 66 +-
.../dynamodbv2/testing/DdbRecordMatcher.java | 70 +-
.../services/dynamodbv2/testing/FakeKMS.java | 314 ++--
.../dynamodbv2/testing/FakeParameters.java | 187 +-
.../dynamodbv2/testing/ScenarioManifest.java | 124 +-
.../dynamodbv2/testing/TestDelegatedKey.java | 175 +-
.../dynamodbv2/testing/types/BaseClass.java | 319 ++--
.../types/BaseClassWithNewAttribute.java | 15 +-
...seClassWithUnknownAttributeAnnotation.java | 3 +-
.../testing/types/DoNotEncryptField.java | 45 +-
.../testing/types/DoNotTouchField.java | 45 +-
.../dynamodbv2/testing/types/HashKeyOnly.java | 79 +-
.../dynamodbv2/testing/types/KeysOnly.java | 84 +-
.../dynamodbv2/testing/types/Mixed.java | 41 +-
.../dynamodbv2/testing/types/SignOnly.java | 3 +-
...ignOnlyWithUnknownAttributeAnnotation.java | 3 +-
...wnAttributeAnnotationWithNewAttribute.java | 18 +-
.../testing/types/TableOverride.java | 3 +-
.../dynamodbv2/testing/types/Untouched.java | 3 +-
.../types/UntouchedWithNewAttribute.java | 15 +-
...touchedWithUnknownAttributeAnnotation.java | 3 +-
...wnAttributeAnnotationWithNewAttribute.java | 18 +-
138 files changed, 20049 insertions(+), 19537 deletions(-)
create mode 100644 codebuild/static-analysis.yml
diff --git a/buildspec.yml b/buildspec.yml
index 365eb003..1fc7d652 100644
--- a/buildspec.yml
+++ b/buildspec.yml
@@ -23,3 +23,8 @@ batch:
env:
env:
image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
+ - identifier: static_analysis
+ buildspec: codebuild/static-analysis.yml
+ env:
+ env:
+ image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
diff --git a/codebuild/static-analysis.yml b/codebuild/static-analysis.yml
new file mode 100644
index 00000000..3c3b2f38
--- /dev/null
+++ b/codebuild/static-analysis.yml
@@ -0,0 +1,9 @@
+version: 0.2
+
+phases:
+ install:
+ runtime-versions:
+ java: corretto11
+ build:
+ commands:
+ - mvn com.coveo:fmt-maven-plugin:check
diff --git a/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java
index 2bf245f9..a5fb4ef9 100644
--- a/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java
+++ b/examples/src/main/java/com/amazonaws/examples/AsymmetricEncryptedItem.java
@@ -14,6 +14,11 @@
*/
package com.amazonaws.examples;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
@@ -23,15 +28,9 @@
import java.util.Map;
import java.util.Set;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider;
-import com.amazonaws.services.dynamodbv2.model.AttributeValue;
-
/**
- * Example showing use of RSA keys for encryption and signing.
- * For ease of the example, we create new random ones every time.
+ * Example showing use of RSA keys for encryption and signing. For ease of the example, we create
+ * new random ones every time.
*/
public class AsymmetricEncryptedItem {
private static final String STRING_FIELD_NAME = "example";
@@ -50,7 +49,8 @@ public static void main(String[] args) throws GeneralSecurityException {
encryptRecord(tableName, wrappingKeys, signingKeys);
}
- public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair signingKeys) throws GeneralSecurityException {
+ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair signingKeys)
+ throws GeneralSecurityException {
// Sample record to be encrypted
final String partitionKeyName = "partition_attribute";
final String sortKeyName = "sort_attribute";
@@ -59,25 +59,34 @@ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair
record.put(sortKeyName, new AttributeValue().withN("55"));
record.put(STRING_FIELD_NAME, new AttributeValue().withS("data"));
record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99"));
- record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02})));
- record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute
+ record.put(
+ BINARY_FIELD_NAME,
+ new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02})));
+ record.put(
+ IGNORED_FIELD_NAME,
+ new AttributeValue().withS("alone")); // We want to ignore this attribute
- // Set up our configuration and clients. All of this is thread-safe and can be reused across calls.
+ // Set up our configuration and clients. All of this is thread-safe and can be reused across
+ // calls.
// Provider Configuration
- final WrappedMaterialsProvider cmp = new WrappedMaterialsProvider(wrappingKeys.getPublic(), wrappingKeys.getPrivate(), signingKeys);
+ final WrappedMaterialsProvider cmp =
+ new WrappedMaterialsProvider(
+ wrappingKeys.getPublic(), wrappingKeys.getPrivate(), signingKeys);
// Encryptor creation
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
// Information about the context of our data (normally just Table information)
- final EncryptionContext encryptionContext = new EncryptionContext.Builder()
- .withTableName(tableName)
- .withHashKeyName(partitionKeyName)
- .withRangeKeyName(sortKeyName)
- .build();
+ final EncryptionContext encryptionContext =
+ new EncryptionContext.Builder()
+ .withTableName(tableName)
+ .withHashKeyName(partitionKeyName)
+ .withRangeKeyName(sortKeyName)
+ .build();
// Describe what actions need to be taken for each attribute
final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN);
- final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
+ final EnumSet encryptAndSign =
+ EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
final Map> actions = new HashMap<>();
for (final String attributeName : record.keySet()) {
switch (attributeName) {
@@ -98,13 +107,22 @@ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair
// End set-up
// Encrypt the plaintext record directly
- final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext);
+ final Map encrypted_record =
+ encryptor.encryptRecord(record, actions, encryptionContext);
// Encrypted record fields change as expected
- assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes
- assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes
- assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
- assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
+ assert encrypted_record.get(STRING_FIELD_NAME).getB()
+ != null; // the encrypted string is stored as bytes
+ assert encrypted_record.get(NUMBER_FIELD_NAME).getB()
+ != null; // the encrypted number is stored as bytes
+ assert !record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
+ assert record
+ .get(IGNORED_FIELD_NAME)
+ .getS()
+ .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
// We could now put the encrypted item to DynamoDB just as we would any other item.
// We're skipping it to to keep the example simpler.
@@ -113,12 +131,22 @@ public static void encryptRecord(String tableName, KeyPair wrappingKeys, KeyPair
System.out.println("Encrypted Record: " + encrypted_record);
// Decryption is identical. We'll pretend that we retrieved the record from DynamoDB.
- final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
+ final Map decrypted_record =
+ encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
System.out.println("Decrypted Record: " + decrypted_record);
// The decrypted fields match the original fields before encryption
- assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS());
- assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
- assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
+ assert record
+ .get(STRING_FIELD_NAME)
+ .getS()
+ .equals(decrypted_record.get(STRING_FIELD_NAME).getS());
+ assert record
+ .get(NUMBER_FIELD_NAME)
+ .getN()
+ .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
+ assert record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
}
}
diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java
index 7c9f8431..f0d8896b 100644
--- a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java
+++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedItem.java
@@ -14,13 +14,6 @@
*/
package com.amazonaws.examples;
-import java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags;
@@ -28,10 +21,14 @@
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
-/**
- * Example showing use of AWS KMS CMP with record encryption functions directly.
- */
+/** Example showing use of AWS KMS CMP with record encryption functions directly. */
public class AwsKmsEncryptedItem {
private static final String STRING_FIELD_NAME = "example";
private static final String BINARY_FIELD_NAME = "and some binary";
@@ -54,7 +51,9 @@ public static void main(String[] args) throws GeneralSecurityException {
}
}
- public static void encryptRecord(final String tableName, final String cmkArn, final AWSKMS kmsClient) throws GeneralSecurityException {
+ public static void encryptRecord(
+ final String tableName, final String cmkArn, final AWSKMS kmsClient)
+ throws GeneralSecurityException {
// Sample record to be encrypted
final String partitionKeyName = "partition_attribute";
final String sortKeyName = "sort_attribute";
@@ -63,10 +62,15 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi
record.put(sortKeyName, new AttributeValue().withN("55"));
record.put(STRING_FIELD_NAME, new AttributeValue().withS("data"));
record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99"));
- record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02})));
- record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute
+ record.put(
+ BINARY_FIELD_NAME,
+ new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02})));
+ record.put(
+ IGNORED_FIELD_NAME,
+ new AttributeValue().withS("alone")); // We want to ignore this attribute
- // Set up our configuration and clients. All of this is thread-safe and can be reused across calls.
+ // Set up our configuration and clients. All of this is thread-safe and can be reused across
+ // calls.
// This example assumes we already have a AWS KMS client `kmsClient`
// Provider Configuration
final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn);
@@ -74,15 +78,17 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
// Information about the context of our data (normally just Table information)
- final EncryptionContext encryptionContext = new EncryptionContext.Builder()
- .withTableName(tableName)
- .withHashKeyName(partitionKeyName)
- .withRangeKeyName(sortKeyName)
- .build();
+ final EncryptionContext encryptionContext =
+ new EncryptionContext.Builder()
+ .withTableName(tableName)
+ .withHashKeyName(partitionKeyName)
+ .withRangeKeyName(sortKeyName)
+ .build();
// Describe what actions need to be taken for each attribute
final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN);
- final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
+ final EnumSet encryptAndSign =
+ EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
final Map> actions = new HashMap<>();
for (final String attributeName : record.keySet()) {
switch (attributeName) {
@@ -103,13 +109,22 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi
// End set-up
// Encrypt the plaintext record directly
- final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext);
+ final Map encrypted_record =
+ encryptor.encryptRecord(record, actions, encryptionContext);
// Encrypted record fields change as expected
- assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes
- assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes
- assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
- assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
+ assert encrypted_record.get(STRING_FIELD_NAME).getB()
+ != null; // the encrypted string is stored as bytes
+ assert encrypted_record.get(NUMBER_FIELD_NAME).getB()
+ != null; // the encrypted number is stored as bytes
+ assert !record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
+ assert record
+ .get(IGNORED_FIELD_NAME)
+ .getS()
+ .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
// We could now put the encrypted item to DynamoDB just as we would any other item.
// We're skipping it to to keep the example simpler.
@@ -118,12 +133,22 @@ public static void encryptRecord(final String tableName, final String cmkArn, fi
System.out.println("Encrypted Record: " + encrypted_record);
// Decryption is identical. We'll pretend that we retrieved the record from DynamoDB.
- final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
+ final Map decrypted_record =
+ encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
System.out.println("Decrypted Record: " + decrypted_record);
// The decrypted fields match the original fields before encryption
- assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS());
- assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
- assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
+ assert record
+ .get(STRING_FIELD_NAME)
+ .getS()
+ .equals(decrypted_record.get(STRING_FIELD_NAME).getS());
+ assert record
+ .get(NUMBER_FIELD_NAME)
+ .getN()
+ .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
+ assert record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
}
}
diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java
index b148fed5..55139bb4 100644
--- a/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java
+++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsEncryptedObject.java
@@ -14,12 +14,6 @@
*/
package com.amazonaws.examples;
-import java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
@@ -36,13 +30,17 @@
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
/**
- * This demonstrates how to use the {@link DynamoDBMapper} with the {@link AttributeEncryptor}
- * to encrypt your data. Before you can use this you need to set up a DynamoDB table called "ExampleTable"
- * to hold the encrypted data.
- * "ExampleTable" should have a partition key named "partition_attribute" for Strings
- * and a sort (range) key named "sort_attribute" for numbers.
+ * This demonstrates how to use the {@link DynamoDBMapper} with the {@link AttributeEncryptor} to
+ * encrypt your data. Before you can use this you need to set up a DynamoDB table called
+ * "ExampleTable" to hold the encrypted data. "ExampleTable" should have a partition key named
+ * "partition_attribute" for Strings and a sort (range) key named "sort_attribute" for numbers.
*/
public class AwsKmsEncryptedObject {
public static final String EXAMPLE_TABLE_NAME = "ExampleTable";
@@ -74,43 +72,54 @@ public static void main(String[] args) throws GeneralSecurityException {
}
}
- public static void encryptRecord(final String cmkArn, final AmazonDynamoDB ddbClient, final AWSKMS kmsClient) {
+ public static void encryptRecord(
+ final String cmkArn, final AmazonDynamoDB ddbClient, final AWSKMS kmsClient) {
// Sample object to be encrypted
DataPoJo record = new DataPoJo();
record.setPartitionAttribute("is this");
record.setSortAttribute(55);
record.setExample("data");
record.setSomeNumbers(99);
- record.setSomeBinary(new byte[]{0x00, 0x01, 0x02});
+ record.setSomeBinary(new byte[] {0x00, 0x01, 0x02});
record.setLeaveMe("alone");
// Set up our configuration and clients
- // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client `kmsClient`
+ // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client
+ // `kmsClient`
final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn);
// Encryptor creation
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
// 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(SaveBehavior.PUT).build();
- DynamoDBMapper mapper = new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor));
+ DynamoDBMapperConfig mapperConfig =
+ DynamoDBMapperConfig.builder().withSaveBehavior(SaveBehavior.PUT).build();
+ DynamoDBMapper mapper =
+ new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor));
System.out.println("Plaintext Record: " + record);
// Save the item to the DynamoDB table
mapper.save(record);
- // Retrieve the encrypted item (directly without decrypting) from Dynamo so we can see it in our example
+ // Retrieve the encrypted item (directly without decrypting) from Dynamo so we can see it in our
+ // example
final Map itemKey = new HashMap<>();
itemKey.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this"));
itemKey.put(SORT_ATTRIBUTE, new AttributeValue().withN("55"));
- final Map encrypted_record = ddbClient.getItem(EXAMPLE_TABLE_NAME, itemKey).getItem();
+ final Map encrypted_record =
+ ddbClient.getItem(EXAMPLE_TABLE_NAME, itemKey).getItem();
System.out.println("Encrypted Record: " + encrypted_record);
// Encrypted record fields change as expected
- assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes
- assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes
- assert !ByteBuffer.wrap(record.getSomeBinary()).equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
- assert record.getLeaveMe().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
+ assert encrypted_record.get(STRING_FIELD_NAME).getB()
+ != null; // the encrypted string is stored as bytes
+ assert encrypted_record.get(NUMBER_FIELD_NAME).getB()
+ != null; // the encrypted number is stored as bytes
+ assert !ByteBuffer.wrap(record.getSomeBinary())
+ .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
+ assert record
+ .getLeaveMe()
+ .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
// Retrieve (and decrypt) it from DynamoDB
DataPoJo decrypted_record = mapper.load(DataPoJo.class, "is this", 55);
@@ -188,11 +197,19 @@ public void setLeaveMe(String leaveMe) {
@Override
public String toString() {
- return "DataPoJo [partitionAttribute=" + partitionAttribute + ", sortAttribute="
- + sortAttribute + ", example=" + example + ", someNumbers=" + someNumbers
- + ", someBinary=" + Arrays.toString(someBinary) + ", leaveMe=" + leaveMe + "]";
+ return "DataPoJo [partitionAttribute="
+ + partitionAttribute
+ + ", sortAttribute="
+ + sortAttribute
+ + ", example="
+ + example
+ + ", someNumbers="
+ + someNumbers
+ + ", someBinary="
+ + Arrays.toString(someBinary)
+ + ", leaveMe="
+ + leaveMe
+ + "]";
}
-
-
}
}
diff --git a/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java
index 5ba27db0..b9173a69 100644
--- a/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java
+++ b/examples/src/main/java/com/amazonaws/examples/AwsKmsMultiRegionKey.java
@@ -2,9 +2,6 @@
// 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;
@@ -15,97 +12,111 @@
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.DirectKmsMaterialProvider;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
/**
- * 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.
+ * 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.
+ * 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];
+ 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);
- }
+ 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");
+ 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);
+ // 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));
+ // 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);
+ 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) {}
+ // 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);
+ // 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));
+ 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);
+ // 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();
- }
- }
+ // 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();
+ }
}
+ }
}
diff --git a/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java b/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java
index 3ea7c218..07e4fcfa 100644
--- a/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java
+++ b/examples/src/main/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapper.java
@@ -14,6 +14,8 @@
*/
package com.amazonaws.examples;
+import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap;
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
@@ -30,163 +32,174 @@
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
-
import java.security.GeneralSecurityException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap;
-
/**
- * This demonstrates how to use an operator to override the table name used in the encryption context.
- * Before you can use this you need to set up a DynamoDB table called "ExampleTableForEncryptionContextOverrides"
- * to hold the encrypted data.
- * "ExampleTableForEncryptionContextOverrides" should have a partition key named "partition_attribute" for Strings
- * and a sort (range) key named "sort_attribute" for numbers.
+ * This demonstrates how to use an operator to override the table name used in the encryption
+ * context. Before you can use this you need to set up a DynamoDB table called
+ * "ExampleTableForEncryptionContextOverrides" to hold the encrypted data.
+ * "ExampleTableForEncryptionContextOverrides" should have a partition key named
+ * "partition_attribute" for Strings and a sort (range) key named "sort_attribute" for numbers.
*/
public class EncryptionContextOverridesWithDynamoDBMapper {
- public static final String TABLE_NAME_TO_OVERRIDE = "ExampleTableForEncryptionContextOverrides";
- public static final String PARTITION_ATTRIBUTE = "partition_attribute";
- public static final String SORT_ATTRIBUTE = "sort_attribute";
-
- private static final String STRING_FIELD_NAME = "example";
- private static final String BINARY_FIELD_NAME = "and some binary";
- private static final String NUMBER_FIELD_NAME = "some numbers";
- private static final String IGNORED_FIELD_NAME = "leave me";
-
- public static void main(String[] args) throws GeneralSecurityException {
- final String cmkArn = args[0];
- final String region = args[1];
- final String encryptionContextTableName = args[2];
-
- AmazonDynamoDB ddb = null;
- AWSKMS kms = null;
- try {
- ddb = AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
- kms = AWSKMSClientBuilder.standard().withRegion(region).build();
- encryptRecord(cmkArn, encryptionContextTableName, ddb, kms);
- } finally {
- if (ddb != null) {
- ddb.shutdown();
- }
- if (kms != null) {
- kms.shutdown();
- }
- }
+ public static final String TABLE_NAME_TO_OVERRIDE = "ExampleTableForEncryptionContextOverrides";
+ public static final String PARTITION_ATTRIBUTE = "partition_attribute";
+ public static final String SORT_ATTRIBUTE = "sort_attribute";
+
+ private static final String STRING_FIELD_NAME = "example";
+ private static final String BINARY_FIELD_NAME = "and some binary";
+ private static final String NUMBER_FIELD_NAME = "some numbers";
+ private static final String IGNORED_FIELD_NAME = "leave me";
+
+ public static void main(String[] args) throws GeneralSecurityException {
+ final String cmkArn = args[0];
+ final String region = args[1];
+ final String encryptionContextTableName = args[2];
+
+ AmazonDynamoDB ddb = null;
+ AWSKMS kms = null;
+ try {
+ ddb = AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
+ kms = AWSKMSClientBuilder.standard().withRegion(region).build();
+ encryptRecord(cmkArn, encryptionContextTableName, ddb, kms);
+ } finally {
+ if (ddb != null) {
+ ddb.shutdown();
+ }
+ if (kms != null) {
+ kms.shutdown();
+ }
+ }
+ }
+
+ public static void encryptRecord(
+ final String cmkArn,
+ final String newEncryptionContextTableName,
+ AmazonDynamoDB ddbClient,
+ AWSKMS kmsClient)
+ throws GeneralSecurityException {
+ // Sample object to be encrypted
+ ExampleItem record = new ExampleItem();
+ record.setPartitionAttribute("is this");
+ record.setSortAttribute(55);
+ record.setExample("my data");
+
+ // Set up our configuration and clients
+ // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client
+ // `kmsClient`
+ final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn);
+ final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
+
+ Map tableNameEncryptionContextOverrides = new HashMap<>();
+ tableNameEncryptionContextOverrides.put(TABLE_NAME_TO_OVERRIDE, newEncryptionContextTableName);
+ tableNameEncryptionContextOverrides.put(
+ "AnotherExampleTableForEncryptionContextOverrides", "this table doesn't exist");
+
+ // Supply an operator to override the table name used in the encryption context
+ encryptor.setEncryptionContextOverrideOperator(
+ overrideEncryptionContextTableNameUsingMap(tableNameEncryptionContextOverrides));
+
+ // 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)
+ .build();
+ DynamoDBMapper mapper =
+ new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor));
+
+ System.out.println("Plaintext Record: " + record.toString());
+ // Save the record to the DynamoDB table
+ mapper.save(record);
+
+ // Retrieve (and decrypt) it from DynamoDB
+ ExampleItem decrypted_record = mapper.load(ExampleItem.class, "is this", 55);
+ System.out.println("Decrypted Record: " + decrypted_record.toString());
+
+ // The decrypted field matches the original field before encryption
+ assert record.getExample().equals(decrypted_record.getExample());
+
+ // Setup new configuration to decrypt without using an overridden EncryptionContext
+ final Map itemKey = new HashMap<>();
+ itemKey.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this"));
+ itemKey.put(SORT_ATTRIBUTE, new AttributeValue().withN("55"));
+
+ final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN);
+ final EnumSet encryptAndSign =
+ EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
+ final Map encryptedItem =
+ ddbClient.getItem(TABLE_NAME_TO_OVERRIDE, itemKey).getItem();
+ System.out.println("Encrypted Record: " + encryptedItem);
+
+ Map> encryptionFlags = new HashMap<>();
+ encryptionFlags.put(PARTITION_ATTRIBUTE, signOnly);
+ encryptionFlags.put(SORT_ATTRIBUTE, signOnly);
+ encryptionFlags.put(STRING_FIELD_NAME, encryptAndSign);
+
+ final DynamoDBEncryptor encryptorWithoutOverrides = DynamoDBEncryptor.getInstance(cmp);
+
+ // Decrypt the record without using an overridden EncryptionContext
+ Map decrypted_without_override_record =
+ encryptorWithoutOverrides.decryptRecord(
+ encryptedItem,
+ encryptionFlags,
+ new EncryptionContext.Builder()
+ .withHashKeyName(PARTITION_ATTRIBUTE)
+ .withRangeKeyName(SORT_ATTRIBUTE)
+ .withTableName(newEncryptionContextTableName)
+ .build());
+ System.out.printf(
+ "The example item was encrypted using the table name '%s' in the EncryptionContext%n",
+ newEncryptionContextTableName);
+
+ // The decrypted field matches the original field before encryption
+ assert record
+ .getExample()
+ .equals(decrypted_without_override_record.get(STRING_FIELD_NAME).getS());
+ }
+
+ @DynamoDBTable(tableName = TABLE_NAME_TO_OVERRIDE)
+ public static final class ExampleItem {
+ private String partitionAttribute;
+ private int sortAttribute;
+ private String example;
+
+ @DynamoDBHashKey(attributeName = PARTITION_ATTRIBUTE)
+ public String getPartitionAttribute() {
+ return partitionAttribute;
}
- public static void encryptRecord(final String cmkArn,
- final String newEncryptionContextTableName,
- AmazonDynamoDB ddbClient,
- AWSKMS kmsClient) throws GeneralSecurityException {
- // Sample object to be encrypted
- ExampleItem record = new ExampleItem();
- record.setPartitionAttribute("is this");
- record.setSortAttribute(55);
- record.setExample("my data");
-
- // Set up our configuration and clients
- // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client `kmsClient`
- final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kmsClient, cmkArn);
- final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
-
- Map tableNameEncryptionContextOverrides = new HashMap<>();
- tableNameEncryptionContextOverrides.put(TABLE_NAME_TO_OVERRIDE, newEncryptionContextTableName);
- tableNameEncryptionContextOverrides.put("AnotherExampleTableForEncryptionContextOverrides", "this table doesn't exist");
-
- // Supply an operator to override the table name used in the encryption context
- encryptor.setEncryptionContextOverrideOperator(
- overrideEncryptionContextTableNameUsingMap(tableNameEncryptionContextOverrides)
- );
-
- // 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).build();
- DynamoDBMapper mapper = new DynamoDBMapper(ddbClient, mapperConfig, new AttributeEncryptor(encryptor));
-
- System.out.println("Plaintext Record: " + record.toString());
- // Save the record to the DynamoDB table
- mapper.save(record);
-
- // Retrieve (and decrypt) it from DynamoDB
- ExampleItem decrypted_record = mapper.load(ExampleItem.class, "is this", 55);
- System.out.println("Decrypted Record: " + decrypted_record.toString());
-
- // The decrypted field matches the original field before encryption
- assert record.getExample().equals(decrypted_record.getExample());
-
- // Setup new configuration to decrypt without using an overridden EncryptionContext
- final Map itemKey = new HashMap<>();
- itemKey.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this"));
- itemKey.put(SORT_ATTRIBUTE, new AttributeValue().withN("55"));
-
- final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN);
- final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
- final Map encryptedItem = ddbClient.getItem(TABLE_NAME_TO_OVERRIDE, itemKey)
- .getItem();
- System.out.println("Encrypted Record: " + encryptedItem);
-
- Map> encryptionFlags = new HashMap<>();
- encryptionFlags.put(PARTITION_ATTRIBUTE, signOnly);
- encryptionFlags.put(SORT_ATTRIBUTE, signOnly);
- encryptionFlags.put(STRING_FIELD_NAME, encryptAndSign);
-
- final DynamoDBEncryptor encryptorWithoutOverrides = DynamoDBEncryptor.getInstance(cmp);
-
- // Decrypt the record without using an overridden EncryptionContext
- Map decrypted_without_override_record = encryptorWithoutOverrides.decryptRecord(encryptedItem,
- encryptionFlags,
- new EncryptionContext.Builder().withHashKeyName(PARTITION_ATTRIBUTE)
- .withRangeKeyName(SORT_ATTRIBUTE)
- .withTableName(newEncryptionContextTableName)
- .build());
- System.out.printf("The example item was encrypted using the table name '%s' in the EncryptionContext%n", newEncryptionContextTableName);
-
- // The decrypted field matches the original field before encryption
- assert record.getExample().equals(decrypted_without_override_record.get(STRING_FIELD_NAME).getS());
+ public void setPartitionAttribute(String partitionAttribute) {
+ this.partitionAttribute = partitionAttribute;
}
- @DynamoDBTable(tableName = TABLE_NAME_TO_OVERRIDE)
- public static final class ExampleItem {
- private String partitionAttribute;
- private int sortAttribute;
- private String example;
-
- @DynamoDBHashKey(attributeName = PARTITION_ATTRIBUTE)
- public String getPartitionAttribute() {
- return partitionAttribute;
- }
-
- public void setPartitionAttribute(String partitionAttribute) {
- this.partitionAttribute = partitionAttribute;
- }
-
- @DynamoDBRangeKey(attributeName = SORT_ATTRIBUTE)
- public int getSortAttribute() {
- return sortAttribute;
- }
-
- public void setSortAttribute(int sortAttribute) {
- this.sortAttribute = sortAttribute;
- }
-
- @DynamoDBAttribute(attributeName = STRING_FIELD_NAME)
- public String getExample() {
- return example;
- }
-
- public void setExample(String example) {
- this.example = example;
- }
-
- public String toString() {
- return String.format("{partition_attribute: %s, sort_attribute: %s, example: %s}",
- partitionAttribute, sortAttribute, example);
- }
+ @DynamoDBRangeKey(attributeName = SORT_ATTRIBUTE)
+ public int getSortAttribute() {
+ return sortAttribute;
}
+ public void setSortAttribute(int sortAttribute) {
+ this.sortAttribute = sortAttribute;
+ }
+
+ @DynamoDBAttribute(attributeName = STRING_FIELD_NAME)
+ public String getExample() {
+ return example;
+ }
+
+ public void setExample(String example) {
+ this.example = example;
+ }
+
+ public String toString() {
+ return String.format(
+ "{partition_attribute: %s, sort_attribute: %s, example: %s}",
+ partitionAttribute, sortAttribute, example);
+ }
+ }
}
diff --git a/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java
index 5f6e870e..55f8a22d 100644
--- a/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java
+++ b/examples/src/main/java/com/amazonaws/examples/MostRecentEncryptedItem.java
@@ -14,13 +14,6 @@
*/
package com.amazonaws.examples;
-import java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
@@ -33,12 +26,17 @@
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
/**
- * This demonstrates how to use the {@link CachingMostRecentProvider} backed by a
- * {@link MetaStore} and the {@link DirectKmsMaterialProvider} to encrypt
- * your data. Before you can use this, you need to set up a table to hold the
- * intermediate keys or use --setup mode to construct the table once
+ * This demonstrates how to use the {@link CachingMostRecentProvider} backed by a {@link MetaStore}
+ * and the {@link DirectKmsMaterialProvider} to encrypt your data. Before you can use this, you need
+ * to set up a table to hold the intermediate keys or use --setup mode to construct the table once
* and then re-run the example without the --setup mode
*/
public class MostRecentEncryptedItem {
@@ -80,39 +78,54 @@ public static void main(String[] args) throws GeneralSecurityException {
}
}
- public static void encryptRecord(String tableName, String keyTableName, String cmkArn, String materialName,
- AmazonDynamoDB ddbClient, AWSKMS kmsClient) throws GeneralSecurityException {
+ public static void encryptRecord(
+ String tableName,
+ String keyTableName,
+ String cmkArn,
+ String materialName,
+ AmazonDynamoDB ddbClient,
+ AWSKMS kmsClient)
+ throws GeneralSecurityException {
// Sample record to be encrypted
final Map record = new HashMap<>();
record.put(PARTITION_ATTRIBUTE, new AttributeValue().withS("is this"));
record.put(SORT_ATTRIBUTE, new AttributeValue().withN("55"));
record.put(STRING_FIELD_NAME, new AttributeValue().withS("data"));
record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99"));
- record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02})));
- record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute
-
- // Set up our configuration and clients. All of this is thread-safe and can be reused across calls.
+ record.put(
+ BINARY_FIELD_NAME,
+ new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02})));
+ record.put(
+ IGNORED_FIELD_NAME,
+ new AttributeValue().withS("alone")); // We want to ignore this attribute
+
+ // Set up our configuration and clients. All of this is thread-safe and can be reused across
+ // calls.
// Provider Configuration to protect the data keys
- // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client `kmsClient`
+ // This example assumes we already have a DynamoDB client `ddbClient` and AWS KMS client
+ // `kmsClient`
final DirectKmsMaterialProvider kmsProv = new DirectKmsMaterialProvider(kmsClient, cmkArn);
final DynamoDBEncryptor keyEncryptor = DynamoDBEncryptor.getInstance(kmsProv);
final MetaStore metaStore = new MetaStore(ddbClient, keyTableName, keyEncryptor);
- //Provider configuration to protect the data
- final CachingMostRecentProvider cmp = new CachingMostRecentProvider(metaStore, materialName, 60_000);
+ // Provider configuration to protect the data
+ final CachingMostRecentProvider cmp =
+ new CachingMostRecentProvider(metaStore, materialName, 60_000);
// Encryptor creation
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
// Information about the context of our data (normally just Table information)
- final EncryptionContext encryptionContext = new EncryptionContext.Builder()
- .withTableName(tableName)
- .withHashKeyName(PARTITION_ATTRIBUTE)
- .withRangeKeyName(SORT_ATTRIBUTE)
- .build();
+ final EncryptionContext encryptionContext =
+ new EncryptionContext.Builder()
+ .withTableName(tableName)
+ .withHashKeyName(PARTITION_ATTRIBUTE)
+ .withRangeKeyName(SORT_ATTRIBUTE)
+ .build();
// Describe what actions need to be taken for each attribute
final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN);
- final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
+ final EnumSet encryptAndSign =
+ EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
final Map> actions = new HashMap<>();
for (final String attributeName : record.keySet()) {
switch (attributeName) {
@@ -133,13 +146,22 @@ public static void encryptRecord(String tableName, String keyTableName, String c
// End set-up
// Encrypt the plaintext record directly
- final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext);
+ final Map encrypted_record =
+ encryptor.encryptRecord(record, actions, encryptionContext);
// Encrypted record fields change as expected
- assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes
- assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes
- assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
- assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
+ assert encrypted_record.get(STRING_FIELD_NAME).getB()
+ != null; // the encrypted string is stored as bytes
+ assert encrypted_record.get(NUMBER_FIELD_NAME).getB()
+ != null; // the encrypted number is stored as bytes
+ assert !record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
+ assert record
+ .get(IGNORED_FIELD_NAME)
+ .getS()
+ .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
// We could now put the encrypted item to DynamoDB just as we would any other item.
// We're skipping it to to keep the example simpler.
@@ -148,12 +170,22 @@ public static void encryptRecord(String tableName, String keyTableName, String c
System.out.println("Encrypted Record: " + encrypted_record);
// Decryption is identical. We'll pretend that we retrieved the record from DynamoDB.
- final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
+ final Map decrypted_record =
+ encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
System.out.println("Decrypted Record: " + decrypted_record);
// The decrypted fields match the original fields before encryption
- assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS());
- assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
- assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
+ assert record
+ .get(STRING_FIELD_NAME)
+ .getS()
+ .equals(decrypted_record.get(STRING_FIELD_NAME).getS());
+ assert record
+ .get(NUMBER_FIELD_NAME)
+ .getN()
+ .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
+ assert record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
}
}
diff --git a/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java b/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java
index e5f54645..ae2ccb96 100644
--- a/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java
+++ b/examples/src/main/java/com/amazonaws/examples/SymmetricEncryptedItem.java
@@ -14,6 +14,11 @@
*/
package com.amazonaws.examples;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
@@ -21,19 +26,12 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.WrappedMaterialsProvider;
-import com.amazonaws.services.dynamodbv2.model.AttributeValue;
-
/**
- * Example showing use of an AES key for encryption and an HmacSHA256 key for signing.
- * For ease of the example, we create new random ones every time.
+ * Example showing use of an AES key for encryption and an HmacSHA256 key for signing. For ease of
+ * the example, we create new random ones every time.
*/
public class SymmetricEncryptedItem {
@@ -57,7 +55,8 @@ public static void main(String[] args) throws GeneralSecurityException {
encryptRecord(tableName, wrappingKey, signingKey);
}
- public static void encryptRecord(String tableName, SecretKey wrappingKey, SecretKey signingKey) throws GeneralSecurityException {
+ public static void encryptRecord(String tableName, SecretKey wrappingKey, SecretKey signingKey)
+ throws GeneralSecurityException {
// Sample record to be encrypted
final String partitionKeyName = "partition_attribute";
final String sortKeyName = "sort_attribute";
@@ -66,28 +65,38 @@ public static void encryptRecord(String tableName, SecretKey wrappingKey, Secret
record.put(sortKeyName, new AttributeValue().withN("55"));
record.put(STRING_FIELD_NAME, new AttributeValue().withS("data"));
record.put(NUMBER_FIELD_NAME, new AttributeValue().withN("99"));
- record.put(BINARY_FIELD_NAME, new AttributeValue().withB(ByteBuffer.wrap(new byte[]{0x00, 0x01, 0x02})));
- record.put(IGNORED_FIELD_NAME, new AttributeValue().withS("alone")); // We want to ignore this attribute
-
- // Set up our configuration and clients. All of this is thread-safe and can be reused across calls.
+ record.put(
+ BINARY_FIELD_NAME,
+ new AttributeValue().withB(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02})));
+ record.put(
+ IGNORED_FIELD_NAME,
+ new AttributeValue().withS("alone")); // We want to ignore this attribute
+
+ // Set up our configuration and clients. All of this is thread-safe and can be reused across
+ // calls.
// Provider Configuration
- final WrappedMaterialsProvider cmp = new WrappedMaterialsProvider(wrappingKey, wrappingKey, signingKey);
+ final WrappedMaterialsProvider cmp =
+ new WrappedMaterialsProvider(wrappingKey, wrappingKey, signingKey);
// While the wrappedMaterialsProvider is better as it uses a unique encryption key per record,
- // many existing systems use the SymmetricStaticProvider which always uses the same encryption key.
- // final SymmetricStaticProvider cmp = new SymmetricStaticProvider(encryptionKey, signingKey);
+ // many existing systems use the SymmetricStaticProvider which always uses the same encryption
+ // key.
+ // final SymmetricStaticProvider cmp = new SymmetricStaticProvider(encryptionKey,
+ // signingKey);
// Encryptor creation
final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp);
// Information about the context of our data (normally just Table information)
- final EncryptionContext encryptionContext = new EncryptionContext.Builder()
- .withTableName(tableName)
- .withHashKeyName(partitionKeyName)
- .withRangeKeyName(sortKeyName)
- .build();
+ final EncryptionContext encryptionContext =
+ new EncryptionContext.Builder()
+ .withTableName(tableName)
+ .withHashKeyName(partitionKeyName)
+ .withRangeKeyName(sortKeyName)
+ .build();
// Describe what actions need to be taken for each attribute
final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN);
- final EnumSet encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
+ final EnumSet encryptAndSign =
+ EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN);
final Map> actions = new HashMap<>();
for (final String attributeName : record.keySet()) {
switch (attributeName) {
@@ -108,13 +117,22 @@ public static void encryptRecord(String tableName, SecretKey wrappingKey, Secret
// End set-up
// Encrypt the plaintext record directly
- final Map encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext);
+ final Map encrypted_record =
+ encryptor.encryptRecord(record, actions, encryptionContext);
// Encrypted record fields change as expected
- assert encrypted_record.get(STRING_FIELD_NAME).getB() != null; // the encrypted string is stored as bytes
- assert encrypted_record.get(NUMBER_FIELD_NAME).getB() != null; // the encrypted number is stored as bytes
- assert !record.get(BINARY_FIELD_NAME).getB().equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
- assert record.get(IGNORED_FIELD_NAME).getS().equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
+ assert encrypted_record.get(STRING_FIELD_NAME).getB()
+ != null; // the encrypted string is stored as bytes
+ assert encrypted_record.get(NUMBER_FIELD_NAME).getB()
+ != null; // the encrypted number is stored as bytes
+ assert !record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(encrypted_record.get(BINARY_FIELD_NAME).getB()); // the encrypted bytes have updated
+ assert record
+ .get(IGNORED_FIELD_NAME)
+ .getS()
+ .equals(encrypted_record.get(IGNORED_FIELD_NAME).getS()); // ignored field is left as is
// We could now put the encrypted item to DynamoDB just as we would any other item.
// We're skipping it to to keep the example simpler.
@@ -123,12 +141,22 @@ public static void encryptRecord(String tableName, SecretKey wrappingKey, Secret
System.out.println("Encrypted Record: " + encrypted_record);
// Decryption is identical. We'll pretend that we retrieved the record from DynamoDB.
- final Map decrypted_record = encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
+ final Map decrypted_record =
+ encryptor.decryptRecord(encrypted_record, actions, encryptionContext);
System.out.println("Decrypted Record: " + decrypted_record);
// The decrypted fields match the original fields before encryption
- assert record.get(STRING_FIELD_NAME).getS().equals(decrypted_record.get(STRING_FIELD_NAME).getS());
- assert record.get(NUMBER_FIELD_NAME).getN().equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
- assert record.get(BINARY_FIELD_NAME).getB().equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
+ assert record
+ .get(STRING_FIELD_NAME)
+ .getS()
+ .equals(decrypted_record.get(STRING_FIELD_NAME).getS());
+ assert record
+ .get(NUMBER_FIELD_NAME)
+ .getN()
+ .equals(decrypted_record.get(NUMBER_FIELD_NAME).getN());
+ assert record
+ .get(BINARY_FIELD_NAME)
+ .getB()
+ .equals(decrypted_record.get(BINARY_FIELD_NAME).getB());
}
}
diff --git a/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java b/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java
index 3ead80f5..31ee3506 100644
--- a/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java
+++ b/examples/src/test/java/com/amazonaws/examples/AsymmetricEncryptedItemTest.java
@@ -3,22 +3,21 @@
package com.amazonaws.examples;
-import org.testng.annotations.Test;
-
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import org.testng.annotations.Test;
public class AsymmetricEncryptedItemTest {
- private static final String TABLE_NAME = "java-ddbec-test-table-asym-example";
+ private static final String TABLE_NAME = "java-ddbec-test-table-asym-example";
- @Test
- public void testEncryptAndDecrypt() throws GeneralSecurityException {
- final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
- keyGen.initialize(2048);
- final KeyPair wrappingKeys = keyGen.generateKeyPair();
- final KeyPair signingKeys = keyGen.generateKeyPair();
+ @Test
+ public void testEncryptAndDecrypt() throws GeneralSecurityException {
+ final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ final KeyPair wrappingKeys = keyGen.generateKeyPair();
+ final KeyPair signingKeys = keyGen.generateKeyPair();
- AsymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKeys, signingKeys);
- }
+ AsymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKeys, signingKeys);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java
index 43dc5acf..537e664e 100644
--- a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java
+++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedItemIT.java
@@ -3,21 +3,20 @@
package com.amazonaws.examples;
+import static com.amazonaws.examples.TestUtils.US_WEST_2;
+import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID;
+
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
-import org.testng.annotations.Test;
-
import java.security.GeneralSecurityException;
-
-import static com.amazonaws.examples.TestUtils.US_WEST_2;
-import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID;
+import org.testng.annotations.Test;
public class AwsKmsEncryptedItemIT {
- private static final String TABLE_NAME = "java-ddbec-test-table-kms-item-example";
+ private static final String TABLE_NAME = "java-ddbec-test-table-kms-item-example";
- @Test
- public void testEncryptAndDecrypt() throws GeneralSecurityException {
- final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
- AwsKmsEncryptedItem.encryptRecord(TABLE_NAME, US_WEST_2_KEY_ID, kms);
- }
+ @Test
+ public void testEncryptAndDecrypt() throws GeneralSecurityException {
+ final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
+ AwsKmsEncryptedItem.encryptRecord(TABLE_NAME, US_WEST_2_KEY_ID, kms);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java
index c2e31831..4dc2c884 100644
--- a/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java
+++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsEncryptedObjectIT.java
@@ -3,12 +3,6 @@
package com.amazonaws.examples;
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
-import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
-import com.amazonaws.services.kms.AWSKMS;
-import com.amazonaws.services.kms.AWSKMSClientBuilder;
-import org.testng.annotations.Test;
-
import static com.amazonaws.examples.AwsKmsEncryptedObject.EXAMPLE_TABLE_NAME;
import static com.amazonaws.examples.AwsKmsEncryptedObject.PARTITION_ATTRIBUTE;
import static com.amazonaws.examples.AwsKmsEncryptedObject.SORT_ATTRIBUTE;
@@ -16,16 +10,22 @@
import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID;
import static com.amazonaws.examples.TestUtils.createDDBTable;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
+import com.amazonaws.services.kms.AWSKMS;
+import com.amazonaws.services.kms.AWSKMSClientBuilder;
+import org.testng.annotations.Test;
+
public class AwsKmsEncryptedObjectIT {
- @Test
- public void testEncryptAndDecrypt() {
- final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
- final AmazonDynamoDB ddb = DynamoDBEmbedded.create();
+ @Test
+ public void testEncryptAndDecrypt() {
+ final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
+ final AmazonDynamoDB ddb = DynamoDBEmbedded.create();
- // Create the table under test
- createDDBTable(ddb, EXAMPLE_TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE);
+ // Create the table under test
+ createDDBTable(ddb, EXAMPLE_TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE);
- AwsKmsEncryptedObject.encryptRecord(US_WEST_2_KEY_ID, ddb, kms);
- }
+ AwsKmsEncryptedObject.encryptRecord(US_WEST_2_KEY_ID, ddb, kms);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java
index 28ba4f34..664b7bce 100644
--- a/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java
+++ b/examples/src/test/java/com/amazonaws/examples/AwsKmsMultiRegionKeyIT.java
@@ -3,18 +3,17 @@
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;
+import java.security.GeneralSecurityException;
+import org.testng.annotations.Test;
+
public class AwsKmsMultiRegionKeyIT {
- private static final String TABLE_NAME = "ddbec-mrk-testing";
+ 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);
- }
+ @Test
+ public void testEncryptAndDecrypt() throws GeneralSecurityException {
+ AwsKmsMultiRegionKey.encryptRecord(TABLE_NAME, US_EAST_1_MRK_KEY_ID, US_WEST_2_MRK_KEY_ID);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java b/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java
index fa32dd88..4dbc30b6 100644
--- a/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java
+++ b/examples/src/test/java/com/amazonaws/examples/EncryptionContextOverridesWithDynamoDBMapperIT.java
@@ -3,15 +3,6 @@
package com.amazonaws.examples;
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
-import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
-
-import com.amazonaws.services.kms.AWSKMS;
-import com.amazonaws.services.kms.AWSKMSClientBuilder;
-import org.testng.annotations.Test;
-
-import java.security.GeneralSecurityException;
-
import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.PARTITION_ATTRIBUTE;
import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.SORT_ATTRIBUTE;
import static com.amazonaws.examples.EncryptionContextOverridesWithDynamoDBMapper.TABLE_NAME_TO_OVERRIDE;
@@ -19,17 +10,25 @@
import static com.amazonaws.examples.TestUtils.US_WEST_2_KEY_ID;
import static com.amazonaws.examples.TestUtils.createDDBTable;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
+import com.amazonaws.services.kms.AWSKMS;
+import com.amazonaws.services.kms.AWSKMSClientBuilder;
+import java.security.GeneralSecurityException;
+import org.testng.annotations.Test;
+
public class EncryptionContextOverridesWithDynamoDBMapperIT {
- private static final String OVERRIDE_TABLE_NAME = "java-ddbec-test-table-encctx-override-example";
+ private static final String OVERRIDE_TABLE_NAME = "java-ddbec-test-table-encctx-override-example";
- @Test
- public void testEncryptAndDecrypt() throws GeneralSecurityException {
- final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
- final AmazonDynamoDB ddb = DynamoDBEmbedded.create();
+ @Test
+ public void testEncryptAndDecrypt() throws GeneralSecurityException {
+ final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
+ final AmazonDynamoDB ddb = DynamoDBEmbedded.create();
- // Create the table under test
- createDDBTable(ddb, TABLE_NAME_TO_OVERRIDE, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE);
+ // Create the table under test
+ createDDBTable(ddb, TABLE_NAME_TO_OVERRIDE, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE);
- EncryptionContextOverridesWithDynamoDBMapper.encryptRecord(US_WEST_2_KEY_ID, OVERRIDE_TABLE_NAME, ddb, kms);
- }
+ EncryptionContextOverridesWithDynamoDBMapper.encryptRecord(
+ US_WEST_2_KEY_ID, OVERRIDE_TABLE_NAME, ddb, kms);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java b/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java
index 420f0a7b..3c5e1f55 100644
--- a/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java
+++ b/examples/src/test/java/com/amazonaws/examples/MostRecentEncryptedItemIT.java
@@ -3,36 +3,36 @@
package com.amazonaws.examples;
+import static com.amazonaws.examples.MostRecentEncryptedItem.PARTITION_ATTRIBUTE;
+import static com.amazonaws.examples.MostRecentEncryptedItem.SORT_ATTRIBUTE;
+import static com.amazonaws.examples.TestUtils.*;
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store.MetaStore;
import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
-import org.testng.annotations.Test;
-
import java.security.GeneralSecurityException;
-
-import static com.amazonaws.examples.MostRecentEncryptedItem.PARTITION_ATTRIBUTE;
-import static com.amazonaws.examples.MostRecentEncryptedItem.SORT_ATTRIBUTE;
-import static com.amazonaws.examples.TestUtils.*;
+import org.testng.annotations.Test;
public class MostRecentEncryptedItemIT {
- private static final String TABLE_NAME = "java-ddbec-test-table-mostrecent-example";
- private static final String KEY_TABLE_NAME = "java-ddbec-test-table-mostrecent-example-keys";
- private static final String MATERIAL_NAME = "testMaterial";
+ private static final String TABLE_NAME = "java-ddbec-test-table-mostrecent-example";
+ private static final String KEY_TABLE_NAME = "java-ddbec-test-table-mostrecent-example-keys";
+ private static final String MATERIAL_NAME = "testMaterial";
- @Test
- public void testEncryptAndDecrypt() throws GeneralSecurityException {
- final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
- final AmazonDynamoDB ddb = DynamoDBEmbedded.create();
+ @Test
+ public void testEncryptAndDecrypt() throws GeneralSecurityException {
+ final AWSKMS kms = AWSKMSClientBuilder.standard().withRegion(US_WEST_2).build();
+ final AmazonDynamoDB ddb = DynamoDBEmbedded.create();
- // Create the key table under test
- MetaStore.createTable(ddb, KEY_TABLE_NAME, new ProvisionedThroughput(1L, 1L));
+ // Create the key table under test
+ MetaStore.createTable(ddb, KEY_TABLE_NAME, new ProvisionedThroughput(1L, 1L));
- // Create the table under test
- createDDBTable(ddb, TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE);
+ // Create the table under test
+ createDDBTable(ddb, TABLE_NAME, PARTITION_ATTRIBUTE, SORT_ATTRIBUTE);
- MostRecentEncryptedItem.encryptRecord(TABLE_NAME, KEY_TABLE_NAME, US_WEST_2_KEY_ID, MATERIAL_NAME, ddb, kms);
- }
+ MostRecentEncryptedItem.encryptRecord(
+ TABLE_NAME, KEY_TABLE_NAME, US_WEST_2_KEY_ID, MATERIAL_NAME, ddb, kms);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java b/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java
index 54c14353..acf54d6c 100644
--- a/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java
+++ b/examples/src/test/java/com/amazonaws/examples/SymmetricEncryptedItemTest.java
@@ -3,26 +3,25 @@
package com.amazonaws.examples;
-import org.testng.annotations.Test;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import org.testng.annotations.Test;
public class SymmetricEncryptedItemTest {
- private static final String TABLE_NAME = "java-ddbec-test-table-sym-example";
+ private static final String TABLE_NAME = "java-ddbec-test-table-sym-example";
- @Test
- public void testEncryptAndDecrypt() throws GeneralSecurityException {
- final SecureRandom secureRandom = new SecureRandom();
- byte[] rawAes = new byte[32];
- byte[] rawHmac = new byte[32];
- secureRandom.nextBytes(rawAes);
- secureRandom.nextBytes(rawHmac);
- final SecretKey wrappingKey = new SecretKeySpec(rawAes, "AES");
- final SecretKey signingKey = new SecretKeySpec(rawHmac, "HmacSHA256");
+ @Test
+ public void testEncryptAndDecrypt() throws GeneralSecurityException {
+ final SecureRandom secureRandom = new SecureRandom();
+ byte[] rawAes = new byte[32];
+ byte[] rawHmac = new byte[32];
+ secureRandom.nextBytes(rawAes);
+ secureRandom.nextBytes(rawHmac);
+ final SecretKey wrappingKey = new SecretKeySpec(rawAes, "AES");
+ final SecretKey signingKey = new SecretKeySpec(rawHmac, "HmacSHA256");
- SymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKey, signingKey);
- }
+ SymmetricEncryptedItem.encryptRecord(TABLE_NAME, wrappingKey, signingKey);
+ }
}
diff --git a/examples/src/test/java/com/amazonaws/examples/TestUtils.java b/examples/src/test/java/com/amazonaws/examples/TestUtils.java
index 349e1a41..abc43913 100644
--- a/examples/src/test/java/com/amazonaws/examples/TestUtils.java
+++ b/examples/src/test/java/com/amazonaws/examples/TestUtils.java
@@ -1,43 +1,55 @@
package com.amazonaws.examples;
+import static com.amazonaws.examples.AwsKmsEncryptedObject.*;
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.model.*;
-
import java.util.ArrayList;
-import static com.amazonaws.examples.AwsKmsEncryptedObject.*;
-
public class TestUtils {
- private TestUtils() {
- throw new UnsupportedOperationException(
- "This class exists to hold static resources and cannot be instantiated."
- );
- }
-
- /**
- * These special test keys have been configured to allow Encrypt, Decrypt, and GenerateDataKey operations from any
- * AWS principal and should be used when adding new KMS tests.
- *
- * This should go without saying, but never use these keys for production purposes (as anyone in the world can
- * decrypt data encrypted using them).
- */
- 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 attrDef = new ArrayList();
- attrDef.add(new AttributeDefinition().withAttributeName(partitionName).withAttributeType(ScalarAttributeType.S));
- attrDef.add(new AttributeDefinition().withAttributeName(sortName).withAttributeType(ScalarAttributeType.N));
-
- ArrayList keySchema = new ArrayList();
- keySchema.add(new KeySchemaElement().withAttributeName(partitionName).withKeyType(KeyType.HASH));
- keySchema.add(new KeySchemaElement().withAttributeName(sortName).withKeyType(KeyType.RANGE));
-
- ddb.createTable(new CreateTableRequest().withTableName(tableName)
- .withAttributeDefinitions(attrDef)
- .withKeySchema(keySchema)
- .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L)));
- }
+ private TestUtils() {
+ throw new UnsupportedOperationException(
+ "This class exists to hold static resources and cannot be instantiated.");
+ }
+
+ /**
+ * These special test keys have been configured to allow Encrypt, Decrypt, and GenerateDataKey
+ * operations from any AWS principal and should be used when adding new KMS tests.
+ *
+ * This should go without saying, but never use these keys for production purposes (as anyone
+ * in the world can decrypt data encrypted using them).
+ */
+ 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 attrDef = new ArrayList();
+ attrDef.add(
+ new AttributeDefinition()
+ .withAttributeName(partitionName)
+ .withAttributeType(ScalarAttributeType.S));
+ attrDef.add(
+ new AttributeDefinition()
+ .withAttributeName(sortName)
+ .withAttributeType(ScalarAttributeType.N));
+
+ ArrayList keySchema = new ArrayList();
+ keySchema.add(
+ new KeySchemaElement().withAttributeName(partitionName).withKeyType(KeyType.HASH));
+ keySchema.add(new KeySchemaElement().withAttributeName(sortName).withKeyType(KeyType.RANGE));
+
+ ddb.createTable(
+ new CreateTableRequest()
+ .withTableName(tableName)
+ .withAttributeDefinitions(attrDef)
+ .withKeySchema(keySchema)
+ .withProvisionedThroughput(new ProvisionedThroughput(100L, 100L)));
+ }
}
diff --git a/pom.xml b/pom.xml
index 238301fb..1ce12fac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,6 +53,16 @@
true
+
+
+ com.coveo
+ fmt-maven-plugin
+ 2.10
+
+
+
+
+
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java
index 1727a140..12d8a34f 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/AttributeEncryptor.java
@@ -14,13 +14,6 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.SaveBehavior;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingsRegistry.Mapping;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingsRegistry.Mappings;
@@ -33,259 +26,260 @@
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.TableAadOverride;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
- * Encrypts all non-key fields prior to storing them in DynamoDB.
- * This must be used with {@link SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}.
+ * Encrypts all non-key fields prior to storing them in DynamoDB. This must be used with {@link
+ * SaveBehavior#PUT} or {@link SaveBehavior#CLOBBER}.
*
- * For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
*
- * @author Greg Rubin
+ * @author Greg Rubin
*/
public class AttributeEncryptor implements AttributeTransformer {
- private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class);
- private final DynamoDBEncryptor encryptor;
- private final Map, ModelClassMetadata> metadataCache = new ConcurrentHashMap<>();
-
- public AttributeEncryptor(final DynamoDBEncryptor encryptor) {
- this.encryptor = encryptor;
+ private static final Log LOG = LogFactory.getLog(AttributeEncryptor.class);
+ private final DynamoDBEncryptor encryptor;
+ private final Map, ModelClassMetadata> metadataCache = new ConcurrentHashMap<>();
+
+ public AttributeEncryptor(final DynamoDBEncryptor encryptor) {
+ this.encryptor = encryptor;
+ }
+
+ public AttributeEncryptor(final EncryptionMaterialsProvider encryptionMaterialsProvider) {
+ encryptor = DynamoDBEncryptor.getInstance(encryptionMaterialsProvider);
+ }
+
+ public DynamoDBEncryptor getEncryptor() {
+ return encryptor;
+ }
+
+ @Override
+ public Map transform(final Parameters> parameters) {
+ // one map of attributeFlags per model class
+ final ModelClassMetadata metadata = getModelClassMetadata(parameters);
+
+ final Map attributeValues = parameters.getAttributeValues();
+ // If this class is marked as "DoNotTouch" then we know our encryptor will not change it at all
+ // so we may as well fast-return and do nothing. This also avoids emitting errors when they
+ // would not apply.
+ if (metadata.doNotTouch) {
+ return attributeValues;
}
- public AttributeEncryptor(final EncryptionMaterialsProvider encryptionMaterialsProvider) {
- encryptor = DynamoDBEncryptor.getInstance(encryptionMaterialsProvider);
+ // When AttributeEncryptor is used without SaveBehavior.PUT or CLOBBER, it is trying to
+ // transform only a subset
+ // of the actual fields stored in DynamoDB. This means that the generated signature will not
+ // cover any
+ // unmodified fields. Thus, upon untransform, the signature verification will fail as it won't
+ // cover all
+ // expected fields.
+ if (parameters.isPartialUpdate()) {
+ throw new DynamoDBMappingException(
+ "Use of AttributeEncryptor without SaveBehavior.PUT or SaveBehavior.CLOBBER is an error "
+ + "and can result in data-corruption. This occured while trying to save "
+ + parameters.getModelClass());
}
- public DynamoDBEncryptor getEncryptor() {
- return encryptor;
+ try {
+ return encryptor.encryptRecord(
+ attributeValues, metadata.getEncryptionFlags(), paramsToContext(parameters));
+ } catch (Exception ex) {
+ throw new DynamoDBMappingException(ex);
}
+ }
- @Override
- public Map transform(final Parameters> parameters) {
- // one map of attributeFlags per model class
- final ModelClassMetadata metadata = getModelClassMetadata(parameters);
-
- final Map attributeValues = parameters.getAttributeValues();
- // If this class is marked as "DoNotTouch" then we know our encryptor will not change it at all
- // so we may as well fast-return and do nothing. This also avoids emitting errors when they would not apply.
- if (metadata.doNotTouch) {
- return attributeValues;
- }
-
- // When AttributeEncryptor is used without SaveBehavior.PUT or CLOBBER, it is trying to transform only a subset
- // of the actual fields stored in DynamoDB. This means that the generated signature will not cover any
- // unmodified fields. Thus, upon untransform, the signature verification will fail as it won't cover all
- // expected fields.
- if (parameters.isPartialUpdate()) {
- throw new DynamoDBMappingException(
- "Use of AttributeEncryptor without SaveBehavior.PUT or SaveBehavior.CLOBBER is an error " +
- "and can result in data-corruption. This occured while trying to save " +
- parameters.getModelClass());
- }
+ @Override
+ public Map untransform(final Parameters> parameters) {
+ final Map> attributeFlags = getEncryptionFlags(parameters);
- try {
- return encryptor.encryptRecord(
- attributeValues,
- metadata.getEncryptionFlags(),
- paramsToContext(parameters));
- } catch (Exception ex) {
- throw new DynamoDBMappingException(ex);
- }
+ try {
+ return encryptor.decryptRecord(
+ parameters.getAttributeValues(), attributeFlags, paramsToContext(parameters));
+ } catch (Exception ex) {
+ throw new DynamoDBMappingException(ex);
}
-
- @Override
- public Map untransform(final Parameters> parameters) {
- final Map> attributeFlags = getEncryptionFlags(parameters);
-
- try {
- return encryptor.decryptRecord(
- parameters.getAttributeValues(),
- attributeFlags,
- paramsToContext(parameters));
- } catch (Exception ex) {
- throw new DynamoDBMappingException(ex);
- }
+ }
+
+ /*
+ * For any attributes we see from DynamoDB that aren't modeled in the mapper class,
+ * we either ignore them (the default behavior), or include them for encryption/signing
+ * based on the presence of the @HandleUnknownAttributes annotation (unless the class
+ * has @DoNotTouch, then we don't include them).
+ */
+ private Map> getEncryptionFlags(final Parameters> parameters) {
+ final ModelClassMetadata metadata = getModelClassMetadata(parameters);
+
+ // If the class is annotated with @DoNotTouch, then none of the attributes are
+ // encrypted or signed, so we don't need to bother looking for unknown attributes.
+ if (metadata.getDoNotTouch()) {
+ return metadata.getEncryptionFlags();
}
- /*
- * For any attributes we see from DynamoDB that aren't modeled in the mapper class,
- * we either ignore them (the default behavior), or include them for encryption/signing
- * based on the presence of the @HandleUnknownAttributes annotation (unless the class
- * has @DoNotTouch, then we don't include them).
- */
- private Map> getEncryptionFlags(final Parameters> parameters) {
- final ModelClassMetadata metadata = getModelClassMetadata(parameters);
-
- // If the class is annotated with @DoNotTouch, then none of the attributes are
- // encrypted or signed, so we don't need to bother looking for unknown attributes.
- if (metadata.getDoNotTouch()) {
- return metadata.getEncryptionFlags();
- }
+ final Set unknownAttributeBehavior = metadata.getUnknownAttributeBehavior();
+ final Map> attributeFlags = new HashMap<>();
+ attributeFlags.putAll(metadata.getEncryptionFlags());
- final Set unknownAttributeBehavior = metadata.getUnknownAttributeBehavior();
- final Map> attributeFlags = new HashMap<>();
- attributeFlags.putAll(metadata.getEncryptionFlags());
-
- for (final String attributeName : parameters.getAttributeValues().keySet()) {
- if (!attributeFlags.containsKey(attributeName) &&
- !encryptor.getSignatureFieldName().equals(attributeName) &&
- !encryptor.getMaterialDescriptionFieldName().equals(attributeName)) {
+ for (final String attributeName : parameters.getAttributeValues().keySet()) {
+ if (!attributeFlags.containsKey(attributeName)
+ && !encryptor.getSignatureFieldName().equals(attributeName)
+ && !encryptor.getMaterialDescriptionFieldName().equals(attributeName)) {
- attributeFlags.put(attributeName, unknownAttributeBehavior);
- }
- }
-
- return attributeFlags;
+ attributeFlags.put(attributeName, unknownAttributeBehavior);
+ }
}
- private ModelClassMetadata getModelClassMetadata(Parameters parameters) {
- // Due to the lack of explicit synchronization, it is possible that
- // elements in the cache will be added multiple times. Since they will
- // all be identical, this is okay. Avoiding explicit synchronization
- // means that in the general (retrieval) case, should never block and
- // should be extremely fast.
- final Class clazz = parameters.getModelClass();
- ModelClassMetadata metadata = metadataCache.get(clazz);
-
- if (metadata == null) {
- Map> attributeFlags = new HashMap<>();
-
- final boolean handleUnknownAttributes = handleUnknownAttributes(clazz);
- final EnumSet unknownAttributeBehavior = EnumSet.noneOf(EncryptionFlags.class);
-
- if (shouldTouch(clazz)) {
- Mappings mappings = DynamoDBMappingsRegistry.instance().mappingsOf(clazz);
-
- for (Mapping mapping : mappings.getMappings()) {
- final EnumSet flags = EnumSet.noneOf(EncryptionFlags.class);
- StandardAnnotationMaps.FieldMap> fieldMap = StandardAnnotationMaps.of(mapping.getter(), null);
- if (shouldTouch(fieldMap)) {
- if (shouldEncryptAttribute(clazz, mapping, fieldMap)) {
- flags.add(EncryptionFlags.ENCRYPT);
- }
- flags.add(EncryptionFlags.SIGN);
- }
- attributeFlags.put(mapping.getAttributeName(), Collections.unmodifiableSet(flags));
- }
-
- if (handleUnknownAttributes) {
- unknownAttributeBehavior.add(EncryptionFlags.SIGN);
-
- if (shouldEncrypt(clazz)) {
- unknownAttributeBehavior.add(EncryptionFlags.ENCRYPT);
- }
- }
+ return attributeFlags;
+ }
+
+ private ModelClassMetadata getModelClassMetadata(Parameters parameters) {
+ // Due to the lack of explicit synchronization, it is possible that
+ // elements in the cache will be added multiple times. Since they will
+ // all be identical, this is okay. Avoiding explicit synchronization
+ // means that in the general (retrieval) case, should never block and
+ // should be extremely fast.
+ final Class clazz = parameters.getModelClass();
+ ModelClassMetadata metadata = metadataCache.get(clazz);
+
+ if (metadata == null) {
+ Map> attributeFlags = new HashMap<>();
+
+ final boolean handleUnknownAttributes = handleUnknownAttributes(clazz);
+ final EnumSet unknownAttributeBehavior =
+ EnumSet.noneOf(EncryptionFlags.class);
+
+ if (shouldTouch(clazz)) {
+ Mappings mappings = DynamoDBMappingsRegistry.instance().mappingsOf(clazz);
+
+ for (Mapping mapping : mappings.getMappings()) {
+ final EnumSet flags = EnumSet.noneOf(EncryptionFlags.class);
+ StandardAnnotationMaps.FieldMap> fieldMap =
+ StandardAnnotationMaps.of(mapping.getter(), null);
+ if (shouldTouch(fieldMap)) {
+ if (shouldEncryptAttribute(clazz, mapping, fieldMap)) {
+ flags.add(EncryptionFlags.ENCRYPT);
}
-
- metadata = new ModelClassMetadata(Collections.unmodifiableMap(attributeFlags), doNotTouch(clazz),
- Collections.unmodifiableSet(unknownAttributeBehavior));
- metadataCache.put(clazz, metadata);
+ flags.add(EncryptionFlags.SIGN);
+ }
+ attributeFlags.put(mapping.getAttributeName(), Collections.unmodifiableSet(flags));
}
- return metadata;
- }
-
- /**
- * @return True if {@link DoNotTouch} is not present on the class level. False otherwise
- */
- private boolean shouldTouch(Class> clazz) {
- return !doNotTouch(clazz);
- }
-
- /**
- * @return True if {@link DoNotTouch} is not present on the getter level. False otherwise.
- */
- private boolean shouldTouch(StandardAnnotationMaps.FieldMap> fieldMap) {
- return !doNotTouch(fieldMap);
- }
-
- /**
- * @return True if {@link DoNotTouch} IS present on the class level. False otherwise.
- */
- private boolean doNotTouch(Class> clazz) {
- return clazz.isAnnotationPresent(DoNotTouch.class);
- }
-
- /**
- * @return True if {@link DoNotTouch} IS present on the getter level. False otherwise.
- */
- private boolean doNotTouch(StandardAnnotationMaps.FieldMap> fieldMap) {
- return fieldMap.actualOf(DoNotTouch.class) != null;
- }
-
- /**
- * @return True if {@link DoNotEncrypt} is NOT present on the class level. False otherwise.
- */
- private boolean shouldEncrypt(Class> clazz) {
- return !doNotEncrypt(clazz);
- }
- /**
- * @return True if {@link DoNotEncrypt} IS present on the class level. False otherwise.
- */
- private boolean doNotEncrypt(Class> clazz) {
- return clazz.isAnnotationPresent(DoNotEncrypt.class);
- }
+ if (handleUnknownAttributes) {
+ unknownAttributeBehavior.add(EncryptionFlags.SIGN);
- /**
- * @return True if {@link DoNotEncrypt} IS present on the getter level. False otherwise.
- */
- private boolean doNotEncrypt(StandardAnnotationMaps.FieldMap> fieldMap) {
- return fieldMap.actualOf(DoNotEncrypt.class) != null;
+ if (shouldEncrypt(clazz)) {
+ unknownAttributeBehavior.add(EncryptionFlags.ENCRYPT);
+ }
+ }
+ }
+
+ metadata =
+ new ModelClassMetadata(
+ Collections.unmodifiableMap(attributeFlags),
+ doNotTouch(clazz),
+ Collections.unmodifiableSet(unknownAttributeBehavior));
+ metadataCache.put(clazz, metadata);
}
-
- /**
- * @return True if the attribute should be encrypted, false otherwise.
- */
- private boolean shouldEncryptAttribute(
- final Class> clazz,
- final Mapping mapping,
- final StandardAnnotationMaps.FieldMap> fieldMap) {
-
- return !(doNotEncrypt(clazz) || doNotEncrypt(fieldMap) || mapping.isPrimaryKey() || mapping.isVersion());
+ return metadata;
+ }
+
+ /** @return True if {@link DoNotTouch} is not present on the class level. False otherwise */
+ private boolean shouldTouch(Class> clazz) {
+ return !doNotTouch(clazz);
+ }
+
+ /** @return True if {@link DoNotTouch} is not present on the getter level. False otherwise. */
+ private boolean shouldTouch(StandardAnnotationMaps.FieldMap> fieldMap) {
+ return !doNotTouch(fieldMap);
+ }
+
+ /** @return True if {@link DoNotTouch} IS present on the class level. False otherwise. */
+ private boolean doNotTouch(Class> clazz) {
+ return clazz.isAnnotationPresent(DoNotTouch.class);
+ }
+
+ /** @return True if {@link DoNotTouch} IS present on the getter level. False otherwise. */
+ private boolean doNotTouch(StandardAnnotationMaps.FieldMap> fieldMap) {
+ return fieldMap.actualOf(DoNotTouch.class) != null;
+ }
+
+ /** @return True if {@link DoNotEncrypt} is NOT present on the class level. False otherwise. */
+ private boolean shouldEncrypt(Class> clazz) {
+ return !doNotEncrypt(clazz);
+ }
+
+ /** @return True if {@link DoNotEncrypt} IS present on the class level. False otherwise. */
+ private boolean doNotEncrypt(Class> clazz) {
+ return clazz.isAnnotationPresent(DoNotEncrypt.class);
+ }
+
+ /** @return True if {@link DoNotEncrypt} IS present on the getter level. False otherwise. */
+ private boolean doNotEncrypt(StandardAnnotationMaps.FieldMap> fieldMap) {
+ return fieldMap.actualOf(DoNotEncrypt.class) != null;
+ }
+
+ /** @return True if the attribute should be encrypted, false otherwise. */
+ private boolean shouldEncryptAttribute(
+ final Class> clazz,
+ final Mapping mapping,
+ final StandardAnnotationMaps.FieldMap> fieldMap) {
+
+ return !(doNotEncrypt(clazz)
+ || doNotEncrypt(fieldMap)
+ || mapping.isPrimaryKey()
+ || mapping.isVersion());
+ }
+
+ private static EncryptionContext paramsToContext(Parameters> params) {
+ final Class> clazz = params.getModelClass();
+ final TableAadOverride override = clazz.getAnnotation(TableAadOverride.class);
+ final String tableName = ((override == null) ? params.getTableName() : override.tableName());
+
+ return new EncryptionContext.Builder()
+ .withHashKeyName(params.getHashKeyName())
+ .withRangeKeyName(params.getRangeKeyName())
+ .withTableName(tableName)
+ .withModeledClass(params.getModelClass())
+ .withAttributeValues(params.getAttributeValues())
+ .build();
+ }
+
+ private boolean handleUnknownAttributes(Class> clazz) {
+ return clazz.getAnnotation(HandleUnknownAttributes.class) != null;
+ }
+
+ private static class ModelClassMetadata {
+ private final Map> encryptionFlags;
+ private final boolean doNotTouch;
+ private final Set unknownAttributeBehavior;
+
+ public ModelClassMetadata(
+ Map> encryptionFlags,
+ boolean doNotTouch,
+ Set unknownAttributeBehavior) {
+ this.encryptionFlags = encryptionFlags;
+ this.doNotTouch = doNotTouch;
+ this.unknownAttributeBehavior = unknownAttributeBehavior;
}
- private static EncryptionContext paramsToContext(Parameters> params) {
- final Class> clazz = params.getModelClass();
- final TableAadOverride override = clazz.getAnnotation(TableAadOverride.class);
- final String tableName = ((override == null) ? params.getTableName() : override.tableName());
-
- return new EncryptionContext.Builder()
- .withHashKeyName(params.getHashKeyName())
- .withRangeKeyName(params.getRangeKeyName())
- .withTableName(tableName)
- .withModeledClass(params.getModelClass())
- .withAttributeValues(params.getAttributeValues()).build();
+ public Map> getEncryptionFlags() {
+ return encryptionFlags;
}
- private boolean handleUnknownAttributes(Class> clazz) {
- return clazz.getAnnotation(HandleUnknownAttributes.class) != null;
+ public boolean getDoNotTouch() {
+ return doNotTouch;
}
- private static class ModelClassMetadata {
- private final Map> encryptionFlags;
- private final boolean doNotTouch;
- private final Set unknownAttributeBehavior;
-
- public ModelClassMetadata(Map> encryptionFlags,
- boolean doNotTouch, Set unknownAttributeBehavior) {
- this.encryptionFlags = encryptionFlags;
- this.doNotTouch = doNotTouch;
- this.unknownAttributeBehavior = unknownAttributeBehavior;
- }
-
- public Map> getEncryptionFlags() {
- return encryptionFlags;
- }
-
- public boolean getDoNotTouch() {
- return doNotTouch;
- }
-
- public Set getUnknownAttributeBehavior() {
- return unknownAttributeBehavior;
- }
+ public Set getUnknownAttributeBehavior() {
+ return unknownAttributeBehavior;
}
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java
index e42e6e97..5445030f 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DelegatedKey.java
@@ -19,7 +19,6 @@
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
-
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
@@ -27,120 +26,104 @@
import javax.crypto.SecretKey;
/**
- * Identifies keys which should not be used directly with {@link Cipher} but
- * instead contain their own cryptographic logic. This can be used to wrap more
- * complex logic, HSM integration, or service-calls.
- *
- *
- * Most delegated keys will only support a subset of these operations. (For
- * example, AES keys will generally not support {@link #sign(byte[], String)} or
- * {@link #verify(byte[], byte[], String)} and HMAC keys will generally not
- * support anything except sign
and verify
.)
- * {@link UnsupportedOperationException} should be thrown in these cases.
- *
- * @author Greg Rubin
+ * Identifies keys which should not be used directly with {@link Cipher} but instead contain their
+ * own cryptographic logic. This can be used to wrap more complex logic, HSM integration, or
+ * service-calls.
+ *
+ *
Most delegated keys will only support a subset of these operations. (For example, AES keys
+ * will generally not support {@link #sign(byte[], String)} or {@link #verify(byte[], byte[],
+ * String)} and HMAC keys will generally not support anything except sign
and
+ * verify
.) {@link UnsupportedOperationException} should be thrown in these cases.
+ *
+ * @author Greg Rubin
*/
public interface DelegatedKey extends SecretKey {
- /**
- * Encrypts the provided plaintext and returns a byte-array containing the ciphertext.
- *
- * @param plainText
- * @param additionalAssociatedData
- * Optional additional data which must then also be provided for successful
- * decryption. Both null
and arrays of length 0 are treated identically.
- * Not all keys will support this parameter.
- * @param algorithm
- * the transformation to be used when encrypting the data
- * @return ciphertext the ciphertext produced by this encryption operation
- * @throws UnsupportedOperationException
- * if encryption is not supported or if additionalAssociatedData
is
- * provided, but not supported.
- */
- public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm)
- throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException,
- NoSuchPaddingException;
+ /**
+ * Encrypts the provided plaintext and returns a byte-array containing the ciphertext.
+ *
+ * @param plainText
+ * @param additionalAssociatedData Optional additional data which must then also be provided for
+ * successful decryption. Both null
and arrays of length 0 are treated
+ * identically. Not all keys will support this parameter.
+ * @param algorithm the transformation to be used when encrypting the data
+ * @return ciphertext the ciphertext produced by this encryption operation
+ * @throws UnsupportedOperationException if encryption is not supported or if
+ * additionalAssociatedData
is provided, but not supported.
+ */
+ public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm)
+ throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
+ NoSuchAlgorithmException, NoSuchPaddingException;
- /**
- * Decrypts the provided ciphertext and returns a byte-array containing the
- * plaintext.
- *
- * @param cipherText
- * @param additionalAssociatedData
- * Optional additional data which was provided during encryption.
- * Both null
and arrays of length 0 are treated
- * identically. Not all keys will support this parameter.
- * @param algorithm
- * the transformation to be used when decrypting the data
- * @return plaintext the result of decrypting the input ciphertext
- * @throws UnsupportedOperationException
- * if decryption is not supported or if
- * additionalAssociatedData
is provided, but not
- * supported.
- */
- public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm)
- throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException,
- NoSuchPaddingException, InvalidAlgorithmParameterException;
+ /**
+ * Decrypts the provided ciphertext and returns a byte-array containing the plaintext.
+ *
+ * @param cipherText
+ * @param additionalAssociatedData Optional additional data which was provided during encryption.
+ * Both null
and arrays of length 0 are treated identically. Not all keys will
+ * support this parameter.
+ * @param algorithm the transformation to be used when decrypting the data
+ * @return plaintext the result of decrypting the input ciphertext
+ * @throws UnsupportedOperationException if decryption is not supported or if
+ * additionalAssociatedData
is provided, but not supported.
+ */
+ public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm)
+ throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
+ NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException;
- /**
- * Wraps (encrypts) the provided key
to make it safe for
- * storage or transmission.
- *
- * @param key
- * @param additionalAssociatedData
- * Optional additional data which must then also be provided for
- * successful unwrapping. Both null
and arrays of
- * length 0 are treated identically. Not all keys will support
- * this parameter.
- * @param algorithm
- * the transformation to be used when wrapping the key
- * @return the wrapped key
- * @throws UnsupportedOperationException
- * if wrapping is not supported or if
- * additionalAssociatedData
is provided, but not
- * supported.
- */
- public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) throws InvalidKeyException,
- NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException;
+ /**
+ * Wraps (encrypts) the provided key
to make it safe for storage or transmission.
+ *
+ * @param key
+ * @param additionalAssociatedData Optional additional data which must then also be provided for
+ * successful unwrapping. Both null
and arrays of length 0 are treated
+ * identically. Not all keys will support this parameter.
+ * @param algorithm the transformation to be used when wrapping the key
+ * @return the wrapped key
+ * @throws UnsupportedOperationException if wrapping is not supported or if
+ * additionalAssociatedData
is provided, but not supported.
+ */
+ public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm)
+ throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
+ IllegalBlockSizeException;
- /**
- * Unwraps (decrypts) the provided wrappedKey
to recover the
- * original key.
- *
- * @param wrappedKey
- * @param additionalAssociatedData
- * Optional additional data which was provided during wrapping.
- * Both null
and arrays of length 0 are treated
- * identically. Not all keys will support this parameter.
- * @param algorithm
- * the transformation to be used when unwrapping the key
- * @return the unwrapped key
- * @throws UnsupportedOperationException
- * if wrapping is not supported or if
- * additionalAssociatedData
is provided, but not
- * supported.
- */
- public Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType,
- byte[] additionalAssociatedData, String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException,
- InvalidKeyException;
+ /**
+ * Unwraps (decrypts) the provided wrappedKey
to recover the original key.
+ *
+ * @param wrappedKey
+ * @param additionalAssociatedData Optional additional data which was provided during wrapping.
+ * Both null
and arrays of length 0 are treated identically. Not all keys will
+ * support this parameter.
+ * @param algorithm the transformation to be used when unwrapping the key
+ * @return the unwrapped key
+ * @throws UnsupportedOperationException if wrapping is not supported or if
+ * additionalAssociatedData
is provided, but not supported.
+ */
+ public Key unwrap(
+ byte[] wrappedKey,
+ String wrappedKeyAlgorithm,
+ int wrappedKeyType,
+ byte[] additionalAssociatedData,
+ String algorithm)
+ throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException;
- /**
- * Calculates and returns a signature for dataToSign
.
- *
- * @param dataToSign
- * @param algorithm
- * @return the signature
- * @throws UnsupportedOperationException if signing is not supported
- */
- public byte[] sign(byte[] dataToSign, String algorithm) throws GeneralSecurityException;
+ /**
+ * Calculates and returns a signature for dataToSign
.
+ *
+ * @param dataToSign
+ * @param algorithm
+ * @return the signature
+ * @throws UnsupportedOperationException if signing is not supported
+ */
+ public byte[] sign(byte[] dataToSign, String algorithm) throws GeneralSecurityException;
- /**
- * Checks the provided signature for correctness.
- *
- * @param dataToSign
- * @param signature
- * @param algorithm
- * @return true if and only if the signature
matches the dataToSign
.
- * @throws UnsupportedOperationException if signature validation is not supported
- */
- public boolean verify(byte[] dataToSign, byte[] signature, String algorithm);
+ /**
+ * Checks the provided signature for correctness.
+ *
+ * @param dataToSign
+ * @param signature
+ * @param algorithm
+ * @return true if and only if the signature
matches the dataToSign
.
+ * @throws UnsupportedOperationException if signature validation is not supported
+ */
+ public boolean verify(byte[] dataToSign, byte[] signature, String algorithm);
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java
index fcf067a8..45f49096 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotEncrypt.java
@@ -14,25 +14,22 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB;
-
/**
* Prevents the associated item (class or attribute) from being encrypted.
*
- *
For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
*
- * @author Greg Rubin
+ * @author Greg Rubin
*/
@DynamoDB
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
-public @interface DoNotEncrypt {
-
-}
+public @interface DoNotEncrypt {}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java
index ee2be7ec..6988ed1f 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DoNotTouch.java
@@ -14,25 +14,22 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDB;
-
/**
* Prevents the associated item from being encrypted or signed.
*
- *
For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
- *
- * @author Greg Rubin
+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
+ *
+ * @author Greg Rubin
*/
@DynamoDB
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
-public @interface DoNotTouch {
-
-}
+public @interface DoNotTouch {}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java
index 678b0e40..5aa5a718 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java
@@ -14,6 +14,14 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption;
+import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials;
+import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider;
+import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller;
+import com.amazonaws.services.dynamodbv2.datamodeling.internal.ByteBufferInputStream;
+import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -33,581 +41,582 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
-
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
-import com.amazonaws.services.dynamodbv2.datamodeling.AttributeEncryptor;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials;
-import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider;
-import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller;
-import com.amazonaws.services.dynamodbv2.datamodeling.internal.ByteBufferInputStream;
-import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils;
-import com.amazonaws.services.dynamodbv2.model.AttributeValue;
-
/**
- * The low-level API used by {@link AttributeEncryptor} to perform crypto
- * operations on the record attributes.
+ * The low-level API used by {@link AttributeEncryptor} to perform crypto operations on the record
+ * attributes.
*
- *
For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
*
- * @author Greg Rubin
+ * @author Greg Rubin
*/
public class DynamoDBEncryptor {
- private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA";
- private static final String DEFAULT_METADATA_FIELD = "*amzn-ddb-map-desc*";
- private static final String DEFAULT_SIGNATURE_FIELD = "*amzn-ddb-map-sig*";
- private static final String DEFAULT_DESCRIPTION_BASE = "amzn-ddb-map-"; // Same as the Mapper
- private static final Charset UTF8 = Charset.forName("UTF-8");
- private static final String SYMMETRIC_ENCRYPTION_MODE = "/CBC/PKCS5Padding";
- private static final ConcurrentHashMap BLOCK_SIZE_CACHE = new ConcurrentHashMap<>();
- private static final Function BLOCK_SIZE_CALCULATOR = (transformation) -> {
+ private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA";
+ private static final String DEFAULT_METADATA_FIELD = "*amzn-ddb-map-desc*";
+ private static final String DEFAULT_SIGNATURE_FIELD = "*amzn-ddb-map-sig*";
+ private static final String DEFAULT_DESCRIPTION_BASE = "amzn-ddb-map-"; // Same as the Mapper
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final String SYMMETRIC_ENCRYPTION_MODE = "/CBC/PKCS5Padding";
+ private static final ConcurrentHashMap BLOCK_SIZE_CACHE =
+ new ConcurrentHashMap<>();
+ private static final Function BLOCK_SIZE_CALCULATOR =
+ (transformation) -> {
try {
- final Cipher c = Cipher.getInstance(transformation);
- return c.getBlockSize();
+ final Cipher c = Cipher.getInstance(transformation);
+ return c.getBlockSize();
} catch (final GeneralSecurityException ex) {
- throw new IllegalArgumentException("Algorithm does not exist", ex);
+ throw new IllegalArgumentException("Algorithm does not exist", ex);
}
- };
-
- private static final int CURRENT_VERSION = 0;
-
- private String signatureFieldName = DEFAULT_SIGNATURE_FIELD;
- private String materialDescriptionFieldName = DEFAULT_METADATA_FIELD;
-
- private EncryptionMaterialsProvider encryptionMaterialsProvider;
- private final String descriptionBase;
- private final String symmetricEncryptionModeHeader;
- private final String signingAlgorithmHeader;
-
- public static final String DEFAULT_SIGNING_ALGORITHM_HEADER = DEFAULT_DESCRIPTION_BASE + "signingAlg";
- private Function encryptionContextOverrideOperator;
-
- protected DynamoDBEncryptor(EncryptionMaterialsProvider provider, String descriptionBase) {
- this.encryptionMaterialsProvider = provider;
- this.descriptionBase = descriptionBase;
- symmetricEncryptionModeHeader = this.descriptionBase + "sym-mode";
- signingAlgorithmHeader = this.descriptionBase + "signingAlg";
- }
-
- public static DynamoDBEncryptor getInstance(EncryptionMaterialsProvider provider, String descriptionbase) {
- return new DynamoDBEncryptor(provider, descriptionbase);
- }
-
- public static DynamoDBEncryptor getInstance(EncryptionMaterialsProvider provider) {
- return getInstance(provider, DEFAULT_DESCRIPTION_BASE);
- }
-
- /**
- * Returns a decrypted version of the provided DynamoDb record. The signature is verified across
- * all provided fields. All fields (except those listed in doNotEncrypt
are
- * decrypted.
- *
- * @param itemAttributes
- * the DynamoDbRecord
- * @param context
- * additional information used to successfully select the encryption materials and
- * decrypt the data. This should include (at least) the tableName and the
- * materialDescription.
- * @param doNotDecrypt
- * those fields which should not be encrypted
- * @return a plaintext version of the DynamoDb record
- * @throws SignatureException
- * if the signature is invalid or cannot be verified
- * @throws GeneralSecurityException
- */
- public Map decryptAllFieldsExcept(Map itemAttributes,
- EncryptionContext context, String... doNotDecrypt) throws GeneralSecurityException {
- return decryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotDecrypt));
- }
-
- /**
- * @see #decryptAllFieldsExcept(Map, EncryptionContext, String...)
- */
- public Map decryptAllFieldsExcept(
- Map itemAttributes,
- EncryptionContext context, Collection doNotDecrypt)
- throws GeneralSecurityException {
- Map> attributeFlags = allDecryptionFlagsExcept(
- itemAttributes, doNotDecrypt);
- return decryptRecord(itemAttributes, attributeFlags, context);
+ };
+
+ private static final int CURRENT_VERSION = 0;
+
+ private String signatureFieldName = DEFAULT_SIGNATURE_FIELD;
+ private String materialDescriptionFieldName = DEFAULT_METADATA_FIELD;
+
+ private EncryptionMaterialsProvider encryptionMaterialsProvider;
+ private final String descriptionBase;
+ private final String symmetricEncryptionModeHeader;
+ private final String signingAlgorithmHeader;
+
+ public static final String DEFAULT_SIGNING_ALGORITHM_HEADER =
+ DEFAULT_DESCRIPTION_BASE + "signingAlg";
+ private Function encryptionContextOverrideOperator;
+
+ protected DynamoDBEncryptor(EncryptionMaterialsProvider provider, String descriptionBase) {
+ this.encryptionMaterialsProvider = provider;
+ this.descriptionBase = descriptionBase;
+ symmetricEncryptionModeHeader = this.descriptionBase + "sym-mode";
+ signingAlgorithmHeader = this.descriptionBase + "signingAlg";
+ }
+
+ public static DynamoDBEncryptor getInstance(
+ EncryptionMaterialsProvider provider, String descriptionbase) {
+ return new DynamoDBEncryptor(provider, descriptionbase);
+ }
+
+ public static DynamoDBEncryptor getInstance(EncryptionMaterialsProvider provider) {
+ return getInstance(provider, DEFAULT_DESCRIPTION_BASE);
+ }
+
+ /**
+ * Returns a decrypted version of the provided DynamoDb record. The signature is verified across
+ * all provided fields. All fields (except those listed in doNotEncrypt
are
+ * decrypted.
+ *
+ * @param itemAttributes the DynamoDbRecord
+ * @param context additional information used to successfully select the encryption materials and
+ * decrypt the data. This should include (at least) the tableName and the materialDescription.
+ * @param doNotDecrypt those fields which should not be encrypted
+ * @return a plaintext version of the DynamoDb record
+ * @throws SignatureException if the signature is invalid or cannot be verified
+ * @throws GeneralSecurityException
+ */
+ public Map decryptAllFieldsExcept(
+ Map itemAttributes, EncryptionContext context, String... doNotDecrypt)
+ throws GeneralSecurityException {
+ return decryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotDecrypt));
+ }
+
+ /** @see #decryptAllFieldsExcept(Map, EncryptionContext, String...) */
+ public Map decryptAllFieldsExcept(
+ Map itemAttributes,
+ EncryptionContext context,
+ Collection doNotDecrypt)
+ throws GeneralSecurityException {
+ Map> attributeFlags =
+ allDecryptionFlagsExcept(itemAttributes, doNotDecrypt);
+ return decryptRecord(itemAttributes, attributeFlags, context);
+ }
+
+ /**
+ * Returns the decryption flags for all item attributes except for those explicitly specified to
+ * be excluded.
+ *
+ * @param doNotDecrypt fields to be excluded
+ */
+ public Map> allDecryptionFlagsExcept(
+ Map itemAttributes, String... doNotDecrypt) {
+ return allDecryptionFlagsExcept(itemAttributes, Arrays.asList(doNotDecrypt));
+ }
+
+ /**
+ * Returns the decryption flags for all item attributes except for those explicitly specified to
+ * be excluded.
+ *
+ * @param doNotDecrypt fields to be excluded
+ */
+ public Map> allDecryptionFlagsExcept(
+ Map itemAttributes, Collection doNotDecrypt) {
+ Map> attributeFlags = new HashMap>();
+
+ for (String fieldName : doNotDecrypt) {
+ attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN));
}
- /**
- * Returns the decryption flags for all item attributes except for those
- * explicitly specified to be excluded.
- * @param doNotDecrypt fields to be excluded
- */
- public Map> allDecryptionFlagsExcept(
- Map itemAttributes,
- String ... doNotDecrypt) {
- return allDecryptionFlagsExcept(itemAttributes, Arrays.asList(doNotDecrypt));
+ for (String fieldName : itemAttributes.keySet()) {
+ if (!attributeFlags.containsKey(fieldName)
+ && !fieldName.equals(getMaterialDescriptionFieldName())
+ && !fieldName.equals(getSignatureFieldName())) {
+ attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN));
+ }
}
-
- /**
- * Returns the decryption flags for all item attributes except for those
- * explicitly specified to be excluded.
- * @param doNotDecrypt fields to be excluded
- */
- public Map> allDecryptionFlagsExcept(
- Map itemAttributes,
- Collection doNotDecrypt) {
- Map> attributeFlags = new HashMap>();
-
- for (String fieldName : doNotDecrypt) {
- attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN));
- }
-
- for (String fieldName : itemAttributes.keySet()) {
- if (!attributeFlags.containsKey(fieldName) &&
- !fieldName.equals(getMaterialDescriptionFieldName()) &&
- !fieldName.equals(getSignatureFieldName())) {
- attributeFlags.put(fieldName,
- EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN));
- }
- }
- return attributeFlags;
+ return attributeFlags;
+ }
+
+ /**
+ * Returns an encrypted version of the provided DynamoDb record. All fields are signed. All fields
+ * (except those listed in doNotEncrypt
) are encrypted.
+ *
+ * @param itemAttributes a DynamoDb Record
+ * @param context additional information used to successfully select the encryption materials and
+ * encrypt the data. This should include (at least) the tableName.
+ * @param doNotEncrypt those fields which should not be encrypted
+ * @return a ciphertext version of the DynamoDb record
+ * @throws GeneralSecurityException
+ */
+ public Map encryptAllFieldsExcept(
+ Map itemAttributes, EncryptionContext context, String... doNotEncrypt)
+ throws GeneralSecurityException {
+
+ return encryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotEncrypt));
+ }
+
+ public Map encryptAllFieldsExcept(
+ Map itemAttributes,
+ EncryptionContext context,
+ Collection doNotEncrypt)
+ throws GeneralSecurityException {
+ Map> attributeFlags =
+ allEncryptionFlagsExcept(itemAttributes, doNotEncrypt);
+ return encryptRecord(itemAttributes, attributeFlags, context);
+ }
+
+ /**
+ * Returns the encryption flags for all item attributes except for those explicitly specified to
+ * be excluded.
+ *
+ * @param doNotEncrypt fields to be excluded
+ */
+ public Map> allEncryptionFlagsExcept(
+ Map itemAttributes, String... doNotEncrypt) {
+ return allEncryptionFlagsExcept(itemAttributes, Arrays.asList(doNotEncrypt));
+ }
+
+ /**
+ * Returns the encryption flags for all item attributes except for those explicitly specified to
+ * be excluded.
+ *
+ * @param doNotEncrypt fields to be excluded
+ */
+ public Map> allEncryptionFlagsExcept(
+ Map itemAttributes, Collection doNotEncrypt) {
+ Map> attributeFlags = new HashMap>();
+ for (String fieldName : doNotEncrypt) {
+ attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN));
}
-
- /**
- * Returns an encrypted version of the provided DynamoDb record. All fields are signed. All fields
- * (except those listed in doNotEncrypt
) are encrypted.
- * @param itemAttributes a DynamoDb Record
- * @param context
- * additional information used to successfully select the encryption materials and
- * encrypt the data. This should include (at least) the tableName.
- * @param doNotEncrypt those fields which should not be encrypted
- * @return a ciphertext version of the DynamoDb record
- * @throws GeneralSecurityException
- */
- public Map encryptAllFieldsExcept(Map itemAttributes,
- EncryptionContext context, String... doNotEncrypt) throws GeneralSecurityException {
-
- return encryptAllFieldsExcept(itemAttributes, context, Arrays.asList(doNotEncrypt));
+
+ for (String fieldName : itemAttributes.keySet()) {
+ if (!attributeFlags.containsKey(fieldName)) {
+ attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN));
+ }
}
-
- public Map encryptAllFieldsExcept(
- Map itemAttributes,
- EncryptionContext context,
- Collection doNotEncrypt)
- throws GeneralSecurityException {
- Map> attributeFlags = allEncryptionFlagsExcept(
- itemAttributes, doNotEncrypt);
- return encryptRecord(itemAttributes, attributeFlags, context);
+ return attributeFlags;
+ }
+
+ public Map decryptRecord(
+ Map itemAttributes,
+ Map> attributeFlags,
+ EncryptionContext context)
+ throws GeneralSecurityException {
+ if (attributeFlags.isEmpty()) {
+ return itemAttributes;
}
+ // Copy to avoid changing anyone elses objects
+ itemAttributes = new HashMap(itemAttributes);
- /**
- * Returns the encryption flags for all item attributes except for those
- * explicitly specified to be excluded.
- * @param doNotEncrypt fields to be excluded
- */
- public Map> allEncryptionFlagsExcept(
- Map itemAttributes,
- String ...doNotEncrypt) {
- return allEncryptionFlagsExcept(itemAttributes, Arrays.asList(doNotEncrypt));
- }
+ Map materialDescription = Collections.emptyMap();
+ DecryptionMaterials materials;
+ SecretKey decryptionKey;
- /**
- * Returns the encryption flags for all item attributes except for those
- * explicitly specified to be excluded.
- * @param doNotEncrypt fields to be excluded
- */
- public Map> allEncryptionFlagsExcept(
- Map itemAttributes,
- Collection doNotEncrypt) {
- Map> attributeFlags =
- new HashMap>();
- for (String fieldName : doNotEncrypt) {
- attributeFlags.put(fieldName, EnumSet.of(EncryptionFlags.SIGN));
- }
+ DynamoDBSigner signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng());
- for (String fieldName : itemAttributes.keySet()) {
- if (!attributeFlags.containsKey(fieldName)) {
- attributeFlags.put(fieldName,
- EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN));
- }
- }
- return attributeFlags;
+ if (itemAttributes.containsKey(materialDescriptionFieldName)) {
+ materialDescription = unmarshallDescription(itemAttributes.get(materialDescriptionFieldName));
}
-
- public Map decryptRecord(
- Map itemAttributes,
- Map> attributeFlags,
- EncryptionContext context) throws GeneralSecurityException {
- if (attributeFlags.isEmpty()) {
- return itemAttributes;
- }
- // Copy to avoid changing anyone elses objects
- itemAttributes = new HashMap(itemAttributes);
-
- Map materialDescription = Collections.emptyMap();
- DecryptionMaterials materials;
- SecretKey decryptionKey;
-
- DynamoDBSigner signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng());
-
- if (itemAttributes.containsKey(materialDescriptionFieldName)) {
- materialDescription = unmarshallDescription(itemAttributes.get(materialDescriptionFieldName));
- }
- // Copy the material description and attribute values into the context
- context = new EncryptionContext.Builder(context)
+ // Copy the material description and attribute values into the context
+ context =
+ new EncryptionContext.Builder(context)
.withMaterialDescription(materialDescription)
.withAttributeValues(itemAttributes)
.build();
- Function encryptionContextOverrideOperator = getEncryptionContextOverrideOperator();
- if (encryptionContextOverrideOperator != null) {
- context = encryptionContextOverrideOperator.apply(context);
- }
-
- materials = encryptionMaterialsProvider.getDecryptionMaterials(context);
- decryptionKey = materials.getDecryptionKey();
- if (materialDescription.containsKey(signingAlgorithmHeader)) {
- String signingAlg = materialDescription.get(signingAlgorithmHeader);
- signer = DynamoDBSigner.getInstance(signingAlg, Utils.getRng());
- }
-
- ByteBuffer signature;
- if (!itemAttributes.containsKey(signatureFieldName) || itemAttributes.get(signatureFieldName).getB() == null) {
- signature = ByteBuffer.allocate(0);
- } else {
- signature = itemAttributes.get(signatureFieldName).getB().asReadOnlyBuffer();
- }
- itemAttributes.remove(signatureFieldName);
-
- String associatedData = "TABLE>" + context.getTableName() + " encryptionContextOverrideOperator =
+ getEncryptionContextOverrideOperator();
+ if (encryptionContextOverrideOperator != null) {
+ context = encryptionContextOverrideOperator.apply(context);
}
- /**
- * Returns the encrypted (and signed) record, which is a map of item
- * attributes. There is no side effect on the input parameters upon calling
- * this method.
- *
- * @param itemAttributes
- * the input record
- * @param attributeFlags
- * the corresponding encryption flags
- * @param context
- * encryption context
- * @return a new instance of item attributes encrypted as necessary
- * @throws GeneralSecurityException
- * if failed to encrypt the record
- */
- public Map encryptRecord(
- Map itemAttributes,
- Map> attributeFlags,
- EncryptionContext context) throws GeneralSecurityException {
- if (attributeFlags.isEmpty()) {
- return itemAttributes;
- }
- // Copy to avoid changing anyone elses objects
- itemAttributes = new HashMap(itemAttributes);
-
- // Copy the attribute values into the context
- context = new EncryptionContext.Builder(context)
- .withAttributeValues(itemAttributes)
- .build();
-
- Function encryptionContextOverrideOperator =
- getEncryptionContextOverrideOperator();
- if (encryptionContextOverrideOperator != null) {
- context = encryptionContextOverrideOperator.apply(context);
- }
-
- EncryptionMaterials materials = encryptionMaterialsProvider.getEncryptionMaterials(context);
- // We need to copy this because we modify it to record other encryption details
- Map materialDescription = new HashMap(
- materials.getMaterialDescription());
- SecretKey encryptionKey = materials.getEncryptionKey();
-
- actualEncryption(itemAttributes, attributeFlags, materialDescription, encryptionKey);
-
- // The description must be stored after encryption because its data
- // is necessary for proper decryption.
- final String signingAlgo = materialDescription.get(signingAlgorithmHeader);
- DynamoDBSigner signer;
- if (signingAlgo != null) {
- signer = DynamoDBSigner.getInstance(signingAlgo, Utils.getRng());
- } else {
- signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng());
- }
-
- if (materials.getSigningKey() instanceof PrivateKey ) {
- materialDescription.put(signingAlgorithmHeader, signer.getSigningAlgorithm());
- }
- if (!materialDescription.isEmpty()) {
- itemAttributes.put(materialDescriptionFieldName, marshallDescription(materialDescription));
- }
-
- String associatedData = "TABLE>" + context.getTableName() + " itemAttributes,
- Map> attributeFlags, SecretKey encryptionKey,
- Map materialDescription) throws GeneralSecurityException {
- final String encryptionMode = encryptionKey != null ? encryptionKey.getAlgorithm() +
- materialDescription.get(symmetricEncryptionModeHeader) : null;
- Cipher cipher = null;
- int blockSize = -1;
-
- for (Map.Entry entry: itemAttributes.entrySet()) {
- Set flags = attributeFlags.get(entry.getKey());
- if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) {
- if (!flags.contains(EncryptionFlags.SIGN)) {
- throw new IllegalArgumentException("All encrypted fields must be signed. Bad field: " + entry.getKey());
- }
- ByteBuffer plainText;
- ByteBuffer cipherText = entry.getValue().getB().asReadOnlyBuffer();
- cipherText.rewind();
- if (encryptionKey instanceof DelegatedKey) {
- plainText = ByteBuffer.wrap(((DelegatedKey)encryptionKey).decrypt(toByteArray(cipherText), null, encryptionMode));
- } else {
- if (cipher == null) {
- blockSize = getBlockSize(encryptionMode);
- cipher = Cipher.getInstance(encryptionMode);
- }
- byte[] iv = new byte[blockSize];
- cipherText.get(iv);
- cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv), Utils.getRng());
- plainText = ByteBuffer.allocate(cipher.getOutputSize(cipherText.remaining()));
- cipher.doFinal(cipherText, plainText);
- plainText.rewind();
- }
- entry.setValue(AttributeValueMarshaller.unmarshall(plainText));
- }
- }
+ itemAttributes.remove(signatureFieldName);
+
+ String associatedData = "TABLE>" + context.getTableName() + " encryptRecord(
+ Map itemAttributes,
+ Map> attributeFlags,
+ EncryptionContext context)
+ throws GeneralSecurityException {
+ if (attributeFlags.isEmpty()) {
+ return itemAttributes;
}
+ // Copy to avoid changing anyone elses objects
+ itemAttributes = new HashMap(itemAttributes);
- protected static int getBlockSize(final String encryptionMode) {
- return BLOCK_SIZE_CACHE.computeIfAbsent(encryptionMode, BLOCK_SIZE_CALCULATOR);
- }
+ // Copy the attribute values into the context
+ context = new EncryptionContext.Builder(context).withAttributeValues(itemAttributes).build();
- /**
- * This method has the side effect of replacing the plaintext
- * attribute-values of "itemAttributes" with ciphertext attribute-values
- * (which are always in the form of ByteBuffer) as per the corresponding
- * attribute flags.
- */
- private void actualEncryption(Map itemAttributes,
- Map> attributeFlags,
- Map materialDescription,
- SecretKey encryptionKey) throws GeneralSecurityException {
- String encryptionMode = null;
- if (encryptionKey != null) {
- materialDescription.put(this.symmetricEncryptionModeHeader,
- SYMMETRIC_ENCRYPTION_MODE);
- encryptionMode = encryptionKey.getAlgorithm() + SYMMETRIC_ENCRYPTION_MODE;
- }
- Cipher cipher = null;
- int blockSize = -1;
-
- for (Map.Entry entry: itemAttributes.entrySet()) {
- Set flags = attributeFlags.get(entry.getKey());
- if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) {
- if (!flags.contains(EncryptionFlags.SIGN)) {
- throw new IllegalArgumentException("All encrypted fields must be signed. Bad field: " + entry.getKey());
- }
- ByteBuffer plainText = AttributeValueMarshaller.marshall(entry.getValue());
- plainText.rewind();
- ByteBuffer cipherText;
- if (encryptionKey instanceof DelegatedKey) {
- DelegatedKey dk = (DelegatedKey) encryptionKey;
- cipherText = ByteBuffer.wrap(
- dk.encrypt(toByteArray(plainText), null, encryptionMode));
- } else {
- if (cipher == null) {
- blockSize = getBlockSize(encryptionMode);
- cipher = Cipher.getInstance(encryptionMode);
- }
- // Encryption format:
- // Note a unique iv is generated per attribute
- cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, Utils.getRng());
- cipherText = ByteBuffer.allocate(blockSize + cipher.getOutputSize(plainText.remaining()));
- cipherText.position(blockSize);
- cipher.doFinal(plainText, cipherText);
- cipherText.flip();
- final byte[] iv = cipher.getIV();
- if (iv.length != blockSize) {
- throw new IllegalStateException(String.format("Generated IV length (%d) not equal to block size (%d)",
- iv.length, blockSize));
- }
- cipherText.put(iv);
- cipherText.rewind();
- }
- // Replace the plaintext attribute value with the encrypted content
- entry.setValue(new AttributeValue().withB(cipherText));
- }
- }
- }
-
- /**
- * Get the name of the DynamoDB field used to store the signature.
- * Defaults to {@link #DEFAULT_SIGNATURE_FIELD}.
- *
- * @return the name of the DynamoDB field used to store the signature
- */
- public String getSignatureFieldName() {
- return signatureFieldName;
+ Function encryptionContextOverrideOperator =
+ getEncryptionContextOverrideOperator();
+ if (encryptionContextOverrideOperator != null) {
+ context = encryptionContextOverrideOperator.apply(context);
}
- /**
- * Set the name of the DynamoDB field used to store the signature.
- *
- * @param signatureFieldName
- */
- public void setSignatureFieldName(final String signatureFieldName) {
- this.signatureFieldName = signatureFieldName;
+ EncryptionMaterials materials = encryptionMaterialsProvider.getEncryptionMaterials(context);
+ // We need to copy this because we modify it to record other encryption details
+ Map materialDescription =
+ new HashMap(materials.getMaterialDescription());
+ SecretKey encryptionKey = materials.getEncryptionKey();
+
+ actualEncryption(itemAttributes, attributeFlags, materialDescription, encryptionKey);
+
+ // The description must be stored after encryption because its data
+ // is necessary for proper decryption.
+ final String signingAlgo = materialDescription.get(signingAlgorithmHeader);
+ DynamoDBSigner signer;
+ if (signingAlgo != null) {
+ signer = DynamoDBSigner.getInstance(signingAlgo, Utils.getRng());
+ } else {
+ signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng());
}
- /**
- * Get the name of the DynamoDB field used to store metadata used by the
- * DynamoDBEncryptedMapper. Defaults to {@link #DEFAULT_METADATA_FIELD}.
- *
- * @return the name of the DynamoDB field used to store metadata used by the
- * DynamoDBEncryptedMapper
- */
- public String getMaterialDescriptionFieldName() {
- return materialDescriptionFieldName;
+ if (materials.getSigningKey() instanceof PrivateKey) {
+ materialDescription.put(signingAlgorithmHeader, signer.getSigningAlgorithm());
}
-
- /**
- * Set the name of the DynamoDB field used to store metadata used by the
- * DynamoDBEncryptedMapper
- *
- * @param materialDescriptionFieldName
- */
- public void setMaterialDescriptionFieldName(final String materialDescriptionFieldName) {
- this.materialDescriptionFieldName = materialDescriptionFieldName;
+ if (!materialDescription.isEmpty()) {
+ itemAttributes.put(materialDescriptionFieldName, marshallDescription(materialDescription));
}
-
- /**
- * Marshalls the description
into a ByteBuffer by outputting
- * each key (modified UTF-8) followed by its value (also in modified UTF-8).
- *
- * @param description
- * @return the description encoded as an AttributeValue with a ByteBuffer value
- * @see java.io.DataOutput#writeUTF(String)
- */
- protected static AttributeValue marshallDescription(Map description) {
- try {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- DataOutputStream out = new DataOutputStream(bos);
- out.writeInt(CURRENT_VERSION);
- for (Map.Entry entry : description.entrySet()) {
- byte[] bytes = entry.getKey().getBytes(UTF8);
- out.writeInt(bytes.length);
- out.write(bytes);
- bytes = entry.getValue().getBytes(UTF8);
- out.writeInt(bytes.length);
- out.write(bytes);
- }
- out.close();
- AttributeValue result = new AttributeValue();
- result.setB(ByteBuffer.wrap(bos.toByteArray()));
- return result;
- } catch (IOException ex) {
- // Due to the objects in use, an IOException is not possible.
- throw new RuntimeException("Unexpected exception", ex);
+
+ String associatedData = "TABLE>" + context.getTableName() + " itemAttributes,
+ Map> attributeFlags,
+ SecretKey encryptionKey,
+ Map materialDescription)
+ throws GeneralSecurityException {
+ final String encryptionMode =
+ encryptionKey != null
+ ? encryptionKey.getAlgorithm() + materialDescription.get(symmetricEncryptionModeHeader)
+ : null;
+ Cipher cipher = null;
+ int blockSize = -1;
+
+ for (Map.Entry entry : itemAttributes.entrySet()) {
+ Set flags = attributeFlags.get(entry.getKey());
+ if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) {
+ if (!flags.contains(EncryptionFlags.SIGN)) {
+ throw new IllegalArgumentException(
+ "All encrypted fields must be signed. Bad field: " + entry.getKey());
}
+ ByteBuffer plainText;
+ ByteBuffer cipherText = entry.getValue().getB().asReadOnlyBuffer();
+ cipherText.rewind();
+ if (encryptionKey instanceof DelegatedKey) {
+ plainText =
+ ByteBuffer.wrap(
+ ((DelegatedKey) encryptionKey)
+ .decrypt(toByteArray(cipherText), null, encryptionMode));
+ } else {
+ if (cipher == null) {
+ blockSize = getBlockSize(encryptionMode);
+ cipher = Cipher.getInstance(encryptionMode);
+ }
+ byte[] iv = new byte[blockSize];
+ cipherText.get(iv);
+ cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv), Utils.getRng());
+ plainText = ByteBuffer.allocate(cipher.getOutputSize(cipherText.remaining()));
+ cipher.doFinal(cipherText, plainText);
+ plainText.rewind();
+ }
+ entry.setValue(AttributeValueMarshaller.unmarshall(plainText));
+ }
}
-
- public String getSigningAlgorithmHeader() {
- return signingAlgorithmHeader;
+ }
+
+ protected static int getBlockSize(final String encryptionMode) {
+ return BLOCK_SIZE_CACHE.computeIfAbsent(encryptionMode, BLOCK_SIZE_CALCULATOR);
+ }
+
+ /**
+ * This method has the side effect of replacing the plaintext attribute-values of "itemAttributes"
+ * with ciphertext attribute-values (which are always in the form of ByteBuffer) as per the
+ * corresponding attribute flags.
+ */
+ private void actualEncryption(
+ Map itemAttributes,
+ Map> attributeFlags,
+ Map materialDescription,
+ SecretKey encryptionKey)
+ throws GeneralSecurityException {
+ String encryptionMode = null;
+ if (encryptionKey != null) {
+ materialDescription.put(this.symmetricEncryptionModeHeader, SYMMETRIC_ENCRYPTION_MODE);
+ encryptionMode = encryptionKey.getAlgorithm() + SYMMETRIC_ENCRYPTION_MODE;
}
- /**
- * @see #marshallDescription(Map)
- */
- protected static Map unmarshallDescription(AttributeValue attributeValue) {
- attributeValue.getB().mark();
- try (DataInputStream in = new DataInputStream(
- new ByteBufferInputStream(attributeValue.getB())) ) {
- Map result = new HashMap();
- int version = in.readInt();
- if (version != CURRENT_VERSION) {
- throw new IllegalArgumentException("Unsupported description version");
- }
-
- String key, value;
- int keyLength, valueLength;
- try {
- while(in.available() > 0) {
- keyLength = in.readInt();
- byte[] bytes = new byte[keyLength];
- if (in.read(bytes) != keyLength) {
- throw new IllegalArgumentException("Malformed description");
- }
- key = new String(bytes, UTF8);
- valueLength = in.readInt();
- bytes = new byte[valueLength];
- if (in.read(bytes) != valueLength) {
- throw new IllegalArgumentException("Malformed description");
- }
- value = new String(bytes, UTF8);
- result.put(key, value);
- }
- } catch (EOFException eof) {
- throw new IllegalArgumentException("Malformed description", eof);
- }
- return result;
- } catch (IOException ex) {
- // Due to the objects in use, an IOException is not possible.
- throw new RuntimeException("Unexpected exception", ex);
- } finally {
- attributeValue.getB().reset();
+ Cipher cipher = null;
+ int blockSize = -1;
+
+ for (Map.Entry entry : itemAttributes.entrySet()) {
+ Set flags = attributeFlags.get(entry.getKey());
+ if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) {
+ if (!flags.contains(EncryptionFlags.SIGN)) {
+ throw new IllegalArgumentException(
+ "All encrypted fields must be signed. Bad field: " + entry.getKey());
}
+ ByteBuffer plainText = AttributeValueMarshaller.marshall(entry.getValue());
+ plainText.rewind();
+ ByteBuffer cipherText;
+ if (encryptionKey instanceof DelegatedKey) {
+ DelegatedKey dk = (DelegatedKey) encryptionKey;
+ cipherText = ByteBuffer.wrap(dk.encrypt(toByteArray(plainText), null, encryptionMode));
+ } else {
+ if (cipher == null) {
+ blockSize = getBlockSize(encryptionMode);
+ cipher = Cipher.getInstance(encryptionMode);
+ }
+ // Encryption format:
+ // Note a unique iv is generated per attribute
+ cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, Utils.getRng());
+ cipherText = ByteBuffer.allocate(blockSize + cipher.getOutputSize(plainText.remaining()));
+ cipherText.position(blockSize);
+ cipher.doFinal(plainText, cipherText);
+ cipherText.flip();
+ final byte[] iv = cipher.getIV();
+ if (iv.length != blockSize) {
+ throw new IllegalStateException(
+ String.format(
+ "Generated IV length (%d) not equal to block size (%d)", iv.length, blockSize));
+ }
+ cipherText.put(iv);
+ cipherText.rewind();
+ }
+ // Replace the plaintext attribute value with the encrypted content
+ entry.setValue(new AttributeValue().withB(cipherText));
+ }
}
-
- /**
- * @param encryptionContextOverrideOperator the nullable operator which will be used to override
- * the EncryptionContext.
- * @see com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators
- */
- public final void setEncryptionContextOverrideOperator(
- Function encryptionContextOverrideOperator) {
- this.encryptionContextOverrideOperator = encryptionContextOverrideOperator;
- }
-
- /**
- * @return the operator used to override the EncryptionContext
- * @see #setEncryptionContextOverrideOperator(Function)
- */
- public final Function getEncryptionContextOverrideOperator() {
- return encryptionContextOverrideOperator;
+ }
+
+ /**
+ * Get the name of the DynamoDB field used to store the signature. Defaults to {@link
+ * #DEFAULT_SIGNATURE_FIELD}.
+ *
+ * @return the name of the DynamoDB field used to store the signature
+ */
+ public String getSignatureFieldName() {
+ return signatureFieldName;
+ }
+
+ /**
+ * Set the name of the DynamoDB field used to store the signature.
+ *
+ * @param signatureFieldName
+ */
+ public void setSignatureFieldName(final String signatureFieldName) {
+ this.signatureFieldName = signatureFieldName;
+ }
+
+ /**
+ * Get the name of the DynamoDB field used to store metadata used by the DynamoDBEncryptedMapper.
+ * Defaults to {@link #DEFAULT_METADATA_FIELD}.
+ *
+ * @return the name of the DynamoDB field used to store metadata used by the
+ * DynamoDBEncryptedMapper
+ */
+ public String getMaterialDescriptionFieldName() {
+ return materialDescriptionFieldName;
+ }
+
+ /**
+ * Set the name of the DynamoDB field used to store metadata used by the DynamoDBEncryptedMapper
+ *
+ * @param materialDescriptionFieldName
+ */
+ public void setMaterialDescriptionFieldName(final String materialDescriptionFieldName) {
+ this.materialDescriptionFieldName = materialDescriptionFieldName;
+ }
+
+ /**
+ * Marshalls the description
into a ByteBuffer by outputting each key (modified
+ * UTF-8) followed by its value (also in modified UTF-8).
+ *
+ * @param description
+ * @return the description encoded as an AttributeValue with a ByteBuffer value
+ * @see java.io.DataOutput#writeUTF(String)
+ */
+ protected static AttributeValue marshallDescription(Map description) {
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(bos);
+ out.writeInt(CURRENT_VERSION);
+ for (Map.Entry entry : description.entrySet()) {
+ byte[] bytes = entry.getKey().getBytes(UTF8);
+ out.writeInt(bytes.length);
+ out.write(bytes);
+ bytes = entry.getValue().getBytes(UTF8);
+ out.writeInt(bytes.length);
+ out.write(bytes);
+ }
+ out.close();
+ AttributeValue result = new AttributeValue();
+ result.setB(ByteBuffer.wrap(bos.toByteArray()));
+ return result;
+ } catch (IOException ex) {
+ // Due to the objects in use, an IOException is not possible.
+ throw new RuntimeException("Unexpected exception", ex);
}
-
- private static byte[] toByteArray(ByteBuffer buffer) {
- buffer = buffer.duplicate();
- // We can only return the array directly if:
- // 1. The ByteBuffer exposes an array
- // 2. The ByteBuffer starts at the beginning of the array
- // 3. The ByteBuffer uses the entire array
- if (buffer.hasArray() && buffer.arrayOffset() == 0) {
- byte[] result = buffer.array();
- if (buffer.remaining() == result.length) {
- return result;
- }
+ }
+
+ public String getSigningAlgorithmHeader() {
+ return signingAlgorithmHeader;
+ }
+ /** @see #marshallDescription(Map) */
+ protected static Map unmarshallDescription(AttributeValue attributeValue) {
+ attributeValue.getB().mark();
+ try (DataInputStream in =
+ new DataInputStream(new ByteBufferInputStream(attributeValue.getB()))) {
+ Map result = new HashMap();
+ int version = in.readInt();
+ if (version != CURRENT_VERSION) {
+ throw new IllegalArgumentException("Unsupported description version");
+ }
+
+ String key, value;
+ int keyLength, valueLength;
+ try {
+ while (in.available() > 0) {
+ keyLength = in.readInt();
+ byte[] bytes = new byte[keyLength];
+ if (in.read(bytes) != keyLength) {
+ throw new IllegalArgumentException("Malformed description");
+ }
+ key = new String(bytes, UTF8);
+ valueLength = in.readInt();
+ bytes = new byte[valueLength];
+ if (in.read(bytes) != valueLength) {
+ throw new IllegalArgumentException("Malformed description");
+ }
+ value = new String(bytes, UTF8);
+ result.put(key, value);
}
-
- byte[] result = new byte[buffer.remaining()];
- buffer.get(result);
+ } catch (EOFException eof) {
+ throw new IllegalArgumentException("Malformed description", eof);
+ }
+ return result;
+ } catch (IOException ex) {
+ // Due to the objects in use, an IOException is not possible.
+ throw new RuntimeException("Unexpected exception", ex);
+ } finally {
+ attributeValue.getB().reset();
+ }
+ }
+
+ /**
+ * @param encryptionContextOverrideOperator the nullable operator which will be used to override
+ * the EncryptionContext.
+ * @see com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators
+ */
+ public final void setEncryptionContextOverrideOperator(
+ Function encryptionContextOverrideOperator) {
+ this.encryptionContextOverrideOperator = encryptionContextOverrideOperator;
+ }
+
+ /**
+ * @return the operator used to override the EncryptionContext
+ * @see #setEncryptionContextOverrideOperator(Function)
+ */
+ public final Function
+ getEncryptionContextOverrideOperator() {
+ return encryptionContextOverrideOperator;
+ }
+
+ private static byte[] toByteArray(ByteBuffer buffer) {
+ buffer = buffer.duplicate();
+ // We can only return the array directly if:
+ // 1. The ByteBuffer exposes an array
+ // 2. The ByteBuffer starts at the beginning of the array
+ // 3. The ByteBuffer uses the entire array
+ if (buffer.hasArray() && buffer.arrayOffset() == 0) {
+ byte[] result = buffer.array();
+ if (buffer.remaining() == result.length) {
return result;
+ }
}
+
+ byte[] result = new byte[buffer.remaining()];
+ buffer.get(result);
+ return result;
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java
index f4a13905..3a4aa14d 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java
@@ -14,6 +14,9 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption;
+import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller;
+import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -33,221 +36,227 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
-import com.amazonaws.services.dynamodbv2.datamodeling.internal.AttributeValueMarshaller;
-import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils;
-import com.amazonaws.services.dynamodbv2.model.AttributeValue;
-
/**
- * For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
*
- * @author Greg Rubin
+ * @author Greg Rubin
*/
// NOTE: This class must remain thread-safe.
class DynamoDBSigner {
- private static final ConcurrentHashMap cache =
- new ConcurrentHashMap();
-
- protected static final Charset UTF8 = Charset.forName("UTF-8");
- private final SecureRandom rnd;
- private final SecretKey hmacComparisonKey;
- private final String signingAlgorithm;
-
- /**
- * @param signingAlgorithm
- * is the algorithm used for asymmetric signing (ex:
- * SHA256withRSA). This is ignored for symmetric HMACs as that
- * algorithm is fully specified by the key.
- */
- static DynamoDBSigner getInstance(String signingAlgorithm, SecureRandom rnd) {
- DynamoDBSigner result = cache.get(signingAlgorithm);
- if (result == null) {
- result = new DynamoDBSigner(signingAlgorithm, rnd);
- cache.putIfAbsent(signingAlgorithm, result);
- }
- return result;
- }
+ private static final ConcurrentHashMap cache =
+ new ConcurrentHashMap();
- /**
- * @param signingAlgorithm
- * is the algorithm used for asymmetric signing (ex:
- * SHA256withRSA). This is ignored for symmetric HMACs as that
- * algorithm is fully specified by the key.
- */
- private DynamoDBSigner(String signingAlgorithm, SecureRandom rnd) {
- if (rnd == null) {
- rnd = Utils.getRng();
- }
- this.rnd = rnd;
- this.signingAlgorithm = signingAlgorithm;
- // Shorter than the output of SHA256 to avoid weak keys.
- // http://cs.nyu.edu/~dodis/ps/h-of-h.pdf
- // http://link.springer.com/chapter/10.1007%2F978-3-642-32009-5_21
- byte[] tmpKey = new byte[31];
- rnd.nextBytes(tmpKey);
- hmacComparisonKey = new SecretKeySpec(tmpKey, "HmacSHA256");
- }
+ protected static final Charset UTF8 = Charset.forName("UTF-8");
+ private final SecureRandom rnd;
+ private final SecretKey hmacComparisonKey;
+ private final String signingAlgorithm;
- void verifySignature(Map itemAttributes, Map> attributeFlags,
- byte[] associatedData, Key verificationKey, ByteBuffer signature) throws GeneralSecurityException {
- if (verificationKey instanceof DelegatedKey) {
- DelegatedKey dKey = (DelegatedKey)verificationKey;
- byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
- if (!dKey.verify(stringToSign, toByteArray(signature), dKey.getAlgorithm())) {
- throw new SignatureException("Bad signature");
- }
- } else if (verificationKey instanceof SecretKey) {
- byte[] calculatedSig = calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey)verificationKey);
- if (!safeEquals(signature, calculatedSig)) {
- throw new SignatureException("Bad signature");
- }
- } else if (verificationKey instanceof PublicKey) {
- PublicKey integrityKey = (PublicKey)verificationKey;
- byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
- Signature sig = Signature.getInstance(getSigningAlgorithm());
- sig.initVerify(integrityKey);
- sig.update(stringToSign);
- if (!sig.verify(toByteArray(signature))) {
- throw new SignatureException("Bad signature");
- }
- } else {
- throw new IllegalArgumentException("No integrity key provided");
- }
+ /**
+ * @param signingAlgorithm is the algorithm used for asymmetric signing (ex: SHA256withRSA). This
+ * is ignored for symmetric HMACs as that algorithm is fully specified by the key.
+ */
+ static DynamoDBSigner getInstance(String signingAlgorithm, SecureRandom rnd) {
+ DynamoDBSigner result = cache.get(signingAlgorithm);
+ if (result == null) {
+ result = new DynamoDBSigner(signingAlgorithm, rnd);
+ cache.putIfAbsent(signingAlgorithm, result);
}
+ return result;
+ }
- static byte[] calculateStringToSign(Map itemAttributes,
- Map> attributeFlags, byte[] associatedData)
- throws NoSuchAlgorithmException {
- try {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- List attrNames = new ArrayList(itemAttributes.keySet());
- Collections.sort(attrNames);
- MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
- if (associatedData != null) {
- out.write(sha256.digest(associatedData));
- } else {
- out.write(sha256.digest());
- }
- sha256.reset();
-
- for (String name : attrNames) {
- Set set = attributeFlags.get(name);
- if(set != null && set.contains(EncryptionFlags.SIGN)) {
- AttributeValue tmp = itemAttributes.get(name);
- out.write(sha256.digest(name.getBytes(UTF8)));
- sha256.reset();
- if (set.contains(EncryptionFlags.ENCRYPT)) {
- sha256.update("ENCRYPTED".getBytes(UTF8));
- } else {
- sha256.update("PLAINTEXT".getBytes(UTF8));
- }
- out.write(sha256.digest());
-
- sha256.reset();
-
- sha256.update(AttributeValueMarshaller.marshall(tmp));
- out.write(sha256.digest());
- sha256.reset();
- }
- }
- return out.toByteArray();
- } catch (IOException ex) {
- // Due to the objects in use, an IOException is not possible.
- throw new RuntimeException("Unexpected exception", ex);
- }
+ /**
+ * @param signingAlgorithm is the algorithm used for asymmetric signing (ex: SHA256withRSA). This
+ * is ignored for symmetric HMACs as that algorithm is fully specified by the key.
+ */
+ private DynamoDBSigner(String signingAlgorithm, SecureRandom rnd) {
+ if (rnd == null) {
+ rnd = Utils.getRng();
}
+ this.rnd = rnd;
+ this.signingAlgorithm = signingAlgorithm;
+ // Shorter than the output of SHA256 to avoid weak keys.
+ // http://cs.nyu.edu/~dodis/ps/h-of-h.pdf
+ // http://link.springer.com/chapter/10.1007%2F978-3-642-32009-5_21
+ byte[] tmpKey = new byte[31];
+ rnd.nextBytes(tmpKey);
+ hmacComparisonKey = new SecretKeySpec(tmpKey, "HmacSHA256");
+ }
- /**
- * The itemAttributes have already been encrypted, if necessary, before the
- * signing.
- */
- byte[] calculateSignature(
- Map itemAttributes,
- Map> attributeFlags,
- byte[] associatedData, Key key) throws GeneralSecurityException {
- if (key instanceof DelegatedKey) {
- return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key);
- } else if (key instanceof SecretKey) {
- return calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey) key);
- } else if (key instanceof PrivateKey) {
- return calculateSignature(itemAttributes, attributeFlags, associatedData, (PrivateKey) key);
- } else {
- throw new IllegalArgumentException("No integrity key provided");
- }
+ void verifySignature(
+ Map itemAttributes,
+ Map> attributeFlags,
+ byte[] associatedData,
+ Key verificationKey,
+ ByteBuffer signature)
+ throws GeneralSecurityException {
+ if (verificationKey instanceof DelegatedKey) {
+ DelegatedKey dKey = (DelegatedKey) verificationKey;
+ byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
+ if (!dKey.verify(stringToSign, toByteArray(signature), dKey.getAlgorithm())) {
+ throw new SignatureException("Bad signature");
+ }
+ } else if (verificationKey instanceof SecretKey) {
+ byte[] calculatedSig =
+ calculateSignature(
+ itemAttributes, attributeFlags, associatedData, (SecretKey) verificationKey);
+ if (!safeEquals(signature, calculatedSig)) {
+ throw new SignatureException("Bad signature");
+ }
+ } else if (verificationKey instanceof PublicKey) {
+ PublicKey integrityKey = (PublicKey) verificationKey;
+ byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
+ Signature sig = Signature.getInstance(getSigningAlgorithm());
+ sig.initVerify(integrityKey);
+ sig.update(stringToSign);
+ if (!sig.verify(toByteArray(signature))) {
+ throw new SignatureException("Bad signature");
+ }
+ } else {
+ throw new IllegalArgumentException("No integrity key provided");
}
+ }
- byte[] calculateSignature(Map itemAttributes,
- Map> attributeFlags, byte[] associatedData,
- DelegatedKey key) throws GeneralSecurityException {
- byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
- return key.sign(stringToSign, key.getAlgorithm());
- }
+ static byte[] calculateStringToSign(
+ Map itemAttributes,
+ Map> attributeFlags,
+ byte[] associatedData)
+ throws NoSuchAlgorithmException {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ List attrNames = new ArrayList(itemAttributes.keySet());
+ Collections.sort(attrNames);
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ if (associatedData != null) {
+ out.write(sha256.digest(associatedData));
+ } else {
+ out.write(sha256.digest());
+ }
+ sha256.reset();
- byte[] calculateSignature(Map itemAttributes,
- Map> attributeFlags, byte[] associatedData,
- SecretKey key) throws GeneralSecurityException {
- if (key instanceof DelegatedKey) {
- return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey)key);
+ for (String name : attrNames) {
+ Set set = attributeFlags.get(name);
+ if (set != null && set.contains(EncryptionFlags.SIGN)) {
+ AttributeValue tmp = itemAttributes.get(name);
+ out.write(sha256.digest(name.getBytes(UTF8)));
+ sha256.reset();
+ if (set.contains(EncryptionFlags.ENCRYPT)) {
+ sha256.update("ENCRYPTED".getBytes(UTF8));
+ } else {
+ sha256.update("PLAINTEXT".getBytes(UTF8));
+ }
+ out.write(sha256.digest());
+
+ sha256.reset();
+
+ sha256.update(AttributeValueMarshaller.marshall(tmp));
+ out.write(sha256.digest());
+ sha256.reset();
}
- byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
- Mac hmac = Mac.getInstance(key.getAlgorithm());
- hmac.init(key);
- hmac.update(stringToSign);
- return hmac.doFinal();
+ }
+ return out.toByteArray();
+ } catch (IOException ex) {
+ // Due to the objects in use, an IOException is not possible.
+ throw new RuntimeException("Unexpected exception", ex);
}
+ }
- byte[] calculateSignature(Map itemAttributes,
- Map> attributeFlags, byte[] associatedData,
- PrivateKey key) throws GeneralSecurityException {
- byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
- Signature sig = Signature.getInstance(signingAlgorithm);
- sig.initSign(key, rnd);
- sig.update(stringToSign);
- return sig.sign();
+ /** The itemAttributes have already been encrypted, if necessary, before the signing. */
+ byte[] calculateSignature(
+ Map itemAttributes,
+ Map> attributeFlags,
+ byte[] associatedData,
+ Key key)
+ throws GeneralSecurityException {
+ if (key instanceof DelegatedKey) {
+ return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key);
+ } else if (key instanceof SecretKey) {
+ return calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey) key);
+ } else if (key instanceof PrivateKey) {
+ return calculateSignature(itemAttributes, attributeFlags, associatedData, (PrivateKey) key);
+ } else {
+ throw new IllegalArgumentException("No integrity key provided");
}
+ }
+
+ byte[] calculateSignature(
+ Map itemAttributes,
+ Map> attributeFlags,
+ byte[] associatedData,
+ DelegatedKey key)
+ throws GeneralSecurityException {
+ byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
+ return key.sign(stringToSign, key.getAlgorithm());
+ }
- String getSigningAlgorithm() {
- return signingAlgorithm;
+ byte[] calculateSignature(
+ Map itemAttributes,
+ Map> attributeFlags,
+ byte[] associatedData,
+ SecretKey key)
+ throws GeneralSecurityException {
+ if (key instanceof DelegatedKey) {
+ return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key);
}
+ byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
+ Mac hmac = Mac.getInstance(key.getAlgorithm());
+ hmac.init(key);
+ hmac.update(stringToSign);
+ return hmac.doFinal();
+ }
- /**
- * Constant-time equality check.
- */
- private boolean safeEquals(ByteBuffer signature, byte[] calculatedSig) {
- try {
- signature.rewind();
- Mac hmac = Mac.getInstance(hmacComparisonKey.getAlgorithm());
- hmac.init(hmacComparisonKey);
- hmac.update(signature);
- byte[] signatureHash = hmac.doFinal();
-
- hmac.reset();
- hmac.update(calculatedSig);
- byte[] calculatedHash = hmac.doFinal();
-
- return MessageDigest.isEqual(signatureHash, calculatedHash);
- } catch (GeneralSecurityException ex) {
- // We've hardcoded these algorithms, so the error should not be possible.
- throw new RuntimeException("Unexpected exception", ex);
- }
+ byte[] calculateSignature(
+ Map itemAttributes,
+ Map> attributeFlags,
+ byte[] associatedData,
+ PrivateKey key)
+ throws GeneralSecurityException {
+ byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData);
+ Signature sig = Signature.getInstance(signingAlgorithm);
+ sig.initSign(key, rnd);
+ sig.update(stringToSign);
+ return sig.sign();
+ }
+
+ String getSigningAlgorithm() {
+ return signingAlgorithm;
+ }
+
+ /** Constant-time equality check. */
+ private boolean safeEquals(ByteBuffer signature, byte[] calculatedSig) {
+ try {
+ signature.rewind();
+ Mac hmac = Mac.getInstance(hmacComparisonKey.getAlgorithm());
+ hmac.init(hmacComparisonKey);
+ hmac.update(signature);
+ byte[] signatureHash = hmac.doFinal();
+
+ hmac.reset();
+ hmac.update(calculatedSig);
+ byte[] calculatedHash = hmac.doFinal();
+
+ return MessageDigest.isEqual(signatureHash, calculatedHash);
+ } catch (GeneralSecurityException ex) {
+ // We've hardcoded these algorithms, so the error should not be possible.
+ throw new RuntimeException("Unexpected exception", ex);
}
+ }
- private static byte[] toByteArray(ByteBuffer buffer) {
- if (buffer.hasArray()) {
- byte[] result = buffer.array();
- buffer.rewind();
- return result;
- } else {
- byte[] result = new byte[buffer.remaining()];
- buffer.get(result);
- buffer.rewind();
- return result;
- }
+ private static byte[] toByteArray(ByteBuffer buffer) {
+ if (buffer.hasArray()) {
+ byte[] result = buffer.array();
+ buffer.rewind();
+ return result;
+ } else {
+ byte[] result = new byte[buffer.remaining()];
+ buffer.get(result);
+ buffer.rewind();
+ return result;
}
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java
index 4340598e..c36a7664 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionContext.java
@@ -14,212 +14,207 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials;
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
/**
- * This class serves to provide additional useful data to
- * {@link EncryptionMaterialsProvider}s so they can more intelligently select
- * the proper {@link EncryptionMaterials} or {@link DecryptionMaterials} for
- * use. Any of the methods are permitted to return null.
- *
- * For the simplest cases, all a developer needs to provide in the context are:
+ * This class serves to provide additional useful data to {@link EncryptionMaterialsProvider}s so
+ * they can more intelligently select the proper {@link EncryptionMaterials} or {@link
+ * DecryptionMaterials} for use. Any of the methods are permitted to return null.
+ *
+ *
For the simplest cases, all a developer needs to provide in the context are:
+ *
*
- * - TableName
- * - HashKeyName
- * - RangeKeyName (if present)
+ * - TableName
+ *
- HashKeyName
+ *
- RangeKeyName (if present)
*
- *
+ *
* This class is immutable.
- *
- * @author Greg Rubin
+ *
+ * @author Greg Rubin
*/
public final class EncryptionContext {
- private final String tableName;
- private final Map attributeValues;
- private final Class> modeledClass;
- private final Object developerContext;
- private final String hashKeyName;
- private final String rangeKeyName;
- private final Map materialDescription;
-
- private EncryptionContext(Builder builder) {
- tableName = builder.getTableName();
- attributeValues = builder.getAttributeValues();
- modeledClass = builder.getModeledClass();
- developerContext = builder.getDeveloperContext();
- hashKeyName = builder.getHashKeyName();
- rangeKeyName = builder.getRangeKeyName();
- materialDescription = builder.getMaterialDescription();
- }
-
- /**
- * Returns the name of the DynamoDB Table this record is associated with.
- */
+ private final String tableName;
+ private final Map attributeValues;
+ private final Class> modeledClass;
+ private final Object developerContext;
+ private final String hashKeyName;
+ private final String rangeKeyName;
+ private final Map materialDescription;
+
+ private EncryptionContext(Builder builder) {
+ tableName = builder.getTableName();
+ attributeValues = builder.getAttributeValues();
+ modeledClass = builder.getModeledClass();
+ developerContext = builder.getDeveloperContext();
+ hashKeyName = builder.getHashKeyName();
+ rangeKeyName = builder.getRangeKeyName();
+ materialDescription = builder.getMaterialDescription();
+ }
+
+ /** Returns the name of the DynamoDB Table this record is associated with. */
+ public String getTableName() {
+ return tableName;
+ }
+
+ /** Returns the DynamoDB record about to be encrypted/decrypted. */
+ public Map getAttributeValues() {
+ return attributeValues;
+ }
+
+ /**
+ * When used for an object mapping layer (such as {@link DynamoDBMapper}) this represents the
+ * class being mapped to/from DynamoDB.
+ */
+ public Class> getModeledClass() {
+ return modeledClass;
+ }
+
+ /**
+ * This object has no meaning (and will not be set or examined) by any core libraries. It exists
+ * to allow custom object mappers and data access layers to pass data to {@link
+ * EncryptionMaterialsProvider}s through the {@link DynamoDBEncryptor}.
+ */
+ public Object getDeveloperContext() {
+ return developerContext;
+ }
+
+ /** Returns the name of the HashKey attribute for the record to be encrypted/decrypted. */
+ public String getHashKeyName() {
+ return hashKeyName;
+ }
+
+ /** Returns the name of the RangeKey attribute for the record to be encrypted/decrypted. */
+ public String getRangeKeyName() {
+ return rangeKeyName;
+ }
+
+ public Map getMaterialDescription() {
+ return materialDescription;
+ }
+
+ /**
+ * Builder class for {@link EncryptionContext}. Mutable objects (other than developerContext
+ *
) will undergo a defensive copy prior to being stored in the builder.
+ *
+ * This class is not thread-safe.
+ */
+ public static final class Builder {
+ private String tableName = null;
+ private Map attributeValues = null;
+ private Class> modeledClass = null;
+ private Object developerContext = null;
+ private String hashKeyName = null;
+ private String rangeKeyName = null;
+ private Map materialDescription = null;
+
+ /** Defaults all fields to null
. */
+ public Builder() {}
+
+ /** Copy constructor. This will perform a shallow copy of the DeveloperContext
. */
+ public Builder(EncryptionContext context) {
+ tableName = context.getTableName();
+ attributeValues = context.getAttributeValues();
+ modeledClass = context.getModeledClass();
+ developerContext = context.getDeveloperContext();
+ hashKeyName = context.getHashKeyName();
+ rangeKeyName = context.getRangeKeyName();
+ materialDescription = context.getMaterialDescription();
+ }
+
+ public EncryptionContext build() {
+ return new EncryptionContext(this);
+ }
+
+ public Builder withTableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ public Builder withAttributeValues(Map attributeValues) {
+ this.attributeValues =
+ Collections.unmodifiableMap(new HashMap(attributeValues));
+ return this;
+ }
+
+ public Builder withModeledClass(Class> modeledClass) {
+ this.modeledClass = modeledClass;
+ return this;
+ }
+
+ public Builder withDeveloperContext(Object developerContext) {
+ this.developerContext = developerContext;
+ return this;
+ }
+
+ public Builder withHashKeyName(String hashKeyName) {
+ this.hashKeyName = hashKeyName;
+ return this;
+ }
+
+ public Builder withRangeKeyName(String rangeKeyName) {
+ this.rangeKeyName = rangeKeyName;
+ return this;
+ }
+
+ public Builder withMaterialDescription(Map materialDescription) {
+ this.materialDescription =
+ Collections.unmodifiableMap(new HashMap(materialDescription));
+ return this;
+ }
+
public String getTableName() {
- return tableName;
+ return tableName;
}
-
- /**
- * Returns the DynamoDB record about to be encrypted/decrypted.
- */
+
public Map getAttributeValues() {
- return attributeValues;
+ return attributeValues;
}
-
- /**
- * When used for an object mapping layer (such as {@link DynamoDBMapper})
- * this represents the class being mapped to/from DynamoDB.
- */
+
public Class> getModeledClass() {
- return modeledClass;
- }
-
- /**
- * This object has no meaning (and will not be set or examined) by any core libraries.
- * It exists to allow custom object mappers and data access layers to pass
- * data to {@link EncryptionMaterialsProvider}s through the {@link DynamoDBEncryptor}.
- */
+ return modeledClass;
+ }
+
public Object getDeveloperContext() {
- return developerContext;
+ return developerContext;
}
-
- /**
- * Returns the name of the HashKey attribute for the record to be encrypted/decrypted.
- */
+
public String getHashKeyName() {
- return hashKeyName;
+ return hashKeyName;
}
- /**
- * Returns the name of the RangeKey attribute for the record to be encrypted/decrypted.
- */
public String getRangeKeyName() {
- return rangeKeyName;
+ return rangeKeyName;
}
public Map getMaterialDescription() {
- return materialDescription;
- }
-
- /**
- * Builder class for {@link EncryptionContext}.
- * Mutable objects (other than developerContext
) will undergo
- * a defensive copy prior to being stored in the builder.
- *
- * This class is not thread-safe.
- */
- public static final class Builder {
- private String tableName = null;
- private Map attributeValues = null;
- private Class> modeledClass = null;
- private Object developerContext = null;
- private String hashKeyName = null;
- private String rangeKeyName = null;
- private Map materialDescription = null;
-
- /**
- * Defaults all fields to null
.
- */
- public Builder() {
- }
-
- /**
- * Copy constructor.
- * This will perform a shallow copy of the DeveloperContext
.
- */
- public Builder(EncryptionContext context) {
- tableName = context.getTableName();
- attributeValues = context.getAttributeValues();
- modeledClass = context.getModeledClass();
- developerContext = context.getDeveloperContext();
- hashKeyName = context.getHashKeyName();
- rangeKeyName = context.getRangeKeyName();
- materialDescription = context.getMaterialDescription();
- }
-
- public EncryptionContext build() {
- return new EncryptionContext(this);
- }
-
- public Builder withTableName(String tableName) {
- this.tableName = tableName;
- return this;
- }
-
- public Builder withAttributeValues(Map attributeValues) {
- this.attributeValues = Collections.unmodifiableMap(
- new HashMap(attributeValues));
- return this;
- }
-
- public Builder withModeledClass(Class> modeledClass) {
- this.modeledClass = modeledClass;
- return this;
- }
-
- public Builder withDeveloperContext(Object developerContext) {
- this.developerContext = developerContext;
- return this;
- }
-
- public Builder withHashKeyName(String hashKeyName) {
- this.hashKeyName = hashKeyName;
- return this;
- }
-
- public Builder withRangeKeyName(String rangeKeyName) {
- this.rangeKeyName = rangeKeyName;
- return this;
- }
-
- public Builder withMaterialDescription(Map materialDescription) {
- this.materialDescription = Collections.unmodifiableMap(
- new HashMap(materialDescription));
- return this;
- }
-
- public String getTableName() {
- return tableName;
- }
-
- public Map getAttributeValues() {
- return attributeValues;
- }
-
- public Class> getModeledClass() {
- return modeledClass;
- }
-
- public Object getDeveloperContext() {
- return developerContext;
- }
-
- public String getHashKeyName() {
- return hashKeyName;
- }
-
- public String getRangeKeyName() {
- return rangeKeyName;
- }
-
- public Map getMaterialDescription() {
- return materialDescription;
- }
- }
-
- @Override
- public String toString() {
- return "EncryptionContext [tableName=" + tableName + ", attributeValues=" + attributeValues
- + ", modeledClass=" + modeledClass + ", developerContext=" + developerContext
- + ", hashKeyName=" + hashKeyName + ", rangeKeyName=" + rangeKeyName
- + ", materialDescription=" + materialDescription + "]";
+ return materialDescription;
}
+ }
+
+ @Override
+ public String toString() {
+ return "EncryptionContext [tableName="
+ + tableName
+ + ", attributeValues="
+ + attributeValues
+ + ", modeledClass="
+ + modeledClass
+ + ", developerContext="
+ + developerContext
+ + ", hashKeyName="
+ + hashKeyName
+ + ", rangeKeyName="
+ + rangeKeyName
+ + ", materialDescription="
+ + materialDescription
+ + "]";
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java
index 4a946761..124365e9 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/EncryptionFlags.java
@@ -14,10 +14,8 @@
*/
package com.amazonaws.services.dynamodbv2.datamodeling.encryption;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public enum EncryptionFlags {
- ENCRYPT,
- SIGN
+ ENCRYPT,
+ SIGN
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java
index fafa85b5..282faf10 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/HandleUnknownAttributes.java
@@ -20,21 +20,20 @@
import java.lang.annotation.Target;
/**
- * Marker annotation that indicates that attributes found during unmarshalling
- * that are in the DynamoDB item but not modeled in the mapper model class
- * should be included in for decryption/signature verification. The default
- * behavior (without this annotation) is to ignore them, which can lead to
- * signature verification failures when attributes are removed from model classes.
- *
- * If this annotation is added to a class with @DoNotEncrypt, then the unknown
- * attributes will only be included in the signature calculation, and if it's
- * added to a class with default encryption behavior, the unknown attributes
- * will be signed and decrypted.
+ * Marker annotation that indicates that attributes found during unmarshalling that are in the
+ * DynamoDB item but not modeled in the mapper model class should be included in for
+ * decryption/signature verification. The default behavior (without this annotation) is to ignore
+ * them, which can lead to signature verification failures when attributes are removed from model
+ * classes.
+ *
+ * If this annotation is added to a class with @DoNotEncrypt, then the unknown attributes will
+ * only be included in the signature calculation, and if it's added to a class with default
+ * encryption behavior, the unknown attributes will be signed and decrypted.
+ *
+ *
For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
*
- *
For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
- *
* @author Dan Cavallaro
*/
@Target(value = {ElementType.TYPE})
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java
index eb9c15db..ca5ad067 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/TableAadOverride.java
@@ -20,19 +20,18 @@
import java.lang.annotation.Target;
/**
- * Overrides the default tablename used as part of the data signature with
- * {@code tableName} instead. This can be useful when multiple tables are
- * used interchangably and data should be able to be copied or moved
- * between them without needing to be reencrypted.
+ * Overrides the default tablename used as part of the data signature with {@code tableName}
+ * instead. This can be useful when multiple tables are used interchangably and data should be able
+ * to be copied or moved between them without needing to be reencrypted.
*
- * For guidance on performing a safe data model change procedure, please see
- *
- * DynamoDB Encryption Client Developer Guide: Changing your data model
- *
- * @author Greg Rubin
+ * For guidance on performing a safe data model change procedure, please see DynamoDB Encryption Client Developer Guide: Changing your data model
+ *
+ * @author Greg Rubin
*/
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TableAadOverride {
- String tableName();
+ String tableName();
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java
index b7fa559d..94637a32 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AbstractRawMaterials.java
@@ -19,55 +19,52 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-
import javax.crypto.SecretKey;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public abstract class AbstractRawMaterials implements DecryptionMaterials, EncryptionMaterials {
- private Map description;
- private final Key signingKey;
- private final Key verificationKey;
+ private Map description;
+ private final Key signingKey;
+ private final Key verificationKey;
- @SuppressWarnings("unchecked")
- protected AbstractRawMaterials(KeyPair signingPair) {
- this(signingPair, Collections.EMPTY_MAP);
- }
+ @SuppressWarnings("unchecked")
+ protected AbstractRawMaterials(KeyPair signingPair) {
+ this(signingPair, Collections.EMPTY_MAP);
+ }
- protected AbstractRawMaterials(KeyPair signingPair, Map description) {
- this.signingKey = signingPair.getPrivate();
- this.verificationKey = signingPair.getPublic();
- setMaterialDescription(description);
- }
+ protected AbstractRawMaterials(KeyPair signingPair, Map description) {
+ this.signingKey = signingPair.getPrivate();
+ this.verificationKey = signingPair.getPublic();
+ setMaterialDescription(description);
+ }
- @SuppressWarnings("unchecked")
- protected AbstractRawMaterials(SecretKey macKey) {
- this(macKey, Collections.EMPTY_MAP);
- }
+ @SuppressWarnings("unchecked")
+ protected AbstractRawMaterials(SecretKey macKey) {
+ this(macKey, Collections.EMPTY_MAP);
+ }
- protected AbstractRawMaterials(SecretKey macKey, Map description) {
- this.signingKey = macKey;
- this.verificationKey = macKey;
- this.description = Collections.unmodifiableMap(new HashMap(description));
- }
+ protected AbstractRawMaterials(SecretKey macKey, Map description) {
+ this.signingKey = macKey;
+ this.verificationKey = macKey;
+ this.description = Collections.unmodifiableMap(new HashMap(description));
+ }
- @Override
- public Map getMaterialDescription() {
- return new HashMap(description);
- }
+ @Override
+ public Map getMaterialDescription() {
+ return new HashMap(description);
+ }
- public void setMaterialDescription(Map description) {
- this.description = Collections.unmodifiableMap(new HashMap(description));
- }
+ public void setMaterialDescription(Map description) {
+ this.description = Collections.unmodifiableMap(new HashMap(description));
+ }
- @Override
- public Key getSigningKey() {
- return signingKey;
- }
+ @Override
+ public Key getSigningKey() {
+ return signingKey;
+ }
- @Override
- public Key getVerificationKey() {
- return verificationKey;
- }
+ @Override
+ public Key getVerificationKey() {
+ return verificationKey;
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java
index 7e487913..cdd265d6 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/AsymmetricRawMaterials.java
@@ -18,32 +18,31 @@
import java.security.KeyPair;
import java.util.Collections;
import java.util.Map;
-
import javax.crypto.SecretKey;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public class AsymmetricRawMaterials extends WrappedRawMaterials {
- @SuppressWarnings("unchecked")
- public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair)
- throws GeneralSecurityException {
- this(encryptionKey, signingPair, Collections.EMPTY_MAP);
- }
+ @SuppressWarnings("unchecked")
+ public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair)
+ throws GeneralSecurityException {
+ this(encryptionKey, signingPair, Collections.EMPTY_MAP);
+ }
- public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair, Map description)
- throws GeneralSecurityException {
- super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description);
- }
+ public AsymmetricRawMaterials(
+ KeyPair encryptionKey, KeyPair signingPair, Map description)
+ throws GeneralSecurityException {
+ super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description);
+ }
- @SuppressWarnings("unchecked")
- public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey)
- throws GeneralSecurityException {
- this(encryptionKey, macKey, Collections.EMPTY_MAP);
- }
+ @SuppressWarnings("unchecked")
+ public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey)
+ throws GeneralSecurityException {
+ this(encryptionKey, macKey, Collections.EMPTY_MAP);
+ }
- public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey, Map description)
- throws GeneralSecurityException {
- super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description);
- }
+ public AsymmetricRawMaterials(
+ KeyPair encryptionKey, SecretKey macKey, Map description)
+ throws GeneralSecurityException {
+ super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description);
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java
index 58b6c089..8171415b 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/CryptographicMaterials.java
@@ -16,9 +16,7 @@
import java.util.Map;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public interface CryptographicMaterials {
- public Map getMaterialDescription();
+ public Map getMaterialDescription();
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java
index 79d76c8d..6f94eb7f 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/DecryptionMaterials.java
@@ -15,13 +15,11 @@
package com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials;
import java.security.Key;
-
import javax.crypto.SecretKey;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public interface DecryptionMaterials extends CryptographicMaterials {
- public SecretKey getDecryptionKey();
- public Key getVerificationKey();
+ public SecretKey getDecryptionKey();
+
+ public Key getVerificationKey();
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java
index 6486aae4..efd97480 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/EncryptionMaterials.java
@@ -15,13 +15,11 @@
package com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials;
import java.security.Key;
-
import javax.crypto.SecretKey;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public interface EncryptionMaterials extends CryptographicMaterials {
- public SecretKey getEncryptionKey();
- public Key getSigningKey();
+ public SecretKey getEncryptionKey();
+
+ public Key getSigningKey();
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java
index 687299cb..a4da3c8b 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/SymmetricRawMaterials.java
@@ -17,42 +17,41 @@
import java.security.KeyPair;
import java.util.Collections;
import java.util.Map;
-
import javax.crypto.SecretKey;
-/**
- * @author Greg Rubin
- */
+/** @author Greg Rubin */
public class SymmetricRawMaterials extends AbstractRawMaterials {
- private final SecretKey cryptoKey;
-
- @SuppressWarnings("unchecked")
- public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair) {
- this(encryptionKey, signingPair, Collections.EMPTY_MAP);
- }
-
- public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair, Map description) {
- super(signingPair, description);
- this.cryptoKey = encryptionKey;
- }
-
- @SuppressWarnings("unchecked")
- public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey) {
- this(encryptionKey, macKey, Collections.EMPTY_MAP);
- }
-
- public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey, Map description) {
- super(macKey, description);
- this.cryptoKey = encryptionKey;
- }
-
- @Override
- public SecretKey getEncryptionKey() {
- return cryptoKey;
- }
-
- @Override
- public SecretKey getDecryptionKey() {
- return cryptoKey;
- }
+ private final SecretKey cryptoKey;
+
+ @SuppressWarnings("unchecked")
+ public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair) {
+ this(encryptionKey, signingPair, Collections.EMPTY_MAP);
+ }
+
+ public SymmetricRawMaterials(
+ SecretKey encryptionKey, KeyPair signingPair, Map description) {
+ super(signingPair, description);
+ this.cryptoKey = encryptionKey;
+ }
+
+ @SuppressWarnings("unchecked")
+ public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey) {
+ this(encryptionKey, macKey, Collections.EMPTY_MAP);
+ }
+
+ public SymmetricRawMaterials(
+ SecretKey encryptionKey, SecretKey macKey, Map description) {
+ super(macKey, description);
+ this.cryptoKey = encryptionKey;
+ }
+
+ @Override
+ public SecretKey getEncryptionKey() {
+ return cryptoKey;
+ }
+
+ @Override
+ public SecretKey getDecryptionKey() {
+ return cryptoKey;
+ }
}
diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java
index d70b5bd5..ba0c489a 100644
--- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java
+++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/materials/WrappedRawMaterials.java
@@ -17,12 +17,6 @@
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DelegatedKey;
import com.amazonaws.services.dynamodbv2.datamodeling.internal.Base64;
import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils;
-
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyGenerator;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
@@ -30,177 +24,188 @@
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Map;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
/**
- * Represents cryptographic materials used to manage unique record-level keys.
- * This class specifically implements Envelope Encryption where a unique content
- * key is randomly generated each time this class is constructed which is then
- * encrypted with the Wrapping Key and then persisted in the Description. If a
- * wrapped key is present in the Description, then that content key is unwrapped
- * and used to decrypt the actual data in the record.
- *
- * Other possibly implementations might use a Key-Derivation Function to derive
- * a unique key per record.
+ * Represents cryptographic materials used to manage unique record-level keys. This class
+ * specifically implements Envelope Encryption where a unique content key is randomly generated each
+ * time this class is constructed which is then encrypted with the Wrapping Key and then persisted
+ * in the Description. If a wrapped key is present in the Description, then that content key is
+ * unwrapped and used to decrypt the actual data in the record.
+ *
+ * Other possibly implementations might use a Key-Derivation Function to derive a unique key per
+ * record.
*
- * @author Greg Rubin
+ * @author Greg Rubin
*/
public class WrappedRawMaterials extends AbstractRawMaterials {
- /**
- * The key-name in the Description which contains the algorithm use to wrap
- * content key. Example values are "AESWrap", or
- * "RSA/ECB/OAEPWithSHA-256AndMGF1Padding".
- */
- public static final String KEY_WRAPPING_ALGORITHM = "amzn-ddb-wrap-alg";
- /**
- * The key-name in the Description which contains the algorithm used by the
- * content key. Example values are "AES", or "Blowfish".
- */
- public static final String CONTENT_KEY_ALGORITHM = "amzn-ddb-env-alg";
- /**
- * The key-name in the Description which which contains the wrapped content
- * key.
- */
- public static final String ENVELOPE_KEY = "amzn-ddb-env-key";
- private static final String DEFAULT_ALGORITHM = "AES/256";
-
- protected final Key wrappingKey;
- protected final Key unwrappingKey;
- private final SecretKey envelopeKey;
-
- public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair)
- throws GeneralSecurityException {
- this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap());
- }
+ /**
+ * The key-name in the Description which contains the algorithm use to wrap content key. Example
+ * values are "AESWrap", or "RSA/ECB/OAEPWithSHA-256AndMGF1Padding".
+ */
+ public static final String KEY_WRAPPING_ALGORITHM = "amzn-ddb-wrap-alg";
+ /**
+ * The key-name in the Description which contains the algorithm used by the content key. Example
+ * values are "AES", or "Blowfish".
+ */
+ public static final String CONTENT_KEY_ALGORITHM = "amzn-ddb-env-alg";
+ /** The key-name in the Description which which contains the wrapped content key. */
+ public static final String ENVELOPE_KEY = "amzn-ddb-env-key";
- public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair,
- Map description) throws GeneralSecurityException {
- super(signingPair, description);
- this.wrappingKey = wrappingKey;
- this.unwrappingKey = unwrappingKey;
- this.envelopeKey = initEnvelopeKey();
- }
+ private static final String DEFAULT_ALGORITHM = "AES/256";
- public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey)
- throws GeneralSecurityException {
- this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap());
- }
+ protected final Key wrappingKey;
+ protected final Key unwrappingKey;
+ private final SecretKey envelopeKey;
- public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey,
- Map description) throws GeneralSecurityException {
- super(macKey, description);
- this.wrappingKey = wrappingKey;
- this.unwrappingKey = unwrappingKey;
- this.envelopeKey = initEnvelopeKey();
- }
+ public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair)
+ throws GeneralSecurityException {
+ this(wrappingKey, unwrappingKey, signingPair, Collections.