Skip to content

Commit 511be10

Browse files
authored
docs: Move Keystore operations to separate examples (#143)
1 parent 9bbc3cc commit 511be10

14 files changed

+326
-114
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package software.aws.cryptography.examples;
2+
3+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
4+
import software.amazon.awssdk.services.kms.KmsClient;
5+
import software.amazon.cryptography.keystore.KeyStore;
6+
import software.amazon.cryptography.keystore.model.KMSConfiguration;
7+
import software.amazon.cryptography.keystore.model.KeyStoreConfig;
8+
9+
/*
10+
The Hierarchical Keyring Example and Searchable Encryption Examples
11+
rely on the existence of a DDB-backed key store with pre-existing
12+
branch key material or beacon key material.
13+
14+
See the "Create KeyStore Table Example" for how to first set up
15+
the DDB Table that will back this KeyStore.
16+
17+
This example demonstrates configuring a KeyStore and then
18+
using a helper method to create a branch key and beacon key
19+
that share the same Id, then return that Id.
20+
We will always create a new beacon key alongside a new branch key,
21+
even if you are not using searchable encryption.
22+
23+
This key creation should occur within your control plane.
24+
*/
25+
public class CreateKeyStoreKeyExample {
26+
27+
public static String KeyStoreCreateKey(String keyStoreTableName,
28+
String logicalKeyStoreName,
29+
String kmsKeyArn) {
30+
// 1. Configure your KeyStore resource.
31+
// This SHOULD be the same configuration that was used to create the DDB table
32+
// in the "Create KeyStore Table Example".
33+
final KeyStore keystore = KeyStore.builder().KeyStoreConfig(
34+
KeyStoreConfig.builder()
35+
.ddbClient(DynamoDbClient.create())
36+
.ddbTableName(keyStoreTableName)
37+
.logicalKeyStoreName(logicalKeyStoreName)
38+
.kmsClient(KmsClient.create())
39+
.kmsConfiguration(KMSConfiguration.builder()
40+
.kmsKeyArn(kmsKeyArn)
41+
.build())
42+
.build()).build();
43+
44+
// 2. Create a new branch key and beacon key in our KeyStore.
45+
// Both the branch key and the beacon key will share an Id.
46+
// This creation is eventually consistent.
47+
final String branchKeyId = keystore.CreateKey().branchKeyIdentifier();
48+
49+
return branchKeyId;
50+
}
51+
52+
public static void main(final String[] args) {
53+
if (args.length <= 1) {
54+
throw new IllegalArgumentException("To run this example, include the keyStoreTableName, logicalKeyStoreName, and kmsKeyArn in args");
55+
}
56+
final String keyStoreTableName = args[0];
57+
final String logicalKeyStoreName = args[1];
58+
final String kmsKeyArn = args[1];
59+
KeyStoreCreateKey(keyStoreTableName, logicalKeyStoreName, kmsKeyArn);
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package software.aws.cryptography.examples;
2+
3+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
4+
import software.amazon.awssdk.services.kms.KmsClient;
5+
import software.amazon.cryptography.keystore.KeyStore;
6+
import software.amazon.cryptography.keystore.model.CreateKeyStoreInput;
7+
import software.amazon.cryptography.keystore.model.KMSConfiguration;
8+
import software.amazon.cryptography.keystore.model.KeyStoreConfig;
9+
10+
/*
11+
The Hierarchical Keyring Example and Searchable Encryption Examples
12+
rely on the existence of a DDB-backed key store with pre-existing
13+
branch key material or beacon key material.
14+
15+
This example demonstrates configuring a KeyStore and then
16+
using a helper method to create the DDB table that will be
17+
used to persist branch keys and beacons keys for this KeyStore.
18+
19+
This table creation should occur within your control plane. This
20+
only needs to occur once. While not demonstrated in this example,
21+
you should additionally use the `VersionKey` API on the KeyStore
22+
to periodically rotate your branch key material.
23+
*/
24+
public class CreateKeyStoreTableExample {
25+
26+
// Create KeyStore Table Example
27+
public static void KeyStoreCreateTable(String keyStoreTableName,
28+
String logicalKeyStoreName,
29+
String kmsKeyArn) {
30+
// 1. Configure your KeyStore resource.
31+
// `ddbTableName` is the name you want for the DDB table that
32+
// will back your keystore.
33+
// `kmsKeyArn` is the KMS Key that will protect your branch keys and beacon keys
34+
// when they are stored in your DDB table.
35+
final KeyStore keystore = KeyStore.builder().KeyStoreConfig(
36+
KeyStoreConfig.builder()
37+
.ddbClient(DynamoDbClient.create())
38+
.ddbTableName(keyStoreTableName)
39+
.logicalKeyStoreName(logicalKeyStoreName)
40+
.kmsClient(KmsClient.create())
41+
.kmsConfiguration(KMSConfiguration.builder()
42+
.kmsKeyArn(kmsKeyArn)
43+
.build())
44+
.build()).build();
45+
46+
// 2. Create the DynamoDb table that will store the branch keys and beacon keys.
47+
// This checks if the correct table already exists at `ddbTableName`
48+
// by using the DescribeTable API. If no table exists,
49+
// it will create one. If a table exists, it will verify
50+
// the table's configuration and will error if the configuration is incorrect.
51+
keystore.CreateKeyStore(CreateKeyStoreInput.builder().build());
52+
53+
// It may take a couple minutes for the table to become ACTIVE,
54+
// at which point it is ready to store branch and beacon keys.
55+
// See the Create KeyStore Key Example for how to populate
56+
// this table.
57+
}
58+
59+
public static void main(final String[] args) {
60+
if (args.length <= 1) {
61+
throw new IllegalArgumentException("To run this example, include the keyStoreTableName, logicalKeyStoreName, and kmsKeyArn in args");
62+
}
63+
final String keyStoreTableName = args[0];
64+
final String logicalKeyStoreName = args[1];
65+
final String kmsKeyArn = args[2];
66+
KeyStoreCreateTable(keyStoreTableName, logicalKeyStoreName, kmsKeyArn);
67+
}
68+
}

Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/aws/cryptography/examples/keyring/HierarchicalKeyringExample.java

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,6 @@
5050
or you can implement an interface that lets you map the primary key on your items
5151
to the branch key that should be responsible for decrypting that data.
5252
53-
This example first creates the DynamoDb-backed KeyStore,
54-
and creates two branch keys. These are control plane operations that
55-
only need to occur once. While not demonstrated in this example,
56-
you should additionally use the `VersionKey` API on the KeyStore
57-
to periodically rotate your branch key material.
58-
5953
This example then demonstrates configuring a Hierarchical Keyring
6054
with a Branch Key ID Supplier to encrypt and decrypt data for
6155
two separate tenants.
@@ -75,13 +69,17 @@ or you can implement an interface that lets you map the primary key on your item
7569
*/
7670
public class HierarchicalKeyringExample {
7771

78-
public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String keyStoreTableName, String logicalKeyStoreName, String kmsKeyId) {
79-
// Initial KeyStore Setup: Configure a keystore resource to create the table
80-
// that will persist your branch keys, then create two new branch keys.
81-
// This process should occur in your control plane, and returns
82-
// Branch Key IDs that you will need to configure for use in your data plane.
83-
84-
// 1. Configure your KeyStore resource
72+
public static void HierarchicalKeyringGetItemPutItem(
73+
String ddbTableName, String tenant1BranchKeyId, String tenant2BranchKeyId, String keyStoreTableName,
74+
String logicalKeyStoreName, String kmsKeyId) {
75+
// Initial KeyStore Setup: This example requires that you have already
76+
// created your KeyStore, and have populated it with two new branch keys.
77+
// See the "Create KeyStore Table Example" and "Create KeyStore Key Example"
78+
// for an example of how to do this.
79+
80+
// 1. Configure your KeyStore resource.
81+
// This SHOULD be the same configuration that you used
82+
// to initially create and populate your KeyStore.
8583
final KeyStore keystore = KeyStore.builder().KeyStoreConfig(
8684
KeyStoreConfig.builder()
8785
.ddbClient(DynamoDbClient.create())
@@ -93,32 +91,20 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
9391
.build())
9492
.build()).build();
9593

96-
// 2. Create the DynamoDb table to store the branch keys
97-
keystore.CreateKeyStore(CreateKeyStoreInput.builder().build());
98-
99-
// 3. Create two branch keys for our two tenants.
100-
// Use the same KMS Key to protect both keys.
101-
final String tenant1BranchKey = keystore.CreateKey().branchKeyIdentifier();
102-
final String tenant2BranchKey = keystore.CreateKey().branchKeyIdentifier();
103-
104-
// Data Plane: Given the above setup done in our control plane, we have created
105-
// the resources required to encrypt and decrypt items for our two tenants by
106-
// configuring our AWS SDK Client to use a Hierarchical Keyring.
107-
108-
// 4. Create a Branch Key ID Supplier. See ExampleBranchKeyIdSupplier in this directory.
94+
// 2. Create a Branch Key ID Supplier. See ExampleBranchKeyIdSupplier in this directory.
10995
final DynamoDbEncryption ddbEnc = DynamoDbEncryption.builder()
11096
.DynamoDbEncryptionConfig(DynamoDbEncryptionConfig.builder().build())
11197
.build();
11298
final IBranchKeyIdSupplier branchKeyIdSupplier = ddbEnc.CreateDynamoDbEncryptionBranchKeyIdSupplier(
11399
CreateDynamoDbEncryptionBranchKeyIdSupplierInput.builder()
114-
.ddbKeyBranchKeyIdSupplier(new ExampleBranchKeyIdSupplier(tenant1BranchKey, tenant2BranchKey))
100+
.ddbKeyBranchKeyIdSupplier(new ExampleBranchKeyIdSupplier(tenant1BranchKeyId, tenant2BranchKeyId))
115101
.build()).branchKeyIdSupplier();
116102

117-
// 5. Create the Hierarchical Keyring, using the Branch Key ID Supplier above.
103+
// 3. Create the Hierarchical Keyring, using the Branch Key ID Supplier above.
118104
// With this configuration, the AWS SDK Client ultimately configured will be capable
119105
// of encrypting or decrypting items for either tenant (assuming correct KMS access).
120106
// If you want to restrict the client to only encrypt or decrypt for a single tenant,
121-
// configure this Hierarchical Keyring using `.branchKeyId(tenant1BranchKey)` instead
107+
// configure this Hierarchical Keyring using `.branchKeyId(tenant1BranchKeyId)` instead
122108
// of `.branchKeyIdSupplier(branchKeyIdSupplier)`.
123109
final MaterialProviders matProv = MaterialProviders.builder()
124110
.MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
@@ -131,7 +117,7 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
131117
.build();
132118
final IKeyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
133119

134-
// 6. Configure which attributes are encrypted and/or signed when writing new items.
120+
// 4. Configure which attributes are encrypted and/or signed when writing new items.
135121
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
136122
// we must explicitly configure how they should be treated during item encryption:
137123
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
@@ -142,7 +128,7 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
142128
attributeActions.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY
143129
attributeActions.put("tenant_sensitive_data", CryptoAction.ENCRYPT_AND_SIGN);
144130

145-
// 3. Configure which attributes we expect to be included in the signature
131+
// 5. Configure which attributes we expect to be included in the signature
146132
// when reading items. There are two options for configuring this:
147133
//
148134
// - (Recommended) Configure `allowedUnauthenticatedAttributesPrefix`:
@@ -172,7 +158,7 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
172158
// add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
173159
final String unauthAttrPrefix = ":";
174160

175-
// 7. Create the DynamoDb Encryption configuration for the table we will be writing to.
161+
// 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
176162
final Map<String, DynamoDbTableEncryptionConfig> tableConfigs = new HashMap<>();
177163
final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig.builder()
178164
.logicalTableName(ddbTableName)
@@ -184,27 +170,27 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
184170
.build();
185171
tableConfigs.put(ddbTableName, config);
186172

187-
// 8. Create the DynamoDb Encryption Interceptor
173+
// 7. Create the DynamoDb Encryption Interceptor
188174
DynamoDbEncryptionInterceptor encryptionInterceptor = DynamoDbEncryptionInterceptor.builder()
189175
.config(DynamoDbTablesEncryptionConfig.builder()
190176
.tableEncryptionConfigs(tableConfigs)
191177
.build())
192178
.build();
193179

194-
// 9. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
180+
// 8. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
195181
final DynamoDbClient ddb = DynamoDbClient.builder()
196182
.overrideConfiguration(
197183
ClientOverrideConfiguration.builder()
198184
.addExecutionInterceptor(encryptionInterceptor)
199185
.build())
200186
.build();
201187

202-
// 10. Put an item into our table using the above client.
203-
// Before the item gets sent to DynamoDb, it will be encrypted
204-
// client-side, according to our configuration.
205-
// Because the item we are writing uses "tenantId1" as our partition value,
206-
// based on the code we wrote in the ExampleBranchKeySupplier,
207-
// `tenant1BranchKey` will be used to encrypt this item.
188+
// 9. Put an item into our table using the above client.
189+
// Before the item gets sent to DynamoDb, it will be encrypted
190+
// client-side, according to our configuration.
191+
// Because the item we are writing uses "tenantId1" as our partition value,
192+
// based on the code we wrote in the ExampleBranchKeySupplier,
193+
// `tenant1BranchKeyId` will be used to encrypt this item.
208194
final HashMap<String, AttributeValue> item = new HashMap<>();
209195
item.put("partition_key", AttributeValue.builder().s("tenant1Id").build());
210196
item.put("sort_key", AttributeValue.builder().n("0").build());
@@ -220,12 +206,12 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
220206
// Demonstrate that PutItem succeeded
221207
assert 200 == putResponse.sdkHttpResponse().statusCode();
222208

223-
// 11. Get the item back from our table using the same client.
209+
// 10. Get the item back from our table using the same client.
224210
// The client will decrypt the item client-side, and return
225211
// back the original item.
226212
// Because the returned item's partition value is "tenantId1",
227213
// based on the code we wrote in the ExampleBranchKeySupplier,
228-
// `tenant1BranchKey` will be used to decrypt this item.
214+
// `tenant1BranchKeyId` will be used to decrypt this item.
229215
final HashMap<String, AttributeValue> keyToGet = new HashMap<>();
230216
keyToGet.put("partition_key", AttributeValue.builder().s("tenant1Id").build());
231217
keyToGet.put("sort_key", AttributeValue.builder().n("0").build());
@@ -245,12 +231,15 @@ public static void HierarchicalKeyringGetItemPutItem(String ddbTableName, String
245231

246232
public static void main(final String[] args) {
247233
if (args.length <= 0) {
248-
throw new IllegalArgumentException("To run this example, include the ddbTable, keyStoreTableName, and kmsKeyId in args");
234+
throw new IllegalArgumentException("To run this example, include the ddbTable, tenant1BranchKeyId, "
235+
+ "tenant2BranchKeyId, keyStoreTableName, logicalKeyStoreName, and kmsKeyId in args");
249236
}
250237
final String ddbTableName = args[0];
251-
final String keyStoreTableName = args[1];
252-
final String logicalKeyStoreTableName = args[2];
253-
final String kmsKeyId = args[3];
254-
HierarchicalKeyringGetItemPutItem(ddbTableName, keyStoreTableName, logicalKeyStoreTableName, kmsKeyId);
238+
final String tenant1BranchKeyId = args[1];
239+
final String tenant2BranchKeyId = args[2];
240+
final String keyStoreTableName = args[3];
241+
final String logicalKeyStoreName = args[4];
242+
final String kmsKeyId = args[5];
243+
HierarchicalKeyringGetItemPutItem(ddbTableName, tenant1BranchKeyId, tenant2BranchKeyId, keyStoreTableName, logicalKeyStoreName, kmsKeyId);
255244
}
256245
}

0 commit comments

Comments
 (0)