Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AWS KMS Keyring(s) refactor/reimplementation #211

Merged
merged 16 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/MultipleRegions.java)
* [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/MultipleRegions.java)
* How to decrypt when you don't know the CMK
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecrypt.java)
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java)
acioc marked this conversation as resolved.
Show resolved Hide resolved
* [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/DiscoveryDecrypt.java)
* How to decrypt within a region
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java)
* How to decrypt with a preferred region but failover to others
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java)
* How to reproduce the behavior of an AWS KMS master key provider
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java)
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/ActSimilarToAwsKmsMasterKeyProvider.java)
* How to use AWS KMS clients with custom configuration
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/CustomKmsClientConfig.java)
* How to use different AWS KMS client for different regions
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/CustomClientSupplier.java)
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/CustomDataKeyEncryptionDao.java)
* Using raw wrapping keys
* How to use a raw AES wrapping key
* [with keyrings](./java/com/amazonaws/crypto/examples/keyring/rawaes/RawAes.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final File sourcePlaintextFi
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

// Create the encrypting input stream with the keyring and encryption context.
// Because the file might be too large to load into memory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

ByteArrayInputStream inputStream = new ByteArrayInputStream(sourcePlaintext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

// Encrypt your plaintext data.
final AwsCryptoResult<byte[]> encryptResult = awsEncryptionSdk.encrypt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

// Encrypt your plaintext data.
final AwsCryptoResult<byte[]> encryptResult = awsEncryptionSdk.encrypt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

// Create the caching cryptographic materials manager using your keyring.
final CryptoMaterialsManager cmm = CachingCryptoMaterialsManager.newBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

// Create the algorithm suite restricting cryptographic materials manager using your keyring.
final CryptoMaterialsManager cmm = new RequireApprovedAlgorithmSuitesCryptoMaterialsManager(keyring);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext
encryptionContext.put("the data you are handling", "is what you think it is");

// Create the keyring that determines how your data keys are protected.
final Keyring keyring = StandardKeyrings.awsKms(awsKmsCmk);
final Keyring keyring = StandardKeyrings.awsKmsSymmetricMultiCmk(awsKmsCmk);

// Create the classification requiring cryptographic materials manager using your keyring.
final CryptoMaterialsManager cmm = new ClassificationRequiringCryptoMaterialsManager(keyring);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.amazonaws.crypto.examples.keyring.awskms;

import com.amazonaws.arn.Arn;
import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.AwsCryptoResult;
import com.amazonaws.encryptionsdk.DecryptRequest;
Expand All @@ -23,29 +24,42 @@
/**
* You might have used master key providers to protect your data keys
* in an earlier version of the AWS Encryption SDK.
* This example shows how to configure a keyring that behaves like an AWS KMS master key provider.
* This example shows how to configure a keyring that behaves similarly to an AWS KMS master key provider.
* <p>
* The AWS Encryption SDK provided an AWS KMS master key provider for
* interacting with AWS Key Management Service (AWS KMS).
* On encrypt, the AWS KMS master key provider behaves like the AWS KMS keyring
*
* On encrypt, the AWS KMS master key provider behaves like the AWS KMS symmetric multi-CMK keyring
* and encrypts with all CMKs that you identify.
*
* However, on decrypt,
* the AWS KMS master key provider reviews each encrypted data key (EDK).
* If the EDK was encrypted under an AWS KMS CMK,
* the AWS KMS master key provider attempts to decrypt it.
* Whether decryption succeeds depends on permissions on the CMK.
* This continues until the AWS KMS master key provider either runs out of EDKs
* or succeeds in decrypting an EDK.
* In order to maintain a similar behavior,
* we use an AWS KMS symmetric multi-region keyring
* that has a list of regions it will attempt decryption in.
*
* We have found that separating these two behaviors
* makes the expected behavior clearer,
* so that is what we did with the AWS KMS keyring and the AWS KMS discovery keyring.
* so that is what we did with the AWS KMS symmetric keyring and the AWS KMS region discovery keyring.
* However, as you migrate from master key providers to keyrings,
* you might want a keyring that behaves like the AWS KMS master key provider.
* you might want a keyring that behaves similarly the AWS KMS master key provider.
*
* The AWS KMS symmetric multi-region keyring throws an error on encryption,
* so it cannot be combined with an AWS KMS symmetric multi-CMK in a multi-keyring,
* if the encrypt operation is ever called.
* Therefore, we have two separate keyrings.
lavaleri marked this conversation as resolved.
Show resolved Hide resolved
* One for encrypting with a specific list of CMKs
* and one for decrypting with a specific list of regions.
* <p>
* For more examples of how to use the AWS KMS keyring,
* For more examples of how to use the AWS KMS keyrings,
* see the 'keyring/awskms' directory.
*/
public class ActLikeAwsKmsMasterKeyProvider {
public class ActSimilarToAwsKmsMasterKeyProvider {

/**
* Demonstrate how to create a keyring that behaves like an AWS KMS master key provider.
lavaleri marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -84,19 +98,45 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final List<AwsKmsCmkId> awsK
//
// This keyring reproduces the encryption behavior of the AWS KMS master key provider.
//
// The AWS KMS keyring requires that you explicitly identify the CMK
// The AWS KMS symmetric multi-CMK keyring requires that you explicitly identify the CMK
// that you want the keyring to use to generate the data key.
final Keyring cmkKeyring = StandardKeyrings.awsKmsBuilder()
.generatorKeyId(awsKmsCmk)
.keyIds(awsKmsAdditionalCmks)
final Keyring cmkKeyring = StandardKeyrings.awsKmsSymmetricMultiCmkBuilder()
.generator(awsKmsCmk)
.keyNames(awsKmsAdditionalCmks)
.build();

// Create an AWS KMS discovery keyring that will attempt to decrypt
// any data keys that were encrypted under an AWS KMS CMK.
final Keyring discoveryKeyring = StandardKeyrings.awsKmsDiscoveryBuilder().build();

// Combine the CMK and discovery keyrings
// to create a keyring that behaves like an AWS KMS master key provider.
// Create an AWS KMS symmetric multi-region discovery keyring that will attempt to decrypt
// any data keys that were encrypted under an AWS KMS CMK in a specific list of AWS regions.
//
// Please note that the multi-region discovery keyring requires the specific list of AWS regions
// it may communicate with.
//
// In production, if you need a keyring that attempts decryption in all AWS regions,
// you should call a service/API to get an updated list of AWS regions
// and configure the keyring with that list.
// Although there are ways of getting a list of AWS regions directly from the AWS SDK,
// this is more prone to staleness
// than making a service/API call.
//
// In most cases, you should simply call StandardKeyrings.awsKmsSymmetricMultiRegionDiscovery
// with the specific AWS regions you require for decryption
// and not attempt to configure the keyring with all available AWS regions.
// You should only provide the regions you need.
//
// This will provide flexibility for adding more regions over time,
// without allowing unnecessary access to regions that are not currently required.
final List<String> allRegionIds = new ArrayList<>();
allRegionIds.add(Arn.fromString(awsKmsCmk.toString()).getRegion());
for (final AwsKmsCmkId additionalKeyName : awsKmsAdditionalCmks) {
allRegionIds.add(Arn.fromString(additionalKeyName.toString()).getRegion());
}
final Keyring discoveryKeyring = StandardKeyrings.awsKmsSymmetricMultiRegionDiscovery(allRegionIds);

// Note that you cannot combine the AWS KMS symmetric multi-CMK and AWS KMS symmetric multi-region keyrings
// using a multi-keyring because the AWS KMS symmetric multi-region keyring throws an error
// when calling the encryption operation.
//
// Therefore, you should use these keyrings separately (one for encrypt and one for decrypt).
//
// The CMK keyring reproduces the encryption behavior
// and the discovery keyring reproduces the decryption behavior.
Expand All @@ -105,27 +145,26 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final List<AwsKmsCmkId> awsK
// it works on encrypt but fails to match any encrypted data keys on decrypt
// because the serialized key name is the resulting CMK ARN rather than the alias name.
// However, because the discovery keyring attempts to decrypt any AWS KMS-encrypted
// data keys that it finds, the message still decrypts successfully.
final Keyring keyring = StandardKeyrings.multi(cmkKeyring, discoveryKeyring);
// data keys that it finds, you are still able to successfully decrypt the message.

// Encrypt your plaintext data.
final AwsCryptoResult<byte[]> encryptResult = awsEncryptionSdk.encrypt(
EncryptRequest.builder()
.keyring(keyring)
.keyring(cmkKeyring)
.encryptionContext(encryptionContext)
.plaintext(sourcePlaintext).build());
final byte[] ciphertext = encryptResult.getResult();

// Demonstrate that the ciphertext and plaintext are different.
assert !Arrays.equals(ciphertext, sourcePlaintext);

// Decrypt your encrypted data using the same keyring you used on encrypt.
// Decrypt your encrypted data using the AWS KMS symmetric multi-region keyring.
//
// You do not need to specify the encryption context on decrypt because
// the header of the encrypted message includes the encryption context.
acioc marked this conversation as resolved.
Show resolved Hide resolved
final AwsCryptoResult<byte[]> decryptResult = awsEncryptionSdk.decrypt(
DecryptRequest.builder()
.keyring(keyring)
.keyring(discoveryKeyring)
.ciphertext(ciphertext).build());
final byte[] decrypted = decryptResult.getResult();

Expand Down
Loading