Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e9f8409

Browse files
committedAug 7, 2024··
java
1 parent 5a77786 commit e9f8409

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package software.amazon.cryptography.examples;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
6+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
7+
import software.amazon.awssdk.services.dynamodb.model.*;
8+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.DynamoDbEncryptionInterceptor;
9+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTableEncryptionConfig;
10+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTablesEncryptionConfig;
11+
import software.amazon.cryptography.dbencryptionsdk.structuredencryption.model.CryptoAction;
12+
import software.amazon.cryptography.materialproviders.IKeyring;
13+
import software.amazon.cryptography.materialproviders.MaterialProviders;
14+
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsMrkMultiKeyringInput;
15+
import software.amazon.cryptography.materialproviders.model.DBEAlgorithmSuiteId;
16+
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;
17+
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
18+
import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
19+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.transforms.model.CollectionOfErrors;
20+
21+
/*
22+
This example sets up DynamoDb Encryption for the AWS SDK client
23+
and uses the low level PutItem and GetItem DDB APIs to demonstrate
24+
putting a client-side encrypted item into DynamoDb
25+
and then retrieving and decrypting that item from DynamoDb.
26+
27+
Running this example requires access to the DDB Table whose name
28+
is provided in CLI arguments.
29+
This table must be configured with the following
30+
primary key configuration:
31+
- Partition key is named "partition_key" with type (S)
32+
- Sort key is named "sort_key" with type (N)
33+
*/
34+
public class ScanErrorExample {
35+
36+
public static void ScanError(String kmsKeyId, String ddbTableName) {
37+
// 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
38+
// For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use.
39+
// We will use the `CreateMrkMultiKeyring` method to create this keyring,
40+
// as it will correctly handle both single region and Multi-Region KMS Keys.
41+
final MaterialProviders matProv = MaterialProviders
42+
.builder()
43+
.MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
44+
.build();
45+
final CreateAwsKmsMrkMultiKeyringInput keyringInput =
46+
CreateAwsKmsMrkMultiKeyringInput.builder().generator(kmsKeyId).build();
47+
final IKeyring kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(
48+
keyringInput
49+
);
50+
51+
// 2. Configure which attributes are encrypted and/or signed when writing new items.
52+
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
53+
// we must explicitly configure how they should be treated during item encryption:
54+
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
55+
// - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
56+
// - DO_NOTHING: The attribute is not encrypted and not included in the signature
57+
final Map<String, CryptoAction> attributeActionsOnEncrypt = new HashMap<>();
58+
attributeActionsOnEncrypt.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY
59+
attributeActionsOnEncrypt.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY
60+
attributeActionsOnEncrypt.put("attribute1", CryptoAction.ENCRYPT_AND_SIGN);
61+
attributeActionsOnEncrypt.put("attribute2", CryptoAction.SIGN_ONLY);
62+
attributeActionsOnEncrypt.put(":attribute3", CryptoAction.DO_NOTHING);
63+
64+
// 3. Configure which attributes we expect to be included in the signature
65+
// when reading items. There are two options for configuring this:
66+
//
67+
// - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
68+
// When defining your DynamoDb schema and deciding on attribute names,
69+
// choose a distinguishing prefix (such as ":") for all attributes that
70+
// you do not want to include in the signature.
71+
// This has two main benefits:
72+
// - It is easier to reason about the security and authenticity of data within your item
73+
// when all unauthenticated data is easily distinguishable by their attribute name.
74+
// - If you need to add new unauthenticated attributes in the future,
75+
// you can easily make the corresponding update to your `attributeActionsOnEncrypt`
76+
// and immediately start writing to that new attribute, without
77+
// any other configuration update needed.
78+
// Once you configure this field, it is not safe to update it.
79+
//
80+
// - Configure `allowedUnsignedAttributes`: You may also explicitly list
81+
// a set of attributes that should be considered unauthenticated when encountered
82+
// on read. Be careful if you use this configuration. Do not remove an attribute
83+
// name from this configuration, even if you are no longer writing with that attribute,
84+
// as old items may still include this attribute, and our configuration needs to know
85+
// to continue to exclude this attribute from the signature scope.
86+
// If you add new attribute names to this field, you must first deploy the update to this
87+
// field to all readers in your host fleet before deploying the update to start writing
88+
// with that new attribute.
89+
//
90+
// For this example, we have designed our DynamoDb table such that any attribute name with
91+
// the ":" prefix should be considered unauthenticated.
92+
final String unsignAttrPrefix = ":";
93+
94+
// 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
95+
final Map<String, DynamoDbTableEncryptionConfig> tableConfigs =
96+
new HashMap<>();
97+
final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig
98+
.builder()
99+
.logicalTableName(ddbTableName)
100+
.partitionKeyName("partition_key")
101+
.sortKeyName("sort_key")
102+
.attributeActionsOnEncrypt(attributeActionsOnEncrypt)
103+
.keyring(kmsKeyring)
104+
.allowedUnsignedAttributePrefix(unsignAttrPrefix)
105+
// Specifying an algorithm suite is not required,
106+
// but is done here to demonstrate how to do so.
107+
// We suggest using the
108+
// `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
109+
// which includes AES-GCM with key derivation, signing, and key commitment.
110+
// This is also the default algorithm suite if one is not specified in this config.
111+
// For more information on supported algorithm suites, see:
112+
// https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
113+
.algorithmSuiteId(
114+
DBEAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384
115+
)
116+
.build();
117+
tableConfigs.put(ddbTableName, config);
118+
119+
// 5. Create the DynamoDb Encryption Interceptor
120+
DynamoDbEncryptionInterceptor encryptionInterceptor =
121+
DynamoDbEncryptionInterceptor
122+
.builder()
123+
.config(
124+
DynamoDbTablesEncryptionConfig
125+
.builder()
126+
.tableEncryptionConfigs(tableConfigs)
127+
.build()
128+
)
129+
.build();
130+
131+
// 6. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
132+
final DynamoDbClient ddb = DynamoDbClient
133+
.builder()
134+
.overrideConfiguration(
135+
ClientOverrideConfiguration
136+
.builder()
137+
.addExecutionInterceptor(encryptionInterceptor)
138+
.build()
139+
)
140+
.build();
141+
142+
// 7. Perform a Scan for which some records will not decrypt
143+
Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
144+
expressionAttributeValues.put(
145+
":prefix",
146+
AttributeValue.builder().s("Broken").build()
147+
);
148+
149+
ScanRequest scanRequest = ScanRequest
150+
.builder()
151+
.tableName(ddbTableName)
152+
.filterExpression("begins_with(partition_key, :prefix)")
153+
.expressionAttributeValues(expressionAttributeValues)
154+
.build();
155+
156+
try
157+
{
158+
final ScanResponse scanResponse = ddb.scan(scanRequest);
159+
}
160+
// catch (DynamoDbEncryptionException e)
161+
// {
162+
// System.err.println("Encryptor Error : " + e.Message);
163+
// }
164+
catch (CollectionOfErrors e)
165+
{
166+
System.err.println("Decryption Errors : ");
167+
System.err.println(e.message());
168+
for (Exception element : e.list())
169+
{
170+
System.err.println(element);
171+
}
172+
}
173+
catch (Exception e)
174+
{
175+
System.err.println("Other Error : ");
176+
System.err.println(e);
177+
System.err.println(e.getMessage());
178+
System.err.println(e.getCause());
179+
System.err.println(((CollectionOfErrors) e.getCause()).list());
180+
}
181+
}
182+
183+
public static void main(final String[] args) {
184+
if (args.length < 2) {
185+
throw new IllegalArgumentException(
186+
"To run this example, include the kmsKeyId as args[0] and ddbTableName as args[1]"
187+
);
188+
}
189+
final String kmsKeyId = args[0];
190+
final String ddbTableName = args[1];
191+
ScanError(kmsKeyId, ddbTableName);
192+
}
193+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package software.amazon.cryptography.examples;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class TestScanErrorExample {
6+
7+
@Test
8+
public void ScanError() {
9+
ScanErrorExample.ScanError(
10+
TestUtils.TEST_KMS_KEY_ID,
11+
TestUtils.TEST_DDB_TABLE_NAME
12+
);
13+
}
14+
}

0 commit comments

Comments
 (0)
Please sign in to comment.