Skip to content

Incorporate Keyrings into AwsCrypto and deprecate MasterKeyProviders. #151

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

Merged
merged 28 commits into from
Feb 12, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6bb85a0
Incorporate Keyrings into AwsCrypto and deprecate MasterKeyProviders.
WesleyRosenblum Jan 17, 2020
63ff149
Update example code to use keyrings
WesleyRosenblum Jan 21, 2020
d444f52
Using try-with-resources for AwsCrypto streams
WesleyRosenblum Jan 23, 2020
0a3e6e3
Splitting MKP and keyring unit tests
WesleyRosenblum Jan 23, 2020
30c51a9
Making decryptData with ParsedCiphertext public
WesleyRosenblum Jan 23, 2020
4d8614f
Mark KeyStoreProvider as deprecated
WesleyRosenblum Jan 24, 2020
400f521
Reword some comments on the Basic Encryption example
WesleyRosenblum Jan 27, 2020
59e3045
Add test for compability of Keyrings with MasterKeyProviders
WesleyRosenblum Jan 27, 2020
6e473a1
Create individual request types for each AwsCrypto method
WesleyRosenblum Jan 29, 2020
623f6f5
Make EncryptionMaterials, DecryptionMaterials and KeyringTrace immutable
WesleyRosenblum Jan 30, 2020
3af73f0
Rename KmsKeying and related classes to AwsKmsKeyring
WesleyRosenblum Jan 30, 2020
5258475
Create builders for the standard keyrings
WesleyRosenblum Jan 30, 2020
e04c285
Create AwsKmsCmkId type to represent AWS KMS Key Ids
WesleyRosenblum Jan 31, 2020
6396c6c
Add factory methods to Keyring builders
WesleyRosenblum Jan 31, 2020
15c1def
Add comment on not making a defensive copy of plaintext/ciphertext
WesleyRosenblum Jan 31, 2020
6af9b02
Limit ability to create discovery AWS KMS Keyrings to explicit creation
WesleyRosenblum Jan 31, 2020
c182cd5
Add withKeyring to CachingCMM builder
WesleyRosenblum Jan 31, 2020
37d42bb
Fix DecryptRequestTest
WesleyRosenblum Feb 3, 2020
63057f5
Fix Junit 4 assertions in JUnit5 tests
WesleyRosenblum Feb 4, 2020
f415b7f
Renaming StaticKeyring to TestKeyring
WesleyRosenblum Feb 4, 2020
7ec91d6
Adding convenience methods the create builders internally
WesleyRosenblum Feb 6, 2020
876dd5e
Updating wording and adding more Deprecated annotations
WesleyRosenblum Feb 6, 2020
214fabd
Enable AwsKms Client Caching by default to match KmsMasterKeyProvider
WesleyRosenblum Feb 7, 2020
cffe776
Making tests opt-out instead of opt-in and update TestVectorRunner (#…
WesleyRosenblum Feb 7, 2020
8ed8619
Renaming StandardKeyring builder methods and other minors changes
WesleyRosenblum Feb 10, 2020
28aeee2
Fixing test
WesleyRosenblum Feb 11, 2020
ff9dab6
Updating tests to use assertThrows
WesleyRosenblum Feb 11, 2020
12f0c42
Additional example code for Keyrings (#155)
WesleyRosenblum Feb 12, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
import java.util.Map;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CryptoResult;
import com.amazonaws.encryptionsdk.kms.KmsMasterKey;
import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider;
import com.amazonaws.encryptionsdk.AwsCrypto.AwsCryptoConfig;
import com.amazonaws.encryptionsdk.AwsCryptoResult;
import com.amazonaws.encryptionsdk.keyrings.Keyring;
import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
import com.amazonaws.encryptionsdk.kms.KmsClientSupplier;

import static java.util.Collections.emptyList;

/**
* <p>
Expand All @@ -48,32 +52,44 @@ static void encryptAndDecrypt(final String keyArn) {
// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();

// 2. Instantiate a KMS master key provider
final KmsMasterKeyProvider masterKeyProvider = KmsMasterKeyProvider.builder().withKeysForEncryption(keyArn).build();
// 2. Instantiate a KMS Client Supplier. This example uses the default client supplier but you can
// also configure the credentials provider, client configuration and other settings as necessary
final KmsClientSupplier clientSupplier = KmsClientSupplier.builder().build();

// 3. Instantiate a KMS Keyring, supplying the keyArn as the generator for generating a data key.
// For this example, empty lists are provided for grant tokens and additional keys to encrypt the data
// key with, but those can be supplied as necessary.
final Keyring keyring = StandardKeyrings.kms(clientSupplier, emptyList(), emptyList(), keyArn);

// 3. Create an encryption context
// 4. Create an encryption context
//
// Most encrypted data should have an associated encryption context
// to protect integrity. This sample uses placeholder values.
// Most encrypted data should have an associated encryption context
// to protect integrity. This sample uses placeholder values.
//
// For more information see:
// blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
// For more information see:
// blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
final Map<String, String> encryptionContext = Collections.singletonMap("ExampleContextKey", "ExampleContextValue");

// 4. Encrypt the data
final CryptoResult<byte[], KmsMasterKey> encryptResult = crypto.encryptData(masterKeyProvider, EXAMPLE_DATA, encryptionContext);
// 5. Instantiate the AwsCryptoConfig input to AwsCrypto with the keyring and encryption context
final AwsCryptoConfig config = AwsCryptoConfig.builder()
.keyring(keyring)
.encryptionContext(encryptionContext)
.build();

// 6. Encrypt the data
final AwsCryptoResult<byte[]> encryptResult = crypto.encryptData(config, EXAMPLE_DATA);
final byte[] ciphertext = encryptResult.getResult();

// 5. Decrypt the data
final CryptoResult<byte[], KmsMasterKey> decryptResult = crypto.decryptData(masterKeyProvider, ciphertext);
// 7. Decrypt the data
final AwsCryptoResult<byte[]> decryptResult = crypto.decryptData(config, ciphertext);

// 6. Before verifying the plaintext, verify that the customer master key that
// was used in the encryption operation was the one supplied to the master key provider.
if (!decryptResult.getMasterKeyIds().get(0).equals(keyArn)) {
// 8. Before verifying the plaintext, verify that the key that was used in the encryption
// operation was the one used during the decryption operation.
if(!decryptResult.getKeyringTrace().getEntries().get(0).getKeyName().equals(keyArn)) {
throw new IllegalStateException("Wrong key ID!");
}

// 7. Also, verify that the encryption context in the result contains the
// 9. Also, verify that the encryption context in the result contains the
// encryption context supplied to the encryptData method. Because the
// SDK can add values to the encryption context, don't require that
// the entire context matches.
Expand All @@ -82,7 +98,7 @@ static void encryptAndDecrypt(final String keyArn) {
throw new IllegalStateException("Wrong Encryption Context!");
}

// 8. Verify that the decrypted plaintext matches the original plaintext
// 10. Verify that the decrypted plaintext matches the original plaintext
assert Arrays.equals(decryptResult.getResult(), EXAMPLE_DATA);
}
}
Original file line number Diff line number Diff line change
@@ -1,168 +1,172 @@
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
*
*
* http://aws.amazon.com/apache2.0
*
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package com.amazonaws.crypto.examples;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.AwsCrypto.AwsCryptoConfig;
import com.amazonaws.encryptionsdk.keyrings.Keyring;
import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
import com.amazonaws.encryptionsdk.kms.KmsClientSupplier;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CryptoOutputStream;
import com.amazonaws.encryptionsdk.MasterKeyProvider;
import com.amazonaws.encryptionsdk.jce.JceMasterKey;
import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider;
import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory;
import com.amazonaws.util.IOUtils;
import static java.util.Collections.emptyList;

/**
* <p>
* Encrypts a file using both KMS and an asymmetric key pair.
* Encrypts data using both KMS and an asymmetric key pair.
*
* <p>
* Arguments:
* <ol>
* <li>Key ARN: For help finding the Amazon Resource Name (ARN) of your KMS customer master
* <li>Key ARN: For help finding the Amazon Resource Name (ARN) of your KMS customer master
* key (CMK), see 'Viewing Keys' at http://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html
*
* <li>Name of file containing plaintext data to encrypt
* </ol>
*
* You might use AWS Key Management Service (KMS) for most encryption and decryption operations, but
* still want the option of decrypting your data offline independently of KMS. This sample
* You might use AWS Key Management Service (KMS) for most encryption and decryption operations, but
* still want the option of decrypting your data offline independently of KMS. This sample
* demonstrates one way to do this.
*
*
* The sample encrypts data under both a KMS customer master key (CMK) and an "escrowed" RSA key pair
* so that either key alone can decrypt it. You might commonly use the KMS CMK for decryption. However,
* so that either key alone can decrypt it. You might commonly use the KMS CMK for decryption. However,
* at any time, you can use the private RSA key to decrypt the ciphertext independent of KMS.
*
* This sample uses the JCEMasterKey class to generate a RSA public-private key pair
* and saves the key pair in memory. In practice, you would store the private key in a secure offline
* This sample uses an RawRsaKeyring to generate a RSA public-private key pair
* and saves the key pair in memory. In practice, you would store the private key in a secure offline
* location, such as an offline HSM, and distribute the public key to your development team.
*
*/
public class EscrowedEncryptExample {
private static PublicKey publicEscrowKey;
private static PrivateKey privateEscrowKey;
private static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8);

public static void main(final String[] args) throws GeneralSecurityException {
final String kmsArn = args[0];

public static void main(final String[] args) throws Exception {
escrowEncryptAndDecrypt(kmsArn);
}

static void escrowEncryptAndDecrypt(String kmsArn) throws GeneralSecurityException {
// This sample generates a new random key for each operation.
// In practice, you would distribute the public key and save the private key in secure
// storage.
generateEscrowKeyPair();
// In practice, you would distribute the public key and save the private key in secure storage.
final KeyPair escrowKeyPair = generateEscrowKeyPair();

final String kmsArn = args[0];
final String fileName = args[1];
// Encrypt the data under both a KMS Key and an escrowed RSA Key
byte[] encryptedData = standardEncrypt(kmsArn, escrowKeyPair.getPublic());

standardEncrypt(kmsArn, fileName);
standardDecrypt(kmsArn, fileName);
// Decrypt the data using the KMS Key
byte[] standardDecryptedData = standardDecrypt(kmsArn, encryptedData);

escrowDecrypt(fileName);
// Decrypt the data using the escrowed RSA Key
byte[] escrowedDecryptedData = escrowDecrypt(encryptedData, escrowKeyPair.getPublic(), escrowKeyPair.getPrivate());

// Verify both decrypted data instances are the same as the original plaintext
assert Arrays.equals(standardDecryptedData, EXAMPLE_DATA);
assert Arrays.equals(escrowedDecryptedData, EXAMPLE_DATA);
}

private static void standardEncrypt(final String kmsArn, final String fileName) throws Exception {
private static byte[] standardEncrypt(final String kmsArn, final PublicKey publicEscrowKey) {
// Encrypt with the KMS CMK and the escrowed public key

// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();

// 2. Instantiate a KMS master key provider
final KmsMasterKeyProvider kms = new KmsMasterKeyProvider(kmsArn);

// 3. Instantiate a JCE master key provider
// Because the user does not have access to the private escrow key,
// they pass in "null" for the private key parameter.
final JceMasterKey escrowPub = JceMasterKey.getInstance(publicEscrowKey, null, "Escrow", "Escrow",
"RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

// 4. Combine the providers into a single master key provider
final MasterKeyProvider<?> provider = MultipleProviderFactory.buildMultiProvider(kms, escrowPub);

// 5. Encrypt the file
// To simplify the code, we omit the encryption context. Production code should always
// use an encryption context. For an example, see the other SDK samples.
final FileInputStream in = new FileInputStream(fileName);
final FileOutputStream out = new FileOutputStream(fileName + ".encrypted");
final CryptoOutputStream<?> encryptingStream = crypto.createEncryptingStream(provider, out);

IOUtils.copy(in, encryptingStream);
in.close();
encryptingStream.close();
// 2. Instantiate a KMS Client Supplier. This example uses the default client supplier but you can
// also configure the credentials provider, client configuration and other settings as necessary
final KmsClientSupplier clientSupplier = KmsClientSupplier.builder().build();

// 3. Instantiate a KMS Keyring, supplying the keyArn as the generator for generating a data key.
// For this example, empty lists are provided for grant tokens and additional keys to encrypt the data
// key with, but those can be supplied as necessary.
final Keyring kmsKeyring = StandardKeyrings.kms(clientSupplier, emptyList(), emptyList(), kmsArn);

// 4. Instantiate an RawRsaKeyring
// Because the user does not have access to the private escrow key,
// they pass in "null" for the private key parameter.
final Keyring rsaKeyring = StandardKeyrings.rawRsa("Escrow", "Escrow",
publicEscrowKey, null, "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

// 5. Combine the providers into a single MultiKeyring
final Keyring keyring = StandardKeyrings.multi(kmsKeyring, rsaKeyring);

// 6. Instantiate the AwsCryptoConfig input to AwsCrypto with the keyring
// To simplify the code, we omit the encryption context. Production code should always
// use an encryption context. For an example, see the other SDK samples.
final AwsCryptoConfig config = AwsCryptoConfig.builder()
.keyring(keyring)
.build();

// 7. Encrypt the data
return crypto.encryptData(config, EXAMPLE_DATA).getResult();
}

private static void standardDecrypt(final String kmsArn, final String fileName) throws Exception {
// Decrypt with the KMS CMK and the escrow public key. You can use a combined provider,
// as shown here, or just the KMS master key provider.
private static byte[] standardDecrypt(final String kmsArn, final byte[] cipherText) {
// Decrypt with the KMS CMK

// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();

// 2. Instantiate a KMS master key provider
final KmsMasterKeyProvider kms = new KmsMasterKeyProvider(kmsArn);

// 3. Instantiate a JCE master key provider
// Because the user does not have access to the private
// escrow key, they pass in "null" for the private key parameter.
final JceMasterKey escrowPub = JceMasterKey.getInstance(publicEscrowKey, null, "Escrow", "Escrow",
"RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

// 4. Combine the providers into a single master key provider
final MasterKeyProvider<?> provider = MultipleProviderFactory.buildMultiProvider(kms, escrowPub);

// 5. Decrypt the file
// To simplify the code, we omit the encryption context. Production code should always
// use an encryption context. For an example, see the other SDK samples.
final FileInputStream in = new FileInputStream(fileName + ".encrypted");
final FileOutputStream out = new FileOutputStream(fileName + ".decrypted");
final CryptoOutputStream<?> decryptingStream = crypto.createDecryptingStream(provider, out);
IOUtils.copy(in, decryptingStream);
in.close();
decryptingStream.close();
// 2. Instantiate a KMS Client Supplier. This example uses the default client supplier but you can
// also configure the credentials provider, client configuration and other settings as necessary
final KmsClientSupplier clientSupplier = KmsClientSupplier.builder().build();

// 3. Instantiate a KMS Keyring, supplying the keyArn as the generator for generating a data key.
// For this example, empty lists are provided for grant tokens and additional keys to encrypt the data
// key with, but those can be supplied as necessary.
final Keyring kmsKeyring = StandardKeyrings.kms(clientSupplier, emptyList(), emptyList(), kmsArn);

// 4. Instantiate the AwsCryptoConfig input to AwsCrypto with the keyring
// To simplify the code, we omit the encryption context. Production code should always
// use an encryption context. For an example, see the other SDK samples.
final AwsCryptoConfig config = AwsCryptoConfig.builder()
.keyring(kmsKeyring)
.build();

// 5. Decrypt the data
return crypto.decryptData(config, cipherText).getResult();
}

private static void escrowDecrypt(final String fileName) throws Exception {
private static byte[] escrowDecrypt(final byte[] cipherText, final PublicKey publicEscrowKey, final PrivateKey privateEscrowKey) {
// You can decrypt the stream using only the private key.
// This method does not call KMS.

// 1. Instantiate the SDK
final AwsCrypto crypto = new AwsCrypto();

// 2. Instantiate a JCE master key provider
// This method call uses the escrowed private key, not null
final JceMasterKey escrowPriv = JceMasterKey.getInstance(publicEscrowKey, privateEscrowKey, "Escrow", "Escrow",
"RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

// 3. Decrypt the file
// To simplify the code, we omit the encryption context. Production code should always
// use an encryption context. For an example, see the other SDK samples.
final FileInputStream in = new FileInputStream(fileName + ".encrypted");
final FileOutputStream out = new FileOutputStream(fileName + ".deescrowed");
final CryptoOutputStream<?> decryptingStream = crypto.createDecryptingStream(escrowPriv, out);
IOUtils.copy(in, decryptingStream);
in.close();
decryptingStream.close();
// 2. Instantiate a RawRsaKeyring using the escrowed private key
final Keyring rsaKeyring = StandardKeyrings.rawRsa("Escrow", "Escrow",
publicEscrowKey, privateEscrowKey, "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

// 3. Instantiate the AwsCryptoConfig input to AwsCrypto with the keyring
// To simplify the code, we omit the encryption context. Production code should always
// use an encryption context. For an example, see the other SDK samples.
final AwsCryptoConfig config = AwsCryptoConfig.builder()
.keyring(rsaKeyring)
.build();

// 4. Decrypt the data
return crypto.decryptData(config, cipherText).getResult();
}

private static void generateEscrowKeyPair() throws GeneralSecurityException {
private static KeyPair generateEscrowKeyPair() throws GeneralSecurityException {
final KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
kg.initialize(4096); // Escrow keys should be very strong
final KeyPair keyPair = kg.generateKeyPair();
publicEscrowKey = keyPair.getPublic();
privateEscrowKey = keyPair.getPrivate();

return kg.generateKeyPair();
}
}
Loading