diff --git a/pom.xml b/pom.xml index 6722f8e1c..22b369c1e 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,19 @@ test + + com.amazonaws + aws-lambda-java-core + 1.2.0 + test + + + + com.amazonaws + aws-lambda-java-events + 2.2.7 + test + com.google.code.findbugs diff --git a/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java b/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java index 9e76a4627..ce1dcca3c 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java +++ b/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java @@ -28,7 +28,7 @@ /** *

- * Encrypts and then decrypts data using an AWS KMS customer master key. + * Encrypts and then decrypts data using an AWS Key Management Service (AWS KMS) customer master key. * *

* Arguments: @@ -49,9 +49,9 @@ static void encryptAndDecrypt(final AwsKmsCmkId keyArn) { // 1. Instantiate the SDK final AwsCrypto crypto = new AwsCrypto(); - // 2. Instantiate an AWS KMS Keyring, supplying the key ARN as the generator for generating a data key. While - // using a key ARN is a best practice, for encryption operations it is also acceptable to use a CMK alias or - // an alias ARN. + // 2. Instantiate a KMS keyring. Supply the key ARN for the generator key + // that generates a data key. While using a key ARN is a best practice, + // for encryption operations you can also use an alias name or alias ARN. final Keyring keyring = StandardKeyrings.awsKms(keyArn); // 3. Create an encryption context @@ -71,27 +71,25 @@ static void encryptAndDecrypt(final AwsKmsCmkId keyArn) { .plaintext(EXAMPLE_DATA).build()); final byte[] ciphertext = encryptResult.getResult(); - // 5. Decrypt the data. The same keyring may be used to encrypt and decrypt, but for decryption + // 5. Decrypt the data. You can use the same keyring to encrypt and decrypt, but for decryption // the key IDs must be in the key ARN format. final AwsCryptoResult decryptResult = crypto.decrypt( DecryptRequest.builder() .keyring(keyring) .ciphertext(ciphertext).build()); - // 6. Before verifying the plaintext, inspect the Keyring Trace to verify that the CMK used - // to decrypt the encrypted data key was the CMK in the encryption keyring. + // 6. To verify the CMK that was actually used in the decrypt operation, inspect the keyring trace. if(!decryptResult.getKeyringTrace().getEntries().get(0).getKeyName().equals(keyArn.toString())) { throw new IllegalStateException("Wrong key ID!"); } - // 7. 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. - if (!encryptionContext.entrySet().stream() - .allMatch(e -> e.getValue().equals(decryptResult.getEncryptionContext().get(e.getKey())))) { - throw new IllegalStateException("Wrong Encryption Context!"); - } + // 7. To verify that the encryption context used to decrypt the data was the encryption context you expected, + // examine the encryption context in the result. This helps to ensure that you decrypted the ciphertext that + // you intended. + // + // When verifying, test that your expected encryption context is a subset of the actual encryption context, + // not an exact match. The Encryption SDK adds the signing key to the encryption context when appropriate. + assert decryptResult.getEncryptionContext().get("ExampleContextKey").equals("ExampleContextValue"); // 8. Verify that the decrypted plaintext matches the original plaintext assert Arrays.equals(decryptResult.getResult(), EXAMPLE_DATA); diff --git a/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java b/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java index ef4f17dca..32be46198 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java +++ b/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java @@ -84,7 +84,7 @@ private static byte[] standardEncrypt(final AwsKmsCmkId kmsArn, final PublicKey // 1. Instantiate the SDK final AwsCrypto crypto = new AwsCrypto(); - // 2. Instantiate an AWS KMS Keyring, supplying the keyArn as the generator for generating a data key. + // 2. Instantiate a KMS keyring, supplying the keyArn as the generator for generating a data key. final Keyring kmsKeyring = StandardKeyrings.awsKms(kmsArn); // 3. Instantiate a RawRsaKeyring @@ -115,7 +115,7 @@ private static byte[] standardDecrypt(final AwsKmsCmkId kmsArn, final byte[] cip // 1. Instantiate the SDK final AwsCrypto crypto = new AwsCrypto(); - // 2. Instantiate an AWS KMS Keyring, supplying the keyArn as the generator for generating a data key. + // 2. Instantiate a KMS keyring, supplying the keyArn as the generator for generating a data key. final Keyring kmsKeyring = StandardKeyrings.awsKms(kmsArn); // 4. Decrypt the data with the keyring. diff --git a/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java b/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java index f7b8d3436..94cd38853 100644 --- a/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java +++ b/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java @@ -63,9 +63,9 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina // 1. Instantiate the SDK final AwsCrypto crypto = new AwsCrypto(); - // 2. Retrieve an encryption key. In this example, we generate a random key. - // In practice, you would get a key from an existing store - final SecretKey cryptoKey = retrieveEncryptionKey(); + // 2. Get an encryption key. In this example, we generate a random key. + // In practice, you would get a key from an existing key store. + final SecretKey cryptoKey = generateEncryptKey(); // 3. Instantiate a RawAesKeyring using the random key final Keyring keyring = StandardKeyrings.rawAesBuilder() @@ -104,11 +104,12 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina .keyring(keyring) .inputStream(new FileInputStream(encryptedFile)).build())) { - // 8. Verify that the encryption context in the result contains the - // encryption context supplied to the createEncryptingStream method. - if (!"FileStreaming".equals(decryptingStream.getAwsCryptoResult().getEncryptionContext().get("Example"))) { - throw new IllegalStateException("Bad encryption context"); - } + // 8. Verify that the encryption context that was used to decrypt the data is the one that you expect. + // This helps to ensure that the ciphertext that you decrypted was the one that you intended. + // + // When verifying, test that your expected encryption context is a subset of the actual encryption context, + // not an exact match. When appropriate, the Encryption SDK adds the signing key to the encryption context. + assert "FileStreaming".equals(decryptingStream.getAwsCryptoResult().getEncryptionContext().get("Example")); // 9. Copy the plaintext data to a file try (FileOutputStream out = new FileOutputStream(decryptedFile)) { @@ -122,9 +123,9 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina /** * In practice, this key would be saved in a secure location. - * For this demo, we generate a new random key for each operation. + * In this example, we generate a new random key for each operation. */ - private static SecretKey retrieveEncryptionKey() { + private static SecretKey generateEncryptKey() { SecureRandom rnd = new SecureRandom(); byte[] rawKey = new byte[16]; // 128 bits rnd.nextBytes(rawKey); diff --git a/src/examples/java/com/amazonaws/crypto/examples/RawAesKeyringExample.java b/src/examples/java/com/amazonaws/crypto/examples/RawAesKeyringExample.java new file mode 100644 index 000000000..9af19ca54 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/RawAesKeyringExample.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020 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 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 javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +/** + *

+ * Encrypts and then decrypts data using the Raw AES keyring. + */ +public class RawAesKeyringExample { + + private static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8); + + public static void main(final String[] args) { + encryptAndDecrypt(); + } + + static void encryptAndDecrypt() { + // 1. Instantiate the SDK + final AwsCrypto crypto = new AwsCrypto(); + + // 2. Get an encryption key. In this example, we generate a random key. + // In practice, you would get a key from an existing key store + final SecretKey cryptoKey = generateEncryptKey(); + + // 3. Instantiate a Raw AES keyring with the encryption key + final Keyring keyring = StandardKeyrings.rawAesBuilder() + .keyNamespace("ExampleKeyNamespace") + .keyName("ExampleKeyName") + .wrappingKey(cryptoKey).build(); + + // 4. Create an encryption context + // + // 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 + final Map encryptionContext = Collections.singletonMap("ExampleContextKey", "ExampleContextValue"); + + // 5. Encrypt the data with the keyring and encryption context + final AwsCryptoResult encryptResult = crypto.encrypt(EncryptRequest.builder() + .keyring(keyring) + .encryptionContext(encryptionContext) + .plaintext(EXAMPLE_DATA).build()); + final byte[] ciphertext = encryptResult.getResult(); + + // 6. Decrypt the data + final AwsCryptoResult decryptResult = crypto.decrypt(DecryptRequest.builder() + .keyring(keyring) + .ciphertext(ciphertext).build()); + + // 7. Verify that the encryption context that was used to decrypt the data is the one that you expect. + // This helps to ensure that the ciphertext that you decrypted was the one that you intended. + // + // When verifying, test that your expected encryption context is a subset of the actual encryption context, + // not an exact match. When appropriate, the Encryption SDK adds the signing key to the encryption context. + assert decryptResult.getEncryptionContext().get("ExampleContextKey").equals("ExampleContextValue"); + + // 8. Verify that the decrypted plaintext matches the original plaintext + assert Arrays.equals(decryptResult.getResult(), EXAMPLE_DATA); + } + + /** + * In practice, this key would be saved in a secure location. + * For this demo, we generate a new random key for each operation. + */ + private static SecretKey generateEncryptKey() { + SecureRandom rnd = new SecureRandom(); + byte[] rawKey = new byte[16]; // 128 bits + rnd.nextBytes(rawKey); + return new SecretKeySpec(rawKey, "AES"); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExample.java b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExample.java new file mode 100644 index 000000000..a5f3ba488 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExample.java @@ -0,0 +1,56 @@ +/* + * Copyright 2020 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 com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.AwsCryptoResult; +import com.amazonaws.encryptionsdk.DecryptRequest; +import com.amazonaws.encryptionsdk.keyrings.Keyring; +import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings; + +import java.security.KeyPair; + +/** + *

+ * Decrypts data using the Raw RSA keyring. + */ +public class RawRsaKeyringDecryptExample { + + public static byte[] decrypt(byte[] ciphertext, KeyPair keyPair) { + // 1. Instantiate the SDK + final AwsCrypto crypto = new AwsCrypto(); + + // 2. Instantiate a Raw RSA keyring with the private key + final Keyring keyring = StandardKeyrings.rawRsaBuilder() + .keyNamespace("ExampleKeyNamespace") + .keyName("ExampleKeyName") + .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding") + .privateKey(keyPair.getPrivate()).build(); + + // 3. Decrypt the ciphertext with the keyring + final AwsCryptoResult decryptResult = crypto.decrypt(DecryptRequest.builder() + .keyring(keyring) + .ciphertext(ciphertext).build()); + + // 4. Verify that the encryption context that was used to decrypt the data is the one that you expect. + // This helps to ensure that the ciphertext that you decrypted was the one that you intended. + // + // When verifying, test that your expected encryption context is a subset of the actual encryption context, + // not an exact match. When appropriate, the Encryption SDK adds the signing key to the encryption context. + assert decryptResult.getEncryptionContext().get("ExampleContextKey").equals("ExampleContextValue"); + + // 5. Return the decrypted byte array result + return decryptResult.getResult(); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExample.java b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExample.java new file mode 100644 index 000000000..480ac0092 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExample.java @@ -0,0 +1,63 @@ +/* + * Copyright 2020 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 com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.AwsCryptoResult; +import com.amazonaws.encryptionsdk.EncryptRequest; +import com.amazonaws.encryptionsdk.keyrings.Keyring; +import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings; + +import java.nio.charset.StandardCharsets; +import java.security.PublicKey; +import java.util.Collections; +import java.util.Map; + +/** + * Encrypts data using the Raw RSA keyring. + */ +public class RawRsaKeyringEncryptExample { + + static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8); + + public static byte[] encrypt(PublicKey publicKey) { + // 1. Instantiate the SDK + final AwsCrypto crypto = new AwsCrypto(); + + // 2. Instantiate a Raw RSA keyring with the public key + final Keyring keyring = StandardKeyrings.rawRsaBuilder() + .keyNamespace("ExampleKeyNamespace") + .keyName("ExampleKeyName") + .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding") + .publicKey(publicKey).build(); + + // 3. Create an encryption context + // + // 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 + final Map encryptionContext = Collections.singletonMap("ExampleContextKey", "ExampleContextValue"); + + // 4. Encrypt the data with the keyring and encryption context + final AwsCryptoResult encryptResult = crypto.encrypt(EncryptRequest.builder() + .keyring(keyring) + .encryptionContext(encryptionContext) + .plaintext(EXAMPLE_DATA).build()); + + // 5. Return the encrypted byte array result + return encryptResult.getResult(); + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/LambdaDecryptAndWriteExample.java b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/LambdaDecryptAndWriteExample.java new file mode 100644 index 000000000..6cc926d26 --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/LambdaDecryptAndWriteExample.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 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.datakeycaching; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.AwsCryptoResult; +import com.amazonaws.encryptionsdk.DecryptRequest; +import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager; +import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache; +import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import com.amazonaws.services.dynamodbv2.document.DynamoDB; +import com.amazonaws.services.dynamodbv2.document.Item; +import com.amazonaws.services.dynamodbv2.document.Table; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; +import com.amazonaws.util.BinaryUtils; + +/** + * Decrypts all incoming Kinesis records and writes records to DynamoDB. + */ +public class LambdaDecryptAndWriteExample implements RequestHandler { + private static final long MAX_ENTRY_AGE_MILLISECONDS = 600000; + private static final int MAX_CACHE_ENTRIES = 100; + private final CachingCryptoMaterialsManager cachingMaterialsManager_; + private final AwsCrypto crypto_; + private final Table table_; + + /** + * This code doesn't set the max bytes or max message security thresholds that are enforced + * only on data keys used for encryption. + * + * @param cmkArn The AWS KMS customer master key to use for decryption + * @param tableName The name of the DynamoDB table name that stores decrypted messages + */ + public LambdaDecryptAndWriteExample(final String cmkArn, final String tableName) { + cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder() + .withKeyring(StandardKeyrings.awsKms(AwsKmsCmkId.fromString(cmkArn))) + .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES)) + .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS) + .build(); + crypto_ = new AwsCrypto(); + table_ = new DynamoDB(AmazonDynamoDBClientBuilder.defaultClient()).getTable(tableName); + } + + /** + * Decrypts Kinesis events and writes the data to DynamoDB + * + * @param event The KinesisEvent to decrypt + * @param context The lambda context + */ + @Override + public Void handleRequest(KinesisEvent event, Context context) { + for (KinesisEventRecord record : event.getRecords()) { + ByteBuffer ciphertextBuffer = record.getKinesis().getData(); + byte[] ciphertext = BinaryUtils.copyAllBytesFrom(ciphertextBuffer); + + // Decrypt and unpack record + AwsCryptoResult plaintextResult = crypto_.decrypt( + DecryptRequest.builder() + .cryptoMaterialsManager(cachingMaterialsManager_) + .ciphertext(ciphertext).build()); + + // Verify the encryption context value + String streamArn = record.getEventSourceARN(); + String streamName = streamArn.substring(streamArn.indexOf("/") + 1); + if (!streamName.equals(plaintextResult.getEncryptionContext().get("stream"))) { + throw new IllegalStateException("Wrong Encryption Context!"); + } + + // Write record to DynamoDB + String jsonItem = new String(plaintextResult.getResult(), StandardCharsets.UTF_8); + table_.putItem(Item.fromJSON(jsonItem)); + } + + return null; + } +} diff --git a/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/MultiRegionRecordPusherExample.java b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/MultiRegionRecordPusherExample.java new file mode 100644 index 000000000..d7d7e071f --- /dev/null +++ b/src/examples/java/com/amazonaws/crypto/examples/datakeycaching/MultiRegionRecordPusherExample.java @@ -0,0 +1,125 @@ +/* + * Copyright 2020 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.datakeycaching; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.AwsCryptoResult; +import com.amazonaws.encryptionsdk.EncryptRequest; +import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager; +import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache; +import com.amazonaws.encryptionsdk.keyrings.Keyring; +import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings; +import com.amazonaws.encryptionsdk.kms.AwsKmsClientSupplier; +import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId; +import com.amazonaws.regions.Region; +import com.amazonaws.services.kinesis.AmazonKinesis; +import com.amazonaws.services.kinesis.AmazonKinesisClientBuilder; +import com.amazonaws.util.json.Jackson; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static java.util.Collections.emptyList; + +/** + * Pushes data to Kinesis Streams in multiple Regions. + */ +public class MultiRegionRecordPusherExample { + private static final long MAX_ENTRY_AGE_MILLISECONDS = 300000; + private static final long MAX_ENTRY_USES = 100; + private static final int MAX_CACHE_ENTRIES = 100; + private final String streamName_; + private final ArrayList kinesisClients_; + private final CachingCryptoMaterialsManager cachingMaterialsManager_; + private final AwsCrypto crypto_; + + /** + * Creates an instance of this object with Kinesis clients for all target Regions + * and a cached key provider containing AWS KMS master keys in all target Regions. + */ + public MultiRegionRecordPusherExample(final Region[] regions, final String kmsAliasName, final String streamName) { + streamName_ = streamName; + crypto_ = new AwsCrypto(); + kinesisClients_ = new ArrayList<>(); + + final DefaultAWSCredentialsProviderChain credentialsProvider = new DefaultAWSCredentialsProviderChain(); + + // Build AwsKmsKeyring and AmazonKinesisClient objects for each target Region + final List keyrings = new ArrayList<>(); + + for (Region region : regions) { + kinesisClients_.add(AmazonKinesisClientBuilder.standard() + .withCredentials(credentialsProvider) + .withRegion(region.getName()) + .build()); + + keyrings.add(StandardKeyrings.awsKmsBuilder() + .awsKmsClientSupplier(AwsKmsClientSupplier.builder() + .credentialsProvider(credentialsProvider) + .allowedRegions(Collections.singleton(region.getName())) + .build()) + .generatorKeyId(AwsKmsCmkId.fromString(kmsAliasName)).build()); + } + + // Collect keyrings into a single multi-keyring and add cache. In this example, the keyring for the + // first region is used as the generatorKeyring to generate a data key. + final List childrenKeyrings = keyrings.size() > 1 ? keyrings.subList(1, keyrings.size()) : emptyList(); + final Keyring keyring = StandardKeyrings.multi(keyrings.get(0), childrenKeyrings); + + cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder() + .withKeyring(keyring) + .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES)) + .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS) + .withMessageUseLimit(MAX_ENTRY_USES) + .build(); + } + + /** + * JSON serializes and encrypts the received record data and pushes it to all target streams. + */ + public void putRecord(final Map data) { + String partitionKey = UUID.randomUUID().toString(); + Map encryptionContext = Collections.singletonMap("stream", streamName_); + + // JSON serialize data + String jsonData = Jackson.toJsonString(data); + + // Encrypt data + AwsCryptoResult result = crypto_.encrypt( + EncryptRequest.builder() + .cryptoMaterialsManager(cachingMaterialsManager_) + .plaintext(jsonData.getBytes()) + .encryptionContext(encryptionContext) + .build()); + + byte[] encryptedData = result.getResult(); + + // Put records to Kinesis stream in all Regions + for (AmazonKinesis regionalKinesisClient : kinesisClients_) { + regionalKinesisClient.putRecord( + streamName_, + ByteBuffer.wrap(encryptedData), + partitionKey + ); + } + } +} diff --git a/src/test/java/com/amazonaws/crypto/examples/RawAesKeyringExampleTest.java b/src/test/java/com/amazonaws/crypto/examples/RawAesKeyringExampleTest.java new file mode 100644 index 000000000..fa4d2906b --- /dev/null +++ b/src/test/java/com/amazonaws/crypto/examples/RawAesKeyringExampleTest.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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 org.junit.jupiter.api.Test; + +class RawAesKeyringExampleTest { + + @Test + void testEncryptAndDecrypt() { + RawAesKeyringExample.encryptAndDecrypt(); + } +} diff --git a/src/test/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExampleTest.java b/src/test/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExampleTest.java new file mode 100644 index 000000000..ca4c165f1 --- /dev/null +++ b/src/test/java/com/amazonaws/crypto/examples/RawRsaKeyringDecryptExampleTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 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 org.junit.jupiter.api.Test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +class RawRsaKeyringDecryptExampleTest { + + @Test + void testDecrypt() throws Exception { + final KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA"); + kg.initialize(4096); + final KeyPair keyPair = kg.generateKeyPair(); + + byte[] ciphertext = RawRsaKeyringEncryptExample.encrypt(keyPair.getPublic()); + byte[] decryptedResult = RawRsaKeyringDecryptExample.decrypt(ciphertext, keyPair); + + assertArrayEquals(RawRsaKeyringEncryptExample.EXAMPLE_DATA, decryptedResult); + } + +} diff --git a/src/test/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExampleTest.java b/src/test/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExampleTest.java new file mode 100644 index 000000000..25a8bddf2 --- /dev/null +++ b/src/test/java/com/amazonaws/crypto/examples/RawRsaKeyringEncryptExampleTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2020 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 com.amazonaws.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.DecryptRequest; +import com.amazonaws.encryptionsdk.keyrings.Keyring; +import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings; +import org.junit.jupiter.api.Test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +class RawRsaKeyringEncryptExampleTest { + + @Test + void testEncrypt() throws Exception { + final KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA"); + kg.initialize(4096); + final KeyPair keyPair = kg.generateKeyPair(); + + byte[] ciphertext = RawRsaKeyringEncryptExample.encrypt(keyPair.getPublic()); + + final Keyring keyring = StandardKeyrings.rawRsaBuilder() + .keyNamespace("ExampleKeyNamespace") + .keyName("ExampleKeyName") + .privateKey(keyPair.getPrivate()) + .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding") + .build(); + + + final AwsCrypto awsCrypto = new AwsCrypto(); + byte[] decryptedResult = awsCrypto.decrypt(DecryptRequest.builder() + .keyring(keyring) + .ciphertext(ciphertext) + .build()).getResult(); + + assertArrayEquals(RawRsaKeyringEncryptExample.EXAMPLE_DATA, decryptedResult); + } + +}