From c5ba94e704dc0314a94ca16b901afa50308f2c70 Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Fri, 17 Apr 2020 12:40:23 -0700 Subject: [PATCH 1/6] Add an example for replicating the behavior of the AWS KMS MKP --- src/examples/README.md | 2 + .../ActLikeAwsKmsMasterKeyProvider.java | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java diff --git a/src/examples/README.md b/src/examples/README.md index c8ae480b7..a7e785742 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -39,6 +39,8 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * [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 replicate the behavior of an AWS KMS master key provider + * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java) * Using raw wrapping keys * How to use a raw AES wrapping key * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/rawaes/RawAes.java) diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java new file mode 100644 index 000000000..e0448d411 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java @@ -0,0 +1,114 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.amazonaws.crypto.examples.keyring.awskms; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.AwsCryptoResult; +import com.amazonaws.encryptionsdk.DecryptRequest; +import com.amazonaws.encryptionsdk.EncryptRequest; +import com.amazonaws.encryptionsdk.keyrings.Keyring; +import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Before there were keyrings, there were master key providers. + * Master key providers were the original configuration structure + * that we provided for defining how you want to protect your data keys. + *

+ * The AWS KMS master key provider was the tool that we provided for interacting with AWS KMS. + * Like the AWS KMS keyring, + * the AWS KMS master key provider encrypts with all CMKs that you identify, + * but unlike the AWS KMS keyring, + * the AWS KMS master key provider always attempts to decrypt + * *any* data keys that were encrypted under an AWS KMS CMK. + * We have found that separating these two behaviors + * makes it more clear what behavior to expect, + * so that is what we did with the AWS KMS keyring and the AWS KMS discovery keyring. + * However, as you migrate away from master key providers to keyrings, + * you might need to replicate the behavior of the AWS KMS master key provider. + *

+ * This example shows how to configure a keyring that behaves like an AWS KMS master key provider. + *

+ * For more examples of how to use the AWS KMS keyring, + * see the 'keyring/awskms' directory. + */ +public class ActLikeAwsKmsMasterKeyProvider { + + /** + * Demonstrate how to create a keyring that behaves like an AWS KMS master key provider. + * + * @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys + * @param sourcePlaintext Plaintext to encrypt + */ + public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) { + // Instantiate the AWS Encryption SDK. + final AwsCrypto awsEncryptionSdk = new AwsCrypto(); + + // Prepare your encryption context. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + final Map encryptionContext = new HashMap<>(); + encryptionContext.put("encryption", "context"); + encryptionContext.put("is not", "secret"); + encryptionContext.put("but adds", "useful metadata"); + encryptionContext.put("that can help you", "be confident that"); + encryptionContext.put("the data you are handling", "is what you think it is"); + + // This is the master key provider whose behavior we want to replicate. + // + // On encrypt, this master key provider only uses the single target AWS KMS CMK. + // However, on decrypt, this master key provider attempts to decrypt + // any data keys that were encrypted under an AWS KMS CMK. + final KmsMasterKeyProvider masterKeyProviderToReplicate = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsCmk.toString()).build(); + + // Create a keyring that encrypts and decrypts using a single AWS KMS CMK. + final Keyring singleCmkKeyring = StandardKeyrings.awsKms(awsKmsCmk); + + // 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 single-CMK and discovery keyrings + // to create a keyring that behaves like an AWS KMS master key provider. + final Keyring keyring = StandardKeyrings.multi(singleCmkKeyring, discoveryKeyring); + + // Encrypt your plaintext data. + final AwsCryptoResult encryptResult = awsEncryptionSdk.encrypt( + EncryptRequest.builder() + .keyring(keyring) + .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. + // + // You do not need to specify the encryption context on decrypt because + // the header of the encrypted message includes the encryption context. + final AwsCryptoResult decryptResult = awsEncryptionSdk.decrypt( + DecryptRequest.builder() + .keyring(keyring) + .ciphertext(ciphertext).build()); + final byte[] decrypted = decryptResult.getResult(); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + assert Arrays.equals(decrypted, sourcePlaintext); + + // Verify that the encryption context used in the decrypt operation includes + // the encryption context that you specified when encrypting. + // The AWS Encryption SDK can add pairs, so don't require an exact match. + // + // In production, always use a meaningful encryption context. + encryptionContext.forEach((k, v) -> { + assert v.equals(decryptResult.getEncryptionContext().get(k)); + }); + } +} From d00281bf898962e478e2c1c8b2a0d90650a0bafe Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Mon, 20 Apr 2020 13:57:16 -0700 Subject: [PATCH 2/6] Rewording based on feedback --- src/examples/README.md | 2 +- .../ActLikeAwsKmsMasterKeyProvider.java | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/examples/README.md b/src/examples/README.md index a7e785742..5e99106a9 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -39,7 +39,7 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * [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 replicate the behavior of an AWS KMS master key provider + * How to reproduce the behavior of an AWS KMS master key provider * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java) * Using raw wrapping keys * How to use a raw AES wrapping key diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java index e0448d411..9ab825a38 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java @@ -17,21 +17,19 @@ import java.util.Map; /** - * Before there were keyrings, there were master key providers. - * Master key providers were the original configuration structure - * that we provided for defining how you want to protect your data keys. + * In earlier versions of the AWS Encryption SDK, you used master key providers to determine how your data keys are protected. *

- * The AWS KMS master key provider was the tool that we provided for interacting with AWS KMS. + * The AWS Encryption SDK provided an AWS KMS master key provider for interacting with AWS Key Management Service (AWS KMS). * Like the AWS KMS keyring, * the AWS KMS master key provider encrypts with all CMKs that you identify, * but unlike the AWS KMS keyring, * the AWS KMS master key provider always attempts to decrypt * *any* data keys that were encrypted under an AWS KMS CMK. * We have found that separating these two behaviors - * makes it more clear what behavior to expect, + * makes the expected behavior clearer, * so that is what we did with the AWS KMS keyring and the AWS KMS discovery keyring. - * However, as you migrate away from master key providers to keyrings, - * you might need to replicate the behavior of the AWS KMS master key provider. + * However, as you migrate from master key providers to keyrings, + * you might want a keyring that behaves like the AWS KMS master key provider. *

* This example shows how to configure a keyring that behaves like an AWS KMS master key provider. *

@@ -59,15 +57,15 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext encryptionContext.put("that can help you", "be confident that"); encryptionContext.put("the data you are handling", "is what you think it is"); - // This is the master key provider whose behavior we want to replicate. + // This is the master key provider whose behavior we want to reproduce. // - // On encrypt, this master key provider only uses the single target AWS KMS CMK. - // However, on decrypt, this master key provider attempts to decrypt + // When encrypting, this master key provider uses only the specified `aws_kms_cmk`. + // However, when decrypting, this master key provider attempts to decrypt // any data keys that were encrypted under an AWS KMS CMK. final KmsMasterKeyProvider masterKeyProviderToReplicate = KmsMasterKeyProvider.builder() .withKeysForEncryption(awsKmsCmk.toString()).build(); - // Create a keyring that encrypts and decrypts using a single AWS KMS CMK. + // Create a single-CMK keyring that encrypts and decrypts using a single AWS KMS CMK. final Keyring singleCmkKeyring = StandardKeyrings.awsKms(awsKmsCmk); // Create an AWS KMS discovery keyring that will attempt to decrypt From 1a85fb576c1c373b63b88c5371da09ded5c8ee73 Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Wed, 22 Apr 2020 15:18:24 -0700 Subject: [PATCH 3/6] Updating based on python example updates --- .../ActLikeAwsKmsMasterKeyProvider.java | 64 ++++++++++++++----- .../keyring/awskms/DiscoveryDecrypt.java | 9 ++- .../awskms/DiscoveryDecryptInRegionOnly.java | 9 ++- .../DiscoveryDecryptWithPreferredRegions.java | 9 ++- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java index 9ab825a38..56153ae7f 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java @@ -12,27 +12,36 @@ import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; +import static java.util.stream.Collectors.toList; + /** - * In earlier versions of the AWS Encryption SDK, you used master key providers to determine how your data keys are protected. + * 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. *

- * The AWS Encryption SDK provided an AWS KMS master key provider for interacting with AWS Key Management Service (AWS KMS). - * Like the AWS KMS keyring, - * the AWS KMS master key provider encrypts with all CMKs that you identify, - * but unlike the AWS KMS keyring, - * the AWS KMS master key provider always attempts to decrypt - * *any* data keys that were encrypted under an AWS KMS CMK. + * 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 + * 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. * 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. * However, as you migrate from master key providers to keyrings, * you might want a keyring that behaves like the AWS KMS master key provider. *

- * This example shows how to configure a keyring that behaves like an AWS KMS master key provider. - *

* For more examples of how to use the AWS KMS keyring, * see the 'keyring/awskms' directory. */ @@ -41,10 +50,11 @@ public class ActLikeAwsKmsMasterKeyProvider { /** * Demonstrate how to create a keyring that behaves like an AWS KMS master key provider. * - * @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys - * @param sourcePlaintext Plaintext to encrypt + * @param awsKmsCmk The ARN of an AWS KMS CMK that protects data keys + * @param awsKmsAdditionalCmks Additional ARNs of secondary AWS KMS CMKs + * @param sourcePlaintext Plaintext to encrypt */ - public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext) { + public static void run(final AwsKmsCmkId awsKmsCmk, final List awsKmsAdditionalCmks, byte[] sourcePlaintext) { // Instantiate the AWS Encryption SDK. final AwsCrypto awsEncryptionSdk = new AwsCrypto(); @@ -59,14 +69,26 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext // This is the master key provider whose behavior we want to reproduce. // - // When encrypting, this master key provider uses only the specified `aws_kms_cmk`. + // When encrypting, this master key provider generates the data key using the first CMK in the list + // and encrypts the data key using all specified CMKs. // However, when decrypting, this master key provider attempts to decrypt // any data keys that were encrypted under an AWS KMS CMK. + final List masterKeyProviderCmks = new ArrayList<>(); + masterKeyProviderCmks.add(awsKmsCmk.toString()); + masterKeyProviderCmks.addAll(awsKmsAdditionalCmks.stream().map(AwsKmsCmkId::toString).collect(toList())); final KmsMasterKeyProvider masterKeyProviderToReplicate = KmsMasterKeyProvider.builder() - .withKeysForEncryption(awsKmsCmk.toString()).build(); + .withKeysForEncryption(masterKeyProviderCmks).build(); - // Create a single-CMK keyring that encrypts and decrypts using a single AWS KMS CMK. - final Keyring singleCmkKeyring = StandardKeyrings.awsKms(awsKmsCmk); + // Create a CMK keyring that encrypts and decrypts using the specified AWS KMS CMKs. + // + // This keyring reproduces the encryption behavior of the AWS KMS master key provider. + // + // The AWS KMS 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) + .build(); // Create an AWS KMS discovery keyring that will attempt to decrypt // any data keys that were encrypted under an AWS KMS CMK. @@ -74,7 +96,15 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final byte[] sourcePlaintext // Combine the single-CMK and discovery keyrings // to create a keyring that behaves like an AWS KMS master key provider. - final Keyring keyring = StandardKeyrings.multi(singleCmkKeyring, discoveryKeyring); + // + // The CMK keyring reproduces the encryption behavior + // and the discovery keyring reproduces the decryption behavior. + // This also means that it does not matter if the CMK keyring fails on decrypt, + // for example if you configured it with aliases which would work on encrypt + // but fail to match any encrypted data keys on decrypt, + // because the discovery keyring attempts to decrypt any AWS KMS-encrypted + // data keys that it finds. + final Keyring keyring = StandardKeyrings.multi(cmkKeyring, discoveryKeyring); // Encrypt your plaintext data. final AwsCryptoResult encryptResult = awsEncryptionSdk.encrypt( diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecrypt.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecrypt.java index eda15ad94..1240567a8 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecrypt.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecrypt.java @@ -21,8 +21,13 @@ * However, sometimes you need more flexibility on decrypt, * especially when you don't know which CMKs were used to encrypt a message. * To address this need, you can use an AWS KMS discovery keyring. - * The AWS KMS discovery keyring does nothing on encrypt - * but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK. + * The AWS KMS discovery keyring does nothing on encrypt. + * On decrypt it reviews each encrypted data key (EDK). + * If an EDK was encrypted under an AWS KMS CMK, + * the AWS KMS discovery keyring attempts to decrypt it. + * Whether decryption succeeds depends on permissions on the CMK. + * This continues until the AWS KMS discovery keyring either runs out of EDKs + * or succeeds in decrypting an EDK. *

* This example shows how to configure and use an AWS KMS discovery keyring. *

diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java index 3e2aa987f..c6e7824fd 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptInRegionOnly.java @@ -25,8 +25,13 @@ * However, sometimes you need more flexibility on decrypt, * especially if you don't know which CMK was used to encrypt a message. * To address this need, you can use an AWS KMS discovery keyring. - * The AWS KMS discovery keyring does nothing on encrypt - * but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK. + * The AWS KMS discovery keyring does nothing on encrypt. + * On decrypt it reviews each encrypted data key (EDK). + * If an EDK was encrypted under an AWS KMS CMK, + * the AWS KMS discovery keyring attempts to decrypt it. + * Whether decryption succeeds depends on permissions on the CMK. + * This continues until the AWS KMS discovery keyring either runs out of EDKs + * or succeeds in decrypting an EDK. *

* However, sometimes you need to be a *bit* more restrictive than that. * To address this need, you can use a client supplier that restricts the regions an AWS KMS keyring can talk to. diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java index 1826dafe4..2419580d4 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/DiscoveryDecryptWithPreferredRegions.java @@ -25,8 +25,13 @@ * However, sometimes you need more flexibility on decrypt, * especially if you might not know beforehand which CMK was used to encrypt a message. * To address this need, you can use an AWS KMS discovery keyring. - * The AWS KMS discovery keyring will do nothing on encrypt - * but will attempt to decrypt *any* data keys that were encrypted under an AWS KMS CMK. + * The AWS KMS discovery keyring does nothing on encrypt. + * On decrypt it reviews each encrypted data key (EDK). + * If an EDK was encrypted under an AWS KMS CMK, + * the AWS KMS discovery keyring attempts to decrypt it. + * Whether decryption succeeds depends on permissions on the CMK. + * This continues until the AWS KMS discovery keyring either runs out of EDKs + * or succeeds in decrypting an EDK. *

* However, sometimes you need to be a *bit* more restrictive than that. * To address this need, you can use a client supplier to restrict what regions an AWS KMS keyring can talk to. From 4e86ec1f995874f7a0915c123d20cf83da4ce007 Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Thu, 23 Apr 2020 14:40:23 -0700 Subject: [PATCH 4/6] Matching python wording --- .../awskms/ActLikeAwsKmsMasterKeyProvider.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java index 56153ae7f..b0ea6b2a3 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java @@ -99,11 +99,12 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final List awsK // // The CMK keyring reproduces the encryption behavior // and the discovery keyring reproduces the decryption behavior. - // This also means that it does not matter if the CMK keyring fails on decrypt, - // for example if you configured it with aliases which would work on encrypt - // but fail to match any encrypted data keys on decrypt, - // because the discovery keyring attempts to decrypt any AWS KMS-encrypted - // data keys that it finds. + // This also means that it does not matter if the CMK keyring fails to decrypt. + // For example, if you configured the CMK keyring with aliases, + // 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); // Encrypt your plaintext data. From e3c2f3b41af64c481fe083f8d8e6e11981a526bf Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> Date: Thu, 23 Apr 2020 11:12:06 -0700 Subject: [PATCH 5/6] 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 --- .../encryptionsdk/keyrings/RawRsaKeyring.java | 14 ++++++ .../keyrings/RawRsaKeyringTest.java | 45 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java index 9b8a7b453..0ca7923e5 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java +++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java @@ -14,8 +14,10 @@ package com.amazonaws.encryptionsdk.keyrings; import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.exception.AwsCryptoException; import com.amazonaws.encryptionsdk.internal.JceKeyCipher; import com.amazonaws.encryptionsdk.keyrings.RawRsaKeyringBuilder.RsaPaddingScheme; +import com.amazonaws.encryptionsdk.model.EncryptionMaterials; import java.security.PrivateKey; import java.security.PublicKey; @@ -29,8 +31,20 @@ */ class RawRsaKeyring extends RawKeyring { + private final boolean validToEncrypt; + RawRsaKeyring(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, RsaPaddingScheme rsaPaddingScheme) { super(keyNamespace, keyName, JceKeyCipher.rsa(publicKey, privateKey, rsaPaddingScheme.getTransformation())); + validToEncrypt = publicKey != null; + } + + @Override + public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) { + if(!validToEncrypt) { + throw new AwsCryptoException("A public key is required to encrypt"); + } + + return super.onEncrypt(encryptionMaterials); } @Override diff --git a/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java index 59fff4da1..b79d88dfe 100644 --- a/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java +++ b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java @@ -14,6 +14,7 @@ package com.amazonaws.encryptionsdk.keyrings; import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.exception.AwsCryptoException; import com.amazonaws.encryptionsdk.keyrings.RawRsaKeyringBuilder.RsaPaddingScheme; import com.amazonaws.encryptionsdk.model.DecryptionMaterials; import com.amazonaws.encryptionsdk.model.EncryptionMaterials; @@ -34,6 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class RawRsaKeyringTest { @@ -134,4 +136,47 @@ void testEncryptDecryptGenerateDataKey() { assertTrue(decryptionMaterials.getKeyringTrace().getEntries().get(0).getFlags().contains(KeyringTraceFlag.DECRYPTED_DATA_KEY)); } + @Test + void testEncryptWithNoPublicKey() throws Exception { + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + Keyring noPublicKey = new RawRsaKeyring(KEYNAMESPACE, KEYNAME, null, keyPair.getPrivate(), PADDING_SCHEME); + + EncryptionMaterials encryptionMaterials = EncryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setCleartextDataKey(DATA_KEY) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .build(); + + assertThrows(AwsCryptoException.class, () -> noPublicKey.onEncrypt(encryptionMaterials)); + } + + @Test + void testDecryptWithNoPrivateKey() throws Exception { + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + Keyring noPrivateKey = new RawRsaKeyring(KEYNAMESPACE, KEYNAME, keyPair.getPublic(), null, PADDING_SCHEME); + + EncryptionMaterials encryptionMaterials = EncryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setCleartextDataKey(DATA_KEY) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .build(); + + encryptionMaterials = noPrivateKey.onEncrypt(encryptionMaterials); + + DecryptionMaterials decryptionMaterials = DecryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .build(); + + DecryptionMaterials resultDecryptionMaterials = noPrivateKey.onDecrypt(decryptionMaterials, encryptionMaterials.getEncryptedDataKeys()); + + assertEquals(decryptionMaterials, resultDecryptionMaterials); + } + } From 3d0b9a713677b9a4933530623c190fff9fef59e0 Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> Date: Fri, 24 Apr 2020 10:51:52 -0700 Subject: [PATCH 6/6] Update src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java Co-Authored-By: Matt Bullock --- .../examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java index b0ea6b2a3..c6ac3b86e 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java +++ b/src/examples/java/com/amazonaws/crypto/examples/keyring/awskms/ActLikeAwsKmsMasterKeyProvider.java @@ -94,7 +94,7 @@ public static void run(final AwsKmsCmkId awsKmsCmk, final List awsK // any data keys that were encrypted under an AWS KMS CMK. final Keyring discoveryKeyring = StandardKeyrings.awsKmsDiscoveryBuilder().build(); - // Combine the single-CMK and discovery keyrings + // Combine the CMK and discovery keyrings // to create a keyring that behaves like an AWS KMS master key provider. // // The CMK keyring reproduces the encryption behavior