Skip to content

Commit 676407a

Browse files
Add an example for replicating the behavior of the AWS KMS MKP with the AWS KMS keyring (#178)
* Add an example for replicating the behavior of the AWS KMS MKP * Rewording based on feedback * Updating based on python example updates * Matching python wording * Throw a more useful exception when trying to use the RawRsaKeyring to encrypt without a public key (#180) * Throw a more useful exception when trying to use the RawRsaKeyring to encrypt without a public key * Add a test for trying to decrypt without a private key * Update src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java Co-Authored-By: Matt Bullock <[email protected]> Co-authored-by: Matt Bullock <[email protected]>
1 parent 997e80b commit 676407a

File tree

5 files changed

+166
-6
lines changed

5 files changed

+166
-6
lines changed

src/examples/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
3939
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java)
4040
* How to decrypt with a preferred region but failover to others
4141
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java)
42+
* How to reproduce the behavior of an AWS KMS master key provider
43+
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java)
4244
* Using raw wrapping keys
4345
* How to use a raw AES wrapping key
4446
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/rawaes/RawAes.java)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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.keyring.awskms;
5+
6+
import com.amazonaws.encryptionsdk.AwsCrypto;
7+
import com.amazonaws.encryptionsdk.AwsCryptoResult;
8+
import com.amazonaws.encryptionsdk.DecryptRequest;
9+
import com.amazonaws.encryptionsdk.EncryptRequest;
10+
import com.amazonaws.encryptionsdk.keyrings.Keyring;
11+
import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
12+
import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
13+
import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider;
14+
15+
import java.util.ArrayList;
16+
import java.util.Arrays;
17+
import java.util.HashMap;
18+
import java.util.List;
19+
import java.util.Map;
20+
21+
import static java.util.stream.Collectors.toList;
22+
23+
/**
24+
* You might have used master key providers to protect your data keys
25+
* in an earlier version of the AWS Encryption SDK.
26+
* This example shows how to configure a keyring that behaves like an AWS KMS master key provider.
27+
* <p>
28+
* The AWS Encryption SDK provided an AWS KMS master key provider for
29+
* interacting with AWS Key Management Service (AWS KMS).
30+
* On encrypt, the AWS KMS master key provider behaves like the AWS KMS keyring
31+
* and encrypts with all CMKs that you identify.
32+
* However, on decrypt,
33+
* the AWS KMS master key provider reviews each encrypted data key (EDK).
34+
* If the EDK was encrypted under an AWS KMS CMK,
35+
* the AWS KMS master key provider attempts to decrypt it.
36+
* Whether decryption succeeds depends on permissions on the CMK.
37+
* This continues until the AWS KMS master key provider either runs out of EDKs
38+
* or succeeds in decrypting an EDK.
39+
* We have found that separating these two behaviors
40+
* makes the expected behavior clearer,
41+
* so that is what we did with the AWS KMS keyring and the AWS KMS discovery keyring.
42+
* However, as you migrate from master key providers to keyrings,
43+
* you might want a keyring that behaves like the AWS KMS master key provider.
44+
* <p>
45+
* For more examples of how to use the AWS KMS keyring,
46+
* see the 'keyring/awskms' directory.
47+
*/
48+
public class ActLikeAwsKmsMasterKeyProvider {
49+
50+
/**
51+
* Demonstrate how to create a keyring that behaves like an AWS KMS master key provider.
52+
*
53+
* @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys
54+
* @param awsKmsAdditionalCmks Additional ARNs of secondary AWS KMS CMKs
55+
* @param sourcePlaintext Plaintext to encrypt
56+
*/
57+
public static void run(final AwsKmsCmkId awsKmsCmk, final List<AwsKmsCmkId> awsKmsAdditionalCmks, byte[] sourcePlaintext) {
58+
// Instantiate the AWS Encryption SDK.
59+
final AwsCrypto awsEncryptionSdk = new AwsCrypto();
60+
61+
// Prepare your encryption context.
62+
// https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
63+
final Map<String, String> encryptionContext = new HashMap<>();
64+
encryptionContext.put("encryption", "context");
65+
encryptionContext.put("is not", "secret");
66+
encryptionContext.put("but adds", "useful metadata");
67+
encryptionContext.put("that can help you", "be confident that");
68+
encryptionContext.put("the data you are handling", "is what you think it is");
69+
70+
// This is the master key provider whose behavior we want to reproduce.
71+
//
72+
// When encrypting, this master key provider generates the data key using the first CMK in the list
73+
// and encrypts the data key using all specified CMKs.
74+
// However, when decrypting, this master key provider attempts to decrypt
75+
// any data keys that were encrypted under an AWS KMS CMK.
76+
final List<String> masterKeyProviderCmks = new ArrayList<>();
77+
masterKeyProviderCmks.add(awsKmsCmk.toString());
78+
masterKeyProviderCmks.addAll(awsKmsAdditionalCmks.stream().map(AwsKmsCmkId::toString).collect(toList()));
79+
final KmsMasterKeyProvider masterKeyProviderToReplicate = KmsMasterKeyProvider.builder()
80+
.withKeysForEncryption(masterKeyProviderCmks).build();
81+
82+
// Create a CMK keyring that encrypts and decrypts using the specified AWS KMS CMKs.
83+
//
84+
// This keyring reproduces the encryption behavior of the AWS KMS master key provider.
85+
//
86+
// The AWS KMS keyring requires that you explicitly identify the CMK
87+
// that you want the keyring to use to generate the data key.
88+
final Keyring cmkKeyring = StandardKeyrings.awsKmsBuilder()
89+
.generatorKeyId(awsKmsCmk)
90+
.keyIds(awsKmsAdditionalCmks)
91+
.build();
92+
93+
// Create an AWS KMS discovery keyring that will attempt to decrypt
94+
// any data keys that were encrypted under an AWS KMS CMK.
95+
final Keyring discoveryKeyring = StandardKeyrings.awsKmsDiscoveryBuilder().build();
96+
97+
// Combine the CMK and discovery keyrings
98+
// to create a keyring that behaves like an AWS KMS master key provider.
99+
//
100+
// The CMK keyring reproduces the encryption behavior
101+
// and the discovery keyring reproduces the decryption behavior.
102+
// This also means that it does not matter if the CMK keyring fails to decrypt.
103+
// For example, if you configured the CMK keyring with aliases,
104+
// it works on encrypt but fails to match any encrypted data keys on decrypt
105+
// because the serialized key name is the resulting CMK ARN rather than the alias name.
106+
// However, because the discovery keyring attempts to decrypt any AWS KMS-encrypted
107+
// data keys that it finds, the message still decrypts successfully.
108+
final Keyring keyring = StandardKeyrings.multi(cmkKeyring, discoveryKeyring);
109+
110+
// Encrypt your plaintext data.
111+
final AwsCryptoResult<byte[]> encryptResult = awsEncryptionSdk.encrypt(
112+
EncryptRequest.builder()
113+
.keyring(keyring)
114+
.encryptionContext(encryptionContext)
115+
.plaintext(sourcePlaintext).build());
116+
final byte[] ciphertext = encryptResult.getResult();
117+
118+
// Demonstrate that the ciphertext and plaintext are different.
119+
assert !Arrays.equals(ciphertext, sourcePlaintext);
120+
121+
// Decrypt your encrypted data using the same keyring you used on encrypt.
122+
//
123+
// You do not need to specify the encryption context on decrypt because
124+
// the header of the encrypted message includes the encryption context.
125+
final AwsCryptoResult<byte[]> decryptResult = awsEncryptionSdk.decrypt(
126+
DecryptRequest.builder()
127+
.keyring(keyring)
128+
.ciphertext(ciphertext).build());
129+
final byte[] decrypted = decryptResult.getResult();
130+
131+
// Demonstrate that the decrypted plaintext is identical to the original plaintext.
132+
assert Arrays.equals(decrypted, sourcePlaintext);
133+
134+
// Verify that the encryption context used in the decrypt operation includes
135+
// the encryption context that you specified when encrypting.
136+
// The AWS Encryption SDK can add pairs, so don't require an exact match.
137+
//
138+
// In production, always use a meaningful encryption context.
139+
encryptionContext.forEach((k, v) -> {
140+
assert v.equals(decryptResult.getEncryptionContext().get(k));
141+
});
142+
}
143+
}

src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecrypt.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@
2121
* However, sometimes you need more flexibility on decrypt,
2222
* especially when you don't know which CMKs were used to encrypt a message.
2323
* To address this need, you can use an AWS KMS discovery keyring.
24-
* The AWS KMS discovery keyring does nothing on encrypt
25-
* but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK.
24+
* The AWS KMS discovery keyring does nothing on encrypt.
25+
* On decrypt it reviews each encrypted data key (EDK).
26+
* If an EDK was encrypted under an AWS KMS CMK,
27+
* the AWS KMS discovery keyring attempts to decrypt it.
28+
* Whether decryption succeeds depends on permissions on the CMK.
29+
* This continues until the AWS KMS discovery keyring either runs out of EDKs
30+
* or succeeds in decrypting an EDK.
2631
* <p>
2732
* This example shows how to configure and use an AWS KMS discovery keyring.
2833
* <p>

src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@
2525
* However, sometimes you need more flexibility on decrypt,
2626
* especially if you don't know which CMK was used to encrypt a message.
2727
* To address this need, you can use an AWS KMS discovery keyring.
28-
* The AWS KMS discovery keyring does nothing on encrypt
29-
* but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK.
28+
* The AWS KMS discovery keyring does nothing on encrypt.
29+
* On decrypt it reviews each encrypted data key (EDK).
30+
* If an EDK was encrypted under an AWS KMS CMK,
31+
* the AWS KMS discovery keyring attempts to decrypt it.
32+
* Whether decryption succeeds depends on permissions on the CMK.
33+
* This continues until the AWS KMS discovery keyring either runs out of EDKs
34+
* or succeeds in decrypting an EDK.
3035
* <p>
3136
* However, sometimes you need to be a *bit* more restrictive than that.
3237
* To address this need, you can use a client supplier that restricts the regions an AWS KMS keyring can talk to.

src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@
2525
* However, sometimes you need more flexibility on decrypt,
2626
* especially if you might not know beforehand which CMK was used to encrypt a message.
2727
* To address this need, you can use an AWS KMS discovery keyring.
28-
* The AWS KMS discovery keyring will do nothing on encrypt
29-
* but will attempt to decrypt *any* data keys that were encrypted under an AWS KMS CMK.
28+
* The AWS KMS discovery keyring does nothing on encrypt.
29+
* On decrypt it reviews each encrypted data key (EDK).
30+
* If an EDK was encrypted under an AWS KMS CMK,
31+
* the AWS KMS discovery keyring attempts to decrypt it.
32+
* Whether decryption succeeds depends on permissions on the CMK.
33+
* This continues until the AWS KMS discovery keyring either runs out of EDKs
34+
* or succeeds in decrypting an EDK.
3035
* <p>
3136
* However, sometimes you need to be a *bit* more restrictive than that.
3237
* To address this need, you can use a client supplier to restrict what regions an AWS KMS keyring can talk to.

0 commit comments

Comments
 (0)