Skip to content

Commit b16af24

Browse files
add CMM examples (#170)
1 parent ebda774 commit b16af24

File tree

4 files changed

+508
-0
lines changed

4 files changed

+508
-0
lines changed

src/examples/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
5454
* How to combine AWS KMS with an offline escrow key
5555
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/multi/AwsKmsWithEscrow.java)
5656
* [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/multi/AwsKmsWithEscrow.java)
57+
* How to reuse data keys across multiple messages
58+
* [with the caching cryptographic materials manager](./java/com/amazonaws/crypto/examples/cryptomaterialsmanager/caching/SimpleCache.java)
59+
* How to restrict algorithm suites
60+
* [with a custom cryptographic materials manager](./java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/AlgorithmSuiteEnforcement.java)
61+
* How to require encryption context fields
62+
* [with a custom cryptographic materials manager](./java/com/amazonaws/crypto/examples/cryptomaterialsmanager/custom/RequiringEncryptionContextFields.java)
5763

5864
### Keyrings
5965

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package com.amazonaws.crypto.examples.cryptomaterialsmanager.caching;
5+
6+
import com.amazonaws.encryptionsdk.AwsCrypto;
7+
import com.amazonaws.encryptionsdk.AwsCryptoResult;
8+
import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
9+
import com.amazonaws.encryptionsdk.DecryptRequest;
10+
import com.amazonaws.encryptionsdk.EncryptRequest;
11+
import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
12+
import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache;
13+
import com.amazonaws.encryptionsdk.keyrings.Keyring;
14+
import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
15+
import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
16+
17+
import java.util.Arrays;
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
import java.util.concurrent.TimeUnit;
21+
22+
/**
23+
* The default cryptographic materials manager (CMM)
24+
* creates new encryption and decryption materials
25+
* on every call.
26+
* This means every encrypted message is protected by a unique data key,
27+
* but it also means that you need to interact with your key management system
28+
* in order to process any message.
29+
* If this causes performance, operations, or cost issues for you,
30+
* you might benefit from data key caching.
31+
* <p>
32+
* https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/data-key-caching.html
33+
* <p>
34+
* This example shows how to configure the caching CMM
35+
* to reuse data keys across multiple encrypted messages.
36+
* <p>
37+
* In this example, we use an AWS KMS customer master key (CMK),
38+
* but you can use other key management options with the AWS Encryption SDK.
39+
* For examples that demonstrate how to use other key management configurations,
40+
* see the 'keyring' and 'masterkeyprovider' directories.
41+
* <p>
42+
* In this example, we use the one-step encrypt and decrypt APIs.
43+
*/
44+
public class SimpleCache {
45+
46+
/**
47+
* Demonstrate an encrypt/decrypt cycle using the caching cryptographic materials manager.
48+
*
49+
* @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys
50+
* @param sourcePlaintext Plaintext to encrypt
51+
*/
52+
public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) {
53+
// Instantiate the AWS Encryption SDK.
54+
final AwsCrypto awsEncryptionSdk = new AwsCrypto();
55+
56+
// Prepare your encryption context.
57+
// https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
58+
final Map<String, String> encryptionContext = new HashMap<>();
59+
encryptionContext.put("encryption", "context");
60+
encryptionContext.put("is not", "secret");
61+
encryptionContext.put("but adds", "useful metadata");
62+
encryptionContext.put("that can help you", "be confident that");
63+
encryptionContext.put("the data you are handling", "is what you think it is");
64+
65+
// Create the keyring that determines how your data keys are protected.
66+
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
67+
68+
// Create the caching cryptographic materials manager using your keyring.
69+
final CryptoMaterialsManager cmm = CachingCryptoMaterialsManager.newBuilder()
70+
.withKeyring(keyring)
71+
// The cache is where the caching CMM stores the materials.
72+
//
73+
// LocalCryptoMaterialsCache gives you a local, in-memory, cache.
74+
.withCache(new LocalCryptoMaterialsCache(100))
75+
// Max Age determines how long the caching CMM will reuse materials.
76+
//
77+
// This example uses two minutes.
78+
// In production, always chose as small a value as possible
79+
// that works for your requirements.
80+
.withMaxAge(2, TimeUnit.MINUTES)
81+
// Message Use Limit determines how many messages
82+
// the caching CMM will protect with the same materials.
83+
//
84+
// In production, always choose as small a value as possible
85+
// that works for your requirements.
86+
.withMessageUseLimit(10)
87+
.build();
88+
89+
// Encrypt your plaintext data.
90+
final AwsCryptoResult<byte[]> encryptResult = awsEncryptionSdk.encrypt(
91+
EncryptRequest.builder()
92+
.cryptoMaterialsManager(cmm)
93+
.encryptionContext(encryptionContext)
94+
.plaintext(sourcePlaintext).build());
95+
final byte[] ciphertext = encryptResult.getResult();
96+
97+
// Demonstrate that the ciphertext and plaintext are different.
98+
assert !Arrays.equals(ciphertext, sourcePlaintext);
99+
100+
// Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
101+
//
102+
// You do not need to specify the encryption context on decrypt because
103+
// the header of the encrypted message includes the encryption context.
104+
final AwsCryptoResult<byte[]> decryptResult = awsEncryptionSdk.decrypt(
105+
DecryptRequest.builder()
106+
.cryptoMaterialsManager(cmm)
107+
.ciphertext(ciphertext).build());
108+
final byte[] decrypted = decryptResult.getResult();
109+
110+
// Demonstrate that the decrypted plaintext is identical to the original plaintext.
111+
assert Arrays.equals(decrypted, sourcePlaintext);
112+
113+
// Verify that the encryption context used in the decrypt operation includes
114+
// the encryption context that you specified when encrypting.
115+
// The AWS Encryption SDK can add pairs, so don't require an exact match.
116+
//
117+
// In production, always use a meaningful encryption context.
118+
encryptionContext.forEach((k, v) -> {
119+
assert v.equals(decryptResult.getEncryptionContext().get(k));
120+
});
121+
}
122+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package com.amazonaws.crypto.examples.cryptomaterialsmanager.custom;
5+
6+
import com.amazonaws.encryptionsdk.AwsCrypto;
7+
import com.amazonaws.encryptionsdk.AwsCryptoResult;
8+
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
9+
import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
10+
import com.amazonaws.encryptionsdk.DecryptRequest;
11+
import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager;
12+
import com.amazonaws.encryptionsdk.EncryptRequest;
13+
import com.amazonaws.encryptionsdk.keyrings.Keyring;
14+
import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
15+
import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
16+
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
17+
import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
18+
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
19+
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
20+
21+
import java.util.Arrays;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.HashSet;
25+
import java.util.Map;
26+
import java.util.Set;
27+
28+
/**
29+
* The AWS Encryption SDK supports several different algorithm suites
30+
* that offer different security properties.
31+
* <p>
32+
* https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html
33+
* <p>
34+
* By default, the AWS Encryption SDK will let you use any of these,
35+
* but you might want to restrict that further.
36+
* <p>
37+
* We recommend that you use the default algorithm suite,
38+
* which uses AES-GCM with 256-bit keys, HKDF, and ECDSA message signing.
39+
* If your readers and writers have the same permissions,
40+
* you might want to omit the message signature for faster operation.
41+
* For more information about choosing a signed or unsigned algorithm suite,
42+
* see the AWS Encryption SDK developer guide:
43+
* <p>
44+
* https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html#other-algorithms
45+
* <p>
46+
* This example shows how you can make a custom cryptographic materials manager (CMM)
47+
* that only allows encrypt requests that either specify one of these two algorithm suites
48+
* or do not specify an algorithm suite, in which case the default CMM uses the default algorithm suite.
49+
*/
50+
public class AlgorithmSuiteEnforcement {
51+
52+
/**
53+
* Indicates that an unsupported algorithm suite was requested.
54+
*/
55+
static class UnapprovedAlgorithmSuiteException extends RuntimeException {
56+
UnapprovedAlgorithmSuiteException() {
57+
super("Unapproved algorithm suite requested!");
58+
}
59+
}
60+
61+
/**
62+
* Only allow encryption requests for approved algorithm suites.
63+
*/
64+
static class RequireApprovedAlgorithmSuitesCryptoMaterialsManager implements CryptoMaterialsManager {
65+
66+
private final CryptoMaterialsManager cmm;
67+
private final Set<CryptoAlgorithm> ALLOWED_ALGORITHM_SUITES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
68+
CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, // the default algorithm suite
69+
CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256))); // the recommended unsigned algorithm suite
70+
71+
/**
72+
* Set up the inner cryptographic materials manager using the provided keyring.
73+
*
74+
* @param keyring Keyring to use in the inner cryptographic materials manager
75+
*/
76+
RequireApprovedAlgorithmSuitesCryptoMaterialsManager(Keyring keyring) {
77+
// Wrap the provided keyring in the default cryptographic materials manager (CMM).
78+
//
79+
// This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
80+
// do if you provide a keyring instead of a CMM.
81+
cmm = new DefaultCryptoMaterialsManager(keyring);
82+
}
83+
84+
/**
85+
* Block any requests that include an unapproved algorithm suite.
86+
*/
87+
@Override
88+
public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
89+
if (request.getRequestedAlgorithm() != null && !ALLOWED_ALGORITHM_SUITES.contains(request.getRequestedAlgorithm())) {
90+
throw new UnapprovedAlgorithmSuiteException();
91+
}
92+
93+
return cmm.getMaterialsForEncrypt(request);
94+
}
95+
96+
/**
97+
* Be more permissive on decrypt and just pass through.
98+
*/
99+
@Override
100+
public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
101+
return cmm.decryptMaterials(request);
102+
}
103+
}
104+
105+
106+
/**
107+
* Demonstrate an encrypt/decrypt cycle using a custom cryptographic materials manager that filters requests.
108+
*
109+
* @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys
110+
* @param sourcePlaintext Plaintext to encrypt
111+
*/
112+
public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) {
113+
// Instantiate the AWS Encryption SDK.
114+
final AwsCrypto awsEncryptionSdk = new AwsCrypto();
115+
116+
// Prepare your encryption context.
117+
// https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
118+
final Map<String, String> encryptionContext = new HashMap<>();
119+
encryptionContext.put("encryption", "context");
120+
encryptionContext.put("is not", "secret");
121+
encryptionContext.put("but adds", "useful metadata");
122+
encryptionContext.put("that can help you", "be confident that");
123+
encryptionContext.put("the data you are handling", "is what you think it is");
124+
125+
// Create the keyring that determines how your data keys are protected.
126+
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
127+
128+
// Create the algorithm suite restricting cryptographic materials manager using your keyring.
129+
final CryptoMaterialsManager cmm = new RequireApprovedAlgorithmSuitesCryptoMaterialsManager(keyring);
130+
131+
// Demonstrate that the algorithm suite restricting CMM will not let you use an unapproved algorithm suite.
132+
awsEncryptionSdk.setEncryptionAlgorithm(CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF);
133+
134+
try {
135+
awsEncryptionSdk.encrypt(
136+
EncryptRequest.builder()
137+
.cryptoMaterialsManager(cmm)
138+
.encryptionContext(encryptionContext)
139+
.plaintext(sourcePlaintext).build());
140+
// The algorithm suite restricting CMM keeps this from happening.
141+
throw new AssertionError("The algorithm suite restricting CMM does not let this happen!");
142+
} catch (UnapprovedAlgorithmSuiteException ex) {
143+
// You asked for an unapproved algorithm suite.
144+
// Reaching this point means everything is working as expected.
145+
}
146+
147+
// Set an approved algorithm suite.
148+
awsEncryptionSdk.setEncryptionAlgorithm(CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384);
149+
150+
// Encrypt your plaintext data.
151+
final AwsCryptoResult<byte[]> encryptResult = awsEncryptionSdk.encrypt(
152+
EncryptRequest.builder()
153+
.cryptoMaterialsManager(cmm)
154+
.encryptionContext(encryptionContext)
155+
.plaintext(sourcePlaintext).build());
156+
final byte[] ciphertext = encryptResult.getResult();
157+
158+
// Demonstrate that the ciphertext and plaintext are different.
159+
assert !Arrays.equals(ciphertext, sourcePlaintext);
160+
161+
// Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
162+
//
163+
// You do not need to specify the encryption context on decrypt because
164+
// the header of the encrypted message includes the encryption context.
165+
final AwsCryptoResult<byte[]> decryptResult = awsEncryptionSdk.decrypt(
166+
DecryptRequest.builder()
167+
.cryptoMaterialsManager(cmm)
168+
.ciphertext(ciphertext).build());
169+
final byte[] decrypted = decryptResult.getResult();
170+
171+
// Demonstrate that the decrypted plaintext is identical to the original plaintext.
172+
assert Arrays.equals(decrypted, sourcePlaintext);
173+
174+
// Verify that the encryption context used in the decrypt operation includes
175+
// the encryption context that you specified when encrypting.
176+
// The AWS Encryption SDK can add pairs, so don't require an exact match.
177+
//
178+
// In production, always use a meaningful encryption context.
179+
encryptionContext.forEach((k, v) -> {
180+
assert v.equals(decryptResult.getEncryptionContext().get(k));
181+
});
182+
}
183+
}

0 commit comments

Comments
 (0)