diff --git a/src/examples/README.md b/src/examples/README.md index b6e778219..19201887b 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -28,10 +28,13 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * Using AWS Key Management Service (AWS KMS) * How to use one AWS KMS CMK * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/awskms/SingleCmk.java) + * [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/SingleCmk.java) * How to use multiple AWS KMS CMKs in different regions * [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 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 @@ -39,8 +42,10 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * Using raw wrapping keys * How to use a raw AES wrapping key * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/rawaes/RawAes.java) + * [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/rawaes/RawAes.java) * How to use a raw RSA wrapping key * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/rawrsa/RawRsa.java) + * [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/rawrsa/RawRsa.java) * How to encrypt with a raw RSA public key wrapping key without access to the private key * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/rawrsa/PublicPrivateKeySeparate.java) * How to use a raw RSA wrapping key when the key is DER encoded @@ -48,6 +53,7 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * Combining wrapping keys * How to combine AWS KMS with an offline escrow key * [with keyrings](./java/com/amazonaws/crypto/examples/keyring/multi/AwsKmsWithEscrow.java) + * [with master key providers](./java/com/amazonaws/crypto/examples/masterkeyprovider/multi/AwsKmsWithEscrow.java) ### Keyrings diff --git a/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/DiscoveryDecrypt.java b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/DiscoveryDecrypt.java new file mode 100644 index 000000000..e16095224 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/DiscoveryDecrypt.java @@ -0,0 +1,94 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.amazonaws.crypto.examples.masterkeyprovider.awskms; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.CryptoResult; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.encryptionsdk.kms.KmsMasterKey; +import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This example is intended to serve as reference material for users migrating away from master key providers. + * We recommend using keyrings rather than master key providers. + * For examples using keyrings, see the 'examples/keyring' directory. + *

+ * The KMS master key provider uses any key IDs that you specify on encrypt, + * but attempts to decrypt *any* data keys that were encrypted under a KMS CMK. + * This means that you do not need to know which CMKs were used to encrypt a message. + *

+ * This example shows how to configure and use a KMS master key provider to decrypt without provider key IDs. + *

+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + *

+ * For an example of how to use the KMS master key with a single CMK, + * see the {@link SingleCmk} example. + *

+ * For an example of how to use the KMS master key provider with CMKs in multiple regions, + * see the {@link MultipleRegions} example. + */ +public class DiscoveryDecrypt { + + /** + * Demonstrate configuring a KMS master key provider for decryption. + * + * @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"); + + // Create the master key that determines how your data keys are protected. + final KmsMasterKeyProvider encryptMasterKeyProvider = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsCmk.toString()).build(); + + // Create a KMS master key provider to use on decrypt. + final KmsMasterKeyProvider decryptMasterKeyProvider = KmsMasterKeyProvider.builder().build(); + + // Encrypt your plaintext data. + final CryptoResult encryptResult = awsEncryptionSdk.encryptData( + encryptMasterKeyProvider, + sourcePlaintext, + encryptionContext); + final byte[] ciphertext = encryptResult.getResult(); + + // Demonstrate that the ciphertext and plaintext are different. + assert !Arrays.equals(ciphertext, sourcePlaintext); + + // Decrypt your encrypted data using the KMS master key provider. + // + // You do not need to specify the encryption context on decrypt because + // the header of the encrypted message includes the encryption context. + final CryptoResult decryptResult = awsEncryptionSdk.decryptData( + decryptMasterKeyProvider, + ciphertext); + 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)); + }); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/MultipleRegions.java b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/MultipleRegions.java new file mode 100644 index 000000000..e5cb68349 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/MultipleRegions.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.masterkeyprovider.awskms; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.CryptoResult; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.encryptionsdk.kms.KmsMasterKey; +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; + +/** + * This example is intended to serve as reference material for users migrating away from master key providers. + * We recommend using keyrings rather than master key providers. + * For examples using keyrings, see the 'examples/keyring' directory. + *

+ * This example shows how to configure and use a KMS master key provider with with CMKs in multiple regions. + *

+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + *

+ * For an example of how to use the KMS master key with a single CMK, + * see the {@link SingleCmk} example. + *

+ * For an example of how to use the KMS master key provider in discovery mode on decrypt, + * see the {@link DiscoveryDecrypt} example. + */ +public class MultipleRegions { + + /** + * Demonstrate an encrypt/decrypt cycle using a KMS master key provider with CMKs in multiple regions. + * + * @param awsKmsGeneratorCmk The ARN of an AWS KMS CMK that protects data keys + * @param awsKmsAdditionalCmks Additional ARNs of secondary KMS CMKs + * @param sourcePlaintext Plaintext to encrypt + */ + public static void run(final AwsKmsCmkId awsKmsGeneratorCmk, final List awsKmsAdditionalCmks, 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"); + + // Create the master key provider that will encrypt your data keys under all requested CMKs. + // + // The KMS master key provider generates the data key using the first key ID in the list. + final List awsKmsCmks = new ArrayList<>(); + awsKmsCmks.add(awsKmsGeneratorCmk.toString()); + awsKmsCmks.addAll(awsKmsAdditionalCmks.stream().map(AwsKmsCmkId::toString).collect(toList())); + final KmsMasterKeyProvider masterKeyProvider = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsCmks).build(); + + // Create master key providers that each only use one of the CMKs. + // We will use these later to demonstrate that any of the CMKs can be used to decrypt the message. + final KmsMasterKeyProvider singleCmkMasterKeyThatGenerated = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsGeneratorCmk.toString()).build(); + final KmsMasterKeyProvider singleCmkMasterKeyThatEncrypted = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsAdditionalCmks.get(0).toString()).build(); + + // Encrypt your plaintext data using the master key provider that uses all requests CMKs. + final CryptoResult encryptResult = awsEncryptionSdk.encryptData( + masterKeyProvider, + sourcePlaintext, + encryptionContext); + final byte[] ciphertext = encryptResult.getResult(); + + // Verify that the header contains the expected number of encrypted data keys (EDKs). + // It should contain one EDK for each CMK. + assert encryptResult.getHeaders().getEncryptedKeyBlobCount() == awsKmsAdditionalCmks.size() + 1; + + // Demonstrate that the ciphertext and plaintext are different. + assert !Arrays.equals(ciphertext, sourcePlaintext); + + // Decrypt your encrypted data separately using the single-CMK master keys. + // + // You do not need to specify the encryption context on decrypt because + // the header of the encrypted message includes the encryption context. + final CryptoResult decryptResult1 = awsEncryptionSdk.decryptData( + singleCmkMasterKeyThatGenerated, + ciphertext); + final byte[] decrypted1 = decryptResult1.getResult(); + final CryptoResult decryptResult2 = awsEncryptionSdk.decryptData( + singleCmkMasterKeyThatEncrypted, + ciphertext); + final byte[] decrypted2 = decryptResult2.getResult(); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + assert Arrays.equals(decrypted1, sourcePlaintext); + assert Arrays.equals(decrypted2, 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(decryptResult1.getEncryptionContext().get(k)); + assert v.equals(decryptResult2.getEncryptionContext().get(k)); + }); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/SingleCmk.java b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/SingleCmk.java new file mode 100644 index 000000000..2ce88f7fd --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/awskms/SingleCmk.java @@ -0,0 +1,87 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.amazonaws.crypto.examples.masterkeyprovider.awskms; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.CryptoResult; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.encryptionsdk.kms.KmsMasterKey; +import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This example is intended to serve as reference material for users migrating away from master key providers. + * We recommend using keyrings rather than master key providers. + * For examples using keyrings, see the 'examples/keyring' directory. + *

+ * This example shows how to configure and use a KMS master key with a single KMS CMK. + *

+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + *

+ * For an example of how to use the KMS master key provider with CMKs in multiple regions, + * see the {@link MultipleRegions} example. + *

+ * For an example of how to use the KMS master key provider in discovery mode on decrypt, + * see the {@link DiscoveryDecrypt} example. + */ +public class SingleCmk { + + /** + * Demonstrate an encrypt/decrypt cycle using a KMS master key provider with a single CMK. + * + * @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"); + + // Create the master key provider that determines how your data keys are protected. + final KmsMasterKeyProvider masterKeyProvider = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsCmk.toString()).build(); + + // Encrypt your plaintext data. + final CryptoResult encryptResult = awsEncryptionSdk.encryptData( + masterKeyProvider, + sourcePlaintext, + encryptionContext); + 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 master key provider 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 CryptoResult decryptResult = awsEncryptionSdk.decryptData( + masterKeyProvider, + ciphertext); + 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)); + }); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/multi/AwsKmsWithEscrow.java b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/multi/AwsKmsWithEscrow.java new file mode 100644 index 000000000..425f8cff6 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/multi/AwsKmsWithEscrow.java @@ -0,0 +1,154 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.amazonaws.crypto.examples.masterkeyprovider.multi; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.CryptoResult; +import com.amazonaws.encryptionsdk.MasterKeyProvider; +import com.amazonaws.encryptionsdk.jce.JceMasterKey; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.encryptionsdk.kms.KmsMasterKey; +import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; +import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory; + +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This example is intended to serve as reference material for users migrating away from master key providers. + * We recommend using keyrings rather than master key providers. + * For examples using keyrings, see the 'examples/keyring' directory. + *

+ * One use-case that we have seen customers need is + * the ability to enjoy the benefits of AWS KMS during normal operation + * but retain the ability to decrypt encrypted messages without access to AWS KMS. + * This example shows how you can achieve this + * by combining a KMS master key with a raw RSA master key. + *

+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + *

+ * For more examples of how to use the KMS master key provider, see the + * 'masterkeyprovider/awskms' examples' + *

+ * For more examples of how to use the raw RSA master key, see the + * see the 'masterkeyprovider/rawrsa' examples. + *

+ * In this example we generate a RSA keypair + * but in practice you would want to keep your private key in an HSM + * or other key management system. + *

+ * In this example, we use the one-step encrypt and decrypt APIs. + */ +public class AwsKmsWithEscrow { + + /** + * Demonstrate configuring a master key provider to use an AWS KMS CMK and a RSA wrapping key. + * + * @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) throws GeneralSecurityException { + // 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"); + + // Generate an RSA key pair to use with your master key. + // In practice, you should get this key from a secure key management system such as an HSM. + final KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA"); + // The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA. + // https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths + kg.initialize(4096); + final KeyPair keyPair = kg.generateKeyPair(); + + // Create the encrypt master key that only has access to the public key. + final JceMasterKey escrowEncryptMasterKey = JceMasterKey.getInstance( + keyPair.getPublic(), + null, // Private key is null + // The provider ID and key ID are defined by you + // and are used by the raw RSA master key + // to determine whether it should attempt to decrypt + // an encrypted data key. + "some managed raw keys", // provider corresponds to key namespace for keyrings + "my RSA wrapping key", // key ID corresponds to key name for keyrings + // The padding scheme tells the raw RSA master key + // how to use your wrapping key to encrypt data keys. + // + // We recommend using OAEP_SHA256_MGF1. + // You should not use PKCS1 unless you require it for backwards compatibility. + "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + + // Create the decrypt master key that has access to the private key. + final JceMasterKey escrowDecryptMasterKey = JceMasterKey.getInstance( + null, // Public key is null + keyPair.getPrivate(), + // The key namespace and key name MUST match the encrypt master key. + "some managed raw keys", // provider corresponds to key namespace for keyrings + "my RSA wrapping key", // key ID corresponds to key name for keyrings + // The wrapping algorithm MUST match the encrypt master key. + "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + + + // Create the KMS master key that you will use for decryption during normal operations. + final KmsMasterKeyProvider kmsMasterKeyProvider = KmsMasterKeyProvider.builder() + .withKeysForEncryption(awsKmsCmk.toString()).build(); + + // Combine the KMS and escrow providers into a single master key provider. + final MasterKeyProvider masterKeyProvider = MultipleProviderFactory.buildMultiProvider( + kmsMasterKeyProvider, escrowEncryptMasterKey); + + // Encrypt your plaintext data using the combined master keys. + final CryptoResult encryptResult = awsEncryptionSdk.encryptData( + masterKeyProvider, + sourcePlaintext, + encryptionContext); + final byte[] ciphertext = encryptResult.getResult(); + + // Verify that the header contains the expected number of encrypted data keys (EDKs). + // It should contain one EDK for KMS and one for the escrow key. + assert encryptResult.getHeaders().getEncryptedKeyBlobCount() == 2; + + // Demonstrate that the ciphertext and plaintext are different. + assert !Arrays.equals(ciphertext, sourcePlaintext); + + // Decrypt your encrypted data separately using the KMS master key provider + // and the escrow decrypt master key. + // + // You do not need to specify the encryption context on decrypt because + // the header of the encrypted message includes the encryption context. + final CryptoResult decryptedKmsResult = awsEncryptionSdk.decryptData( + kmsMasterKeyProvider, + ciphertext); + final byte[] decryptedKms = decryptedKmsResult.getResult(); + final CryptoResult decryptedEscrowResult = awsEncryptionSdk.decryptData( + escrowDecryptMasterKey, + ciphertext); + final byte[] decryptedEscrow = decryptedKmsResult.getResult(); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + assert Arrays.equals(decryptedKms, sourcePlaintext); + assert Arrays.equals(decryptedEscrow, 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(decryptedKmsResult.getEncryptionContext().get(k)); + assert v.equals(decryptedEscrowResult.getEncryptionContext().get(k)); + }); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/rawaes/RawAes.java b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/rawaes/RawAes.java new file mode 100644 index 000000000..89a8d90cc --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/rawaes/RawAes.java @@ -0,0 +1,98 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.amazonaws.crypto.examples.masterkeyprovider.rawaes; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.CryptoResult; +import com.amazonaws.encryptionsdk.jce.JceMasterKey; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This example is intended to serve as reference material for users migrating away from master key providers. + * We recommend using keyrings rather than master key providers. + * For examples using keyrings, see the 'examples/keyring' directory. + *

+ * This examples shows how to configure and use a raw AES master key. + *

+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + *

+ * In this example, we use the one-step encrypt and decrypt APIs. + */ +public class RawAes { + + /** + * Demonstrate an encrypt/decrypt cycle using a raw AES master key. + * + * @param sourcePlaintext Plaintext to encrypt + */ + public static void run(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"); + + // Generate an AES key to use with your master key. + // + // In practice, you should get this key from a secure key management system such as an HSM. + SecureRandom rnd = new SecureRandom(); + byte[] rawKey = new byte[32]; // 256 bits + rnd.nextBytes(rawKey); + SecretKey key = new SecretKeySpec(rawKey, "AES"); + + // Create the master key that determines how your data keys are protected. + final JceMasterKey masterKey = JceMasterKey.getInstance( + // The key namespace and key name are defined by you + // and are used by the raw AES master key + // to determine whether it should attempt to decrypt + // an encrypted data key. + key, + "some managed raw keys", // provider corresponds to key namespace for keyrings + "my AES wrapping key", // key ID corresponds to key name for keyrings + "AES/GCM/NOPADDING"); // the AES JceMasterKey only supports AES/GCM/NOPADDING + + // Encrypt your plaintext data. + final CryptoResult encryptResult = awsEncryptionSdk.encryptData( + masterKey, + sourcePlaintext, + encryptionContext); + 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 master key 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 CryptoResult decryptResult = awsEncryptionSdk.decryptData( + masterKey, + ciphertext); + 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)); + }); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/rawrsa/RawRsa.java b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/rawrsa/RawRsa.java new file mode 100644 index 000000000..ff1d972f0 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/masterkeyprovider/rawrsa/RawRsa.java @@ -0,0 +1,104 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.amazonaws.crypto.examples.masterkeyprovider.rawrsa; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.CryptoResult; +import com.amazonaws.encryptionsdk.jce.JceMasterKey; + +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This example is intended to serve as reference material for users migrating away from master key providers. + * We recommend using keyrings rather than master key providers. + * For examples using keyrings, see the 'examples/keyring' directory. + *

+ * This examples shows how to configure and use a raw RSA master key using a pre-loaded RSA key pair. + *

+ * https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + *

+ * In this example, we use the one-step encrypt and decrypt APIs. + */ +public class RawRsa { + + /** + * Demonstrate an encrypt/decrypt cycle using a raw RSA master key. + * + * @param sourcePlaintext Plaintext to encrypt + */ + public static void run(final byte[] sourcePlaintext) throws GeneralSecurityException { + // 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"); + + // Generate an RSA key pair to use with your master key. + // In practice, you should get this key from a secure key management system such as an HSM. + final KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA"); + // The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA. + // https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths + kg.initialize(4096); + final KeyPair keyPair = kg.generateKeyPair(); + + // Create the master key that determines how your data keys are protected. + final JceMasterKey masterKey = JceMasterKey.getInstance( + keyPair.getPublic(), + keyPair.getPrivate(), + // The provider ID and key ID are defined by you + // and are used by the raw RSA master key + // to determine whether it should attempt to decrypt + // an encrypted data key. + "some managed raw keys", // provider corresponds to key namespace for keyrings + "my RSA wrapping key", // key ID corresponds to key name for keyrings + // The padding scheme tells the raw RSA master key + // how to use your wrapping key to encrypt data keys. + // + // We recommend using OAEP_SHA256_MGF1. + // You should not use PKCS1 unless you require it for backwards compatibility. + "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + + // Encrypt your plaintext data. + final CryptoResult encryptResult = awsEncryptionSdk.encryptData( + masterKey, + sourcePlaintext, + encryptionContext); + 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 master key 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 CryptoResult decryptResult = awsEncryptionSdk.decryptData( + masterKey, + ciphertext); + 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)); + }); + } +}