+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.0
+
+ integration
diff --git a/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java b/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java
index 748cea536..ce1dcca3c 100644
--- a/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java
+++ b/src/examples/java/com/amazonaws/crypto/examples/BasicEncryptionExample.java
@@ -19,13 +19,16 @@
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.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;
/**
*
- * 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:
@@ -39,48 +42,54 @@ public class BasicEncryptionExample {
private static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8);
public static void main(final String[] args) {
- final String keyArn = args[0];
-
- encryptAndDecrypt(keyArn);
+ encryptAndDecrypt(AwsKmsCmkId.fromString(args[0]));
}
- static void encryptAndDecrypt(final String keyArn) {
+ static void encryptAndDecrypt(final AwsKmsCmkId 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 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
//
- // 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 encryptionContext = Collections.singletonMap("ExampleContextKey", "ExampleContextValue");
- // 4. Encrypt the data
- final CryptoResult encryptResult = crypto.encryptData(masterKeyProvider, EXAMPLE_DATA, encryptionContext);
+ // 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());
final byte[] ciphertext = encryptResult.getResult();
- // 5. Decrypt the data
- final CryptoResult decryptResult = crypto.decryptData(masterKeyProvider, ciphertext);
+ // 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, 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)) {
+ // 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 cdf72dc88..32be46198 100644
--- a/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java
+++ b/src/examples/java/com/amazonaws/crypto/examples/EscrowedEncryptExample.java
@@ -1,11 +1,11 @@
/*
* 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.
@@ -13,156 +13,145 @@
package com.amazonaws.crypto.examples;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
+import com.amazonaws.encryptionsdk.AwsCrypto;
+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 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 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 java.util.Arrays;
/**
*
- * Encrypts a file using both KMS and an asymmetric key pair.
+ * Encrypts data using both KMS and an asymmetric key pair.
*
*
* Arguments:
*
- * Key ARN: For help finding the Amazon Resource Name (ARN) of your KMS customer master
+ * 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
- *
- * Name of file containing plaintext data to encrypt
*
*
- * 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 a 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 {
+ escrowEncryptAndDecrypt(AwsKmsCmkId.fromString(args[0]));
+ }
- public static void main(final String[] args) throws Exception {
+ static void escrowEncryptAndDecrypt(AwsKmsCmkId 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();
+
+ // Encrypt the data under both a KMS Key and an escrowed RSA Key
+ byte[] encryptedData = standardEncrypt(kmsArn, escrowKeyPair.getPublic());
- final String kmsArn = args[0];
- final String fileName = args[1];
+ // Decrypt the data using the KMS Key
+ byte[] standardDecryptedData = standardDecrypt(kmsArn, encryptedData);
- standardEncrypt(kmsArn, fileName);
- standardDecrypt(kmsArn, fileName);
+ // Decrypt the data using the escrowed RSA Key
+ byte[] escrowedDecryptedData = escrowDecrypt(encryptedData, escrowKeyPair.getPrivate());
- escrowDecrypt(fileName);
+ // 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 AwsKmsCmkId 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 keyring, supplying the keyArn as the generator for generating a data key.
+ final Keyring kmsKeyring = StandardKeyrings.awsKms(kmsArn);
+
+ // 3. Instantiate a RawRsaKeyring
+ // Because the user does not have access to the private escrow key,
+ // they do not provide the private key parameter.
+ final Keyring rsaKeyring = StandardKeyrings.rawRsaBuilder()
+ .keyNamespace("Escrow")
+ .keyName("Escrow")
+ .publicKey(publicEscrowKey)
+ .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding")
+ .build();
+
+ // 4. Combine the providers into a single MultiKeyring
+ final Keyring keyring = StandardKeyrings.multi(kmsKeyring, rsaKeyring);
+
+ // 5. Encrypt the data 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.
+ return crypto.encrypt(EncryptRequest.builder()
+ .keyring(keyring)
+ .plaintext(EXAMPLE_DATA).build())
+ .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 AwsKmsCmkId 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 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.
+ // 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.
+ return crypto.decrypt(DecryptRequest.builder()
+ .keyring(kmsKeyring)
+ .ciphertext(cipherText).build()).getResult();
}
- private static void escrowDecrypt(final String fileName) throws Exception {
+ private static byte[] escrowDecrypt(final byte[] cipherText, 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.rawRsaBuilder()
+ .keyNamespace("Escrow")
+ .keyName("Escrow")
+ .privateKey(privateEscrowKey)
+ .wrappingAlgorithm("RSA/ECB/OAEPWithSHA-512AndMGF1Padding")
+ .build();
+
+ // 3. Decrypt the data 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.
+ return crypto.decrypt(DecryptRequest.builder()
+ .keyring(rsaKeyring)
+ .ciphertext(cipherText).build()).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();
}
}
diff --git a/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java b/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java
index 22ade3b3b..94cd38853 100644
--- a/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java
+++ b/src/examples/java/com/amazonaws/crypto/examples/FileStreamingExample.java
@@ -1,11 +1,11 @@
/*
* 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.
@@ -13,21 +13,26 @@
package com.amazonaws.crypto.examples;
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.AwsCryptoInputStream;
+import com.amazonaws.encryptionsdk.CreateDecryptingInputStreamRequest;
+import com.amazonaws.encryptionsdk.CreateEncryptingInputStreamRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
+import com.amazonaws.util.IOUtils;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.BufferedReader;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Map;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import com.amazonaws.encryptionsdk.AwsCrypto;
-import com.amazonaws.encryptionsdk.CryptoInputStream;
-import com.amazonaws.encryptionsdk.MasterKey;
-import com.amazonaws.encryptionsdk.jce.JceMasterKey;
-import com.amazonaws.util.IOUtils;
+import java.util.Objects;
/**
*
@@ -40,61 +45,106 @@
*
*
*
- * This program demonstrates using a standard Java {@link SecretKey} object as a {@link MasterKey} to
+ * This program demonstrates using a standard Java {@link SecretKey} object in a {@link Keyring} to
* encrypt and decrypt streaming data.
*/
public class FileStreamingExample {
- private static String srcFile;
public static void main(String[] args) throws IOException {
- srcFile = args[0];
+ final File srcFile = new File(args[0]);
+ final File encryptedFile = new File(args[1]);
+ final File decryptedFile = new File(args[2]);
+
+ encryptAndDecrypt(srcFile, encryptedFile, decryptedFile);
+
+ }
- // In this example, we generate a random key. In practice,
- // you would get a key from an existing store
- SecretKey cryptoKey = retrieveEncryptionKey();
+ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, final File decryptedFile) throws IOException {
+ // 1. Instantiate the SDK
+ final AwsCrypto crypto = new AwsCrypto();
- // Create a JCE master key provider using the random key and an AES-GCM encryption algorithm
- JceMasterKey masterKey = JceMasterKey.getInstance(cryptoKey, "Example", "RandomKey", "AES/GCM/NoPadding");
+ // 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();
- // Instantiate the SDK
- AwsCrypto crypto = new AwsCrypto();
+ // 3. Instantiate a RawAesKeyring using the random key
+ final Keyring keyring = StandardKeyrings.rawAesBuilder()
+ .keyNamespace("Example")
+ .keyName("RandomKey")
+ .wrappingKey(cryptoKey)
+ .build();
- // Create an encryption context to identify this ciphertext
- Map context = Collections.singletonMap("Example", "FileStreaming");
+ // 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("Example", "FileStreaming");
+
+ // 5. Create the encrypting input stream with the keyring and encryption context.
+ // Because the file might be too large to load into memory,
+ // we stream the data, instead of loading it all at once.
+ try (final AwsCryptoInputStream encryptingStream = crypto.createEncryptingInputStream(
+ CreateEncryptingInputStreamRequest.builder()
+ .keyring(keyring)
+ .encryptionContext(encryptionContext)
+ .inputStream(new FileInputStream(srcFile)).build())) {
+
+ // 6. Copy the encrypted data into the encrypted file.
+ try (FileOutputStream out = new FileOutputStream(encryptedFile)) {
+ IOUtils.copy(encryptingStream, out);
+ }
+ }
- // Because the file might be to large to load into memory, we stream the data, instead of
- //loading it all at once.
- FileInputStream in = new FileInputStream(srcFile);
- CryptoInputStream encryptingStream = crypto.createEncryptingStream(masterKey, in, context);
+ // 7. Create the decrypting input stream with the keyring.
+ try (final AwsCryptoInputStream decryptingStream = crypto.createDecryptingInputStream(
+ CreateDecryptingInputStreamRequest.builder()
+ .keyring(keyring)
+ .inputStream(new FileInputStream(encryptedFile)).build())) {
- FileOutputStream out = new FileOutputStream(srcFile + ".encrypted");
- IOUtils.copy(encryptingStream, out);
- encryptingStream.close();
- out.close();
+ // 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"));
- // Decrypt the file. Verify the encryption context before returning the plaintext.
- in = new FileInputStream(srcFile + ".encrypted");
- CryptoInputStream decryptingStream = crypto.createDecryptingStream(masterKey, in);
- // Does it contain the expected encryption context?
- if (!"FileStreaming".equals(decryptingStream.getCryptoResult().getEncryptionContext().get("Example"))) {
- throw new IllegalStateException("Bad encryption context");
+ // 9. Copy the plaintext data to a file
+ try (FileOutputStream out = new FileOutputStream(decryptedFile)) {
+ IOUtils.copy(decryptingStream, out);
+ }
}
- // Return the plaintext data
- out = new FileOutputStream(srcFile + ".decrypted");
- IOUtils.copy(decryptingStream, out);
- decryptingStream.close();
- out.close();
+ // 10. Compare the decrypted file to the original
+ compareFiles(decryptedFile, srcFile);
}
/**
- * In practice, this key would be saved in a secure location.
- * For this demo, we generate a new random key for each operation.
+ * In practice, this key would be saved in a secure location.
+ * 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);
return new SecretKeySpec(rawKey, "AES");
}
-}
\ No newline at end of file
+
+ private static void compareFiles(File file1, File file2) throws IOException {
+ assert file1.length() == file2.length();
+
+ try (BufferedReader file1Reader = Files.newBufferedReader(file1.toPath());
+ BufferedReader file2Reader = Files.newBufferedReader(file2.toPath())) {
+ String file1Line;
+ String file2Line;
+
+ while ((file1Line = file1Reader.readLine()) != null &&
+ (file2Line = file2Reader.readLine()) != null) {
+ assert Objects.equals(file1Line, file2Line);
+ }
+ }
+ }
+
+}
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/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java b/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
index fecd7333c..61d185fe8 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
@@ -18,6 +18,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
+import java.util.function.Consumer;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
@@ -27,31 +28,46 @@
import com.amazonaws.encryptionsdk.internal.MessageCryptoHandler;
import com.amazonaws.encryptionsdk.internal.ProcessingSummary;
import com.amazonaws.encryptionsdk.internal.Utils;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
+import static java.util.Objects.requireNonNull;
+
/**
* Provides the primary entry-point to the AWS Encryption SDK. All encryption and decryption
* operations should start here. Most people will want to use either
- * {@link #encryptData(MasterKeyProvider, byte[], Map)} and
- * {@link #decryptData(MasterKeyProvider, byte[])} to encrypt/decrypt things.
+ * {@link #encrypt(EncryptRequest)} and
+ * {@link #decrypt(DecryptRequest)} to encrypt/decrypt things.
*
*
* The core concepts (and classes) in this SDK are:
*
* {@link AwsCrypto}
- * {@link DataKey}
+ * {@link Keyring}
+ * {@link EncryptedDataKey}
* {@link MasterKey}
* {@link MasterKeyProvider}
*
*
*
* {@link AwsCrypto} provides the primary way to encrypt/decrypt data. It can operate on
- * byte-arrays, streams, or {@link java.lang.String Strings}. This data is encrypted using the
- * specifed {@link CryptoAlgorithm} and a {@link DataKey} which is unique to each encrypted message.
- * This {@code DataKey} is then encrypted using one (or more) {@link MasterKey MasterKeys}. The
- * process is reversed on decryption with the code selecting a copy of the {@code DataKey} protected
- * by a usable {@code MasterKey}, decrypting the {@code DataKey}, and then decrypted the message.
+ * byte-arrays and streams. This data is encrypted using the specified {@link CryptoAlgorithm} and a
+ * {@code plaintextDataKey} which is unique to each encrypted message. This {@code plaintextDataKey} is then encrypted
+ * using one (or more) {@link MasterKey MasterKeys} or a {@link Keyring}. The process is reversed on decryption with the
+ * code selecting an {@code EncryptedDataKey} embedded in the encrypted message, decrypting the {@code EncryptedDataKey}
+ * using a {@link MasterKey MasterKey} or {@link Keyring}, and then decrypting the message.
+ *
+ *
+ * Note:
+ * {@link MasterKey}s and {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s. Keyrings
+ * provide a simplified architecture over {@link MasterKey}s and {@link MasterKeyProvider}s; The keyring combines the
+ * similar responsibilities of {@link MasterKey}s and {@link MasterKeyProvider}s into one concept, as well as removes
+ * all key management logic from {@link CryptoMaterialsManager}s.
+ *
+ * Due to this simplified architecture, {@link MasterKey}s and {@link MasterKeyProvider}s are deprecated and will be
+ * removed in the future, and new implementations should use {@link Keyring}s. The below discussion on
+ * {@link MasterKeyProvider}s is provided for informational purposes as you transition to {@link Keyring}s.
*
*
* The main way to get a {@code MasterKey} is through the use of a {@link MasterKeyProvider}. This
@@ -150,7 +166,11 @@ public int getEncryptionFrameSize() {
*
* This method is equivalent to calling {@link #estimateCiphertextSize(CryptoMaterialsManager, int, Map)} with a
* {@link DefaultCryptoMaterialsManager} based on the given provider.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #estimateCiphertextSize(EstimateCiphertextSizeRequest)}
*/
+ @Deprecated
public > long estimateCiphertextSize(
final MasterKeyProvider provider,
final int plaintextSize,
@@ -162,34 +182,31 @@ public > long estimateCiphertextSize(
/**
* Returns the best estimate for the output length of encrypting a plaintext with the provided
* {@code plaintextSize} and {@code encryptionContext}. The actual ciphertext may be shorter.
+ *
+ * @deprecated Replaced by {@link #estimateCiphertextSize(EstimateCiphertextSizeRequest)}
*/
+ @Deprecated
public long estimateCiphertextSize(
CryptoMaterialsManager materialsManager,
final int plaintextSize,
final Map encryptionContext
) {
- EncryptionMaterialsRequest request = EncryptionMaterialsRequest.newBuilder()
- .setContext(encryptionContext)
- .setRequestedAlgorithm(getEncryptionAlgorithm())
- // We're not actually encrypting any data, so don't consume any bytes from the cache's limits. We do need to
- // pass /something/ though, or the cache will be bypassed (as it'll assume this is a streaming encrypt of
- // unknown size).
- .setPlaintextSize(0)
- .build();
-
- final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
- getEncryptionFrameSize(),
- checkAlgorithm(materialsManager.getMaterialsForEncrypt(request))
- );
-
- return cryptoHandler.estimateOutputSize(plaintextSize);
+ return estimateCiphertextSize(EstimateCiphertextSizeRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .encryptionContext(encryptionContext)
+ .plaintextSize(plaintextSize)
+ .build());
}
/**
* Returns the equivalent to calling
* {@link #estimateCiphertextSize(MasterKeyProvider, int, Map)} with an empty
* {@code encryptionContext}.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #estimateCiphertextSize(EstimateCiphertextSizeRequest)}
*/
+ @Deprecated
public > long estimateCiphertextSize(
final MasterKeyProvider provider,
final int plaintextSize
@@ -201,7 +218,10 @@ public > long estimateCiphertextSize(
* Returns the equivalent to calling
* {@link #estimateCiphertextSize(CryptoMaterialsManager, int, Map)} with an empty
* {@code encryptionContext}.
+ *
+ * @deprecated Replaced by {@link #estimateCiphertextSize(EstimateCiphertextSizeRequest)}
*/
+ @Deprecated
public long estimateCiphertextSize(
final CryptoMaterialsManager materialsManager,
final int plaintextSize
@@ -210,13 +230,59 @@ public long estimateCiphertextSize(
}
/**
- * Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
- * DataKeys} that are in turn protected by {@link MasterKey MasterKeys} provided by
+ * Returns the best estimate for the output length of encrypting a plaintext with the plaintext size specified in
+ * the provided {@link EstimateCiphertextSizeRequest}. The actual ciphertext may be shorter.
+ *
+ * @param request The {@link EstimateCiphertextSizeRequest}
+ * @return The estimated output length in bytes
+ */
+ public long estimateCiphertextSize(final EstimateCiphertextSizeRequest request) {
+ requireNonNull(request, "request is required");
+
+ final EncryptionMaterialsRequest encryptionMaterialsRequest = EncryptionMaterialsRequest.newBuilder()
+ .setContext(request.encryptionContext())
+ .setRequestedAlgorithm(getEncryptionAlgorithm())
+ // We're not actually encrypting any data, so don't consume any bytes from the cache's limits. We do need to
+ // pass /something/ though, or the cache will be bypassed (as it'll assume this is a streaming encrypt of
+ // unknown size).
+ .setPlaintextSize(0)
+ .build();
+
+ final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
+ getEncryptionFrameSize(),
+ checkAlgorithm(request.cryptoMaterialsManager().getMaterialsForEncrypt(encryptionMaterialsRequest))
+ );
+
+ return cryptoHandler.estimateOutputSize(request.plaintextSize());
+ }
+
+ /**
+ * Returns the best estimate for the output length of encrypting a plaintext with the plaintext size specified in
+ * the provided {@link EstimateCiphertextSizeRequest}. The actual ciphertext may be shorter.
+ *
+ * This is a convenience which creates an instance of the {@link EstimateCiphertextSizeRequest.Builder} avoiding the need to
+ * create one manually via {@link EstimateCiphertextSizeRequest#builder()}
+ *
+ *
+ * @param request A Consumer that will call methods on EstimateCiphertextSizeRequest.Builder to create a request.
+ * @return The estimated output length in bytes
+ */
+ public long estimateCiphertextSize(final Consumer request) {
+ return estimateCiphertextSize(EstimateCiphertextSizeRequest.builder().applyMutation(request).build());
+ }
+
+ /**
+ * Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey}s
+ * that are in turn protected by {@link MasterKey MasterKeys} provided by
* {@code provider}.
*
* This method is equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)} using a
* {@link DefaultCryptoMaterialsManager} based on the given provider.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #encrypt(EncryptRequest)}
*/
+ @Deprecated
public > CryptoResult encryptData(
final MasterKeyProvider provider,
final byte[] plaintext,
@@ -230,38 +296,29 @@ public > CryptoResult encryptData(
/**
* Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
* DataKeys} that are in turn protected by the given CryptoMaterialsProvider.
+ *
+ * @deprecated Replaced by {@link #encrypt(EncryptRequest)}
*/
+ @Deprecated
public CryptoResult encryptData(
CryptoMaterialsManager materialsManager,
final byte[] plaintext,
final Map encryptionContext
) {
- EncryptionMaterialsRequest request = EncryptionMaterialsRequest.newBuilder()
- .setContext(encryptionContext)
- .setRequestedAlgorithm(getEncryptionAlgorithm())
- .setPlaintext(plaintext)
- .build();
-
- final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
- getEncryptionFrameSize(),
- checkAlgorithm(materialsManager.getMaterialsForEncrypt(request))
- );
-
- final int outSizeEstimate = cryptoHandler.estimateOutputSize(plaintext.length);
- final byte[] out = new byte[outSizeEstimate];
- int outLen = cryptoHandler.processBytes(plaintext, 0, plaintext.length, out, 0).getBytesWritten();
- outLen += cryptoHandler.doFinal(out, outLen);
-
- final byte[] outBytes = Utils.truncate(out, outLen);
-
- //noinspection unchecked
- return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
+ return encrypt(EncryptRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .plaintext(plaintext)
+ .encryptionContext(encryptionContext).build()).toCryptoResult();
}
/**
* Returns the equivalent to calling {@link #encryptData(MasterKeyProvider, byte[], Map)} with
* an empty {@code encryptionContext}.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #encrypt(EncryptRequest)}
*/
+ @Deprecated
public > CryptoResult encryptData(final MasterKeyProvider provider,
final byte[] plaintext) {
return encryptData(provider, plaintext, EMPTY_MAP);
@@ -270,7 +327,10 @@ public > CryptoResult encryptData(final Master
/**
* Returns the equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)} with
* an empty {@code encryptionContext}.
+ *
+ * @deprecated Replaced by {@link #encrypt(EncryptRequest)}
*/
+ @Deprecated
public CryptoResult encryptData(
final CryptoMaterialsManager materialsManager,
final byte[] plaintext
@@ -278,11 +338,59 @@ public > CryptoResult encryptData(final Master
return encryptData(materialsManager, plaintext, EMPTY_MAP);
}
+ /**
+ * Returns an encrypted form of {@code plaintext} that has been protected with either the {@link CryptoMaterialsManager}
+ * or the {@link Keyring} specified in the {@link EncryptRequest}.
+ *
+ * @param request The {@link EncryptRequest}
+ * @return An {@link AwsCryptoResult} containing the encrypted data
+ */
+ public AwsCryptoResult encrypt(final EncryptRequest request) {
+ requireNonNull(request, "request is required");
+
+ final EncryptionMaterialsRequest encryptionMaterialsRequest = EncryptionMaterialsRequest.newBuilder()
+ .setContext(request.encryptionContext())
+ .setRequestedAlgorithm(getEncryptionAlgorithm())
+ .setPlaintext(request.plaintext())
+ .build();
+
+ final EncryptionMaterials encryptionMaterials =
+ checkAlgorithm(request.cryptoMaterialsManager().getMaterialsForEncrypt(encryptionMaterialsRequest));
+
+ final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
+ getEncryptionFrameSize(), encryptionMaterials);
+
+ final int outSizeEstimate = cryptoHandler.estimateOutputSize(request.plaintext().length);
+ final byte[] out = new byte[outSizeEstimate];
+ int outLen = cryptoHandler.processBytes(request.plaintext(), 0, request.plaintext().length, out, 0).getBytesWritten();
+ outLen += cryptoHandler.doFinal(out, outLen);
+
+ final byte[] outBytes = Utils.truncate(out, outLen);
+
+ return new AwsCryptoResult<>(outBytes, encryptionMaterials.getKeyringTrace(),
+ cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
+ }
+
+ /**
+ * Returns an encrypted form of {@code plaintext} that has been protected with either the {@link CryptoMaterialsManager}
+ * or the {@link Keyring} specified in the {@link EncryptRequest}.
+ *
+ * This is a convenience which creates an instance of the {@link EncryptRequest.Builder} avoiding the need to
+ * create one manually via {@link EncryptRequest#builder()}
+ *
+ *
+ * @param request A Consumer that will call methods on EncryptRequest.Builder to create a request.
+ * @return An {@link AwsCryptoResult} containing the encrypted data
+ */
+ public AwsCryptoResult encrypt(final Consumer request) {
+ return encrypt(EncryptRequest.builder().applyMutation(request).build());
+ }
+
/**
* Calls {@link #encryptData(MasterKeyProvider, byte[], Map)} on the UTF-8 encoded bytes of
* {@code plaintext} and base64 encodes the result.
- * @deprecated Use the {@link #encryptData(MasterKeyProvider, byte[], Map)} and
- * {@link #decryptData(MasterKeyProvider, byte[])} APIs instead. {@code encryptString} and {@code decryptString}
+ * @deprecated Use the {@link #encrypt(EncryptRequest)} and
+ * {@link #decrypt(DecryptRequest)} APIs instead. {@code encryptString} and {@code decryptString}
* work as expected if you use them together. However, to work with other language implementations of the AWS
* Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
* {@code decryptString}. These deprecated APIs will be removed in the future.
@@ -358,7 +466,11 @@ public > CryptoResult encryptString(final Mast
* Decrypts the provided {@code ciphertext} by requesting that the {@code provider} unwrap any
* usable {@link DataKey} in the ciphertext and then decrypts the ciphertext using that
* {@code DataKey}.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #decrypt(DecryptRequest)}
*/
+ @Deprecated
public > CryptoResult decryptData(final MasterKeyProvider provider,
final byte[] ciphertext) {
return decryptData(Utils.assertNonNull(provider, "provider"), new
@@ -372,7 +484,10 @@ public > CryptoResult decryptData(final Master
* @param materialsManager the {@link CryptoMaterialsManager} to use for decryption operations.
* @param ciphertext the ciphertext to attempt to decrypt.
* @return the {@link CryptoResult} with the decrypted data.
+ *
+ * @deprecated Replaced by {@link #decrypt(DecryptRequest)}
*/
+ @Deprecated
public CryptoResult decryptData(
final CryptoMaterialsManager materialsManager,
final byte[] ciphertext
@@ -383,8 +498,12 @@ public > CryptoResult decryptData(final Master
/**
* @see #decryptData(MasterKeyProvider, byte[])
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #decrypt(DecryptRequest)}
*/
@SuppressWarnings("unchecked")
+ @Deprecated
public > CryptoResult decryptData(
final MasterKeyProvider provider, final ParsedCiphertext ciphertext) {
Utils.assertNonNull(provider, "provider");
@@ -393,22 +512,38 @@ public > CryptoResult decryptData(
/**
* @see #decryptData(CryptoMaterialsManager, byte[])
+ *
+ * @deprecated Replaced by {@link #decrypt(DecryptRequest)}
*/
+ @Deprecated
public CryptoResult decryptData(
final CryptoMaterialsManager materialsManager,
final ParsedCiphertext ciphertext
) {
- Utils.assertNonNull(materialsManager, "materialsManager");
+ return decrypt(DecryptRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .parsedCiphertext(ciphertext).build()).toCryptoResult();
+ }
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager, ciphertext);
+ /**
+ * Decrypts the provided {@link ParsedCiphertext} using the {@link CryptoMaterialsManager} or the {@link Keyring}
+ * specified in the {@link DecryptRequest}.
+ *
+ * @param request The {@link DecryptRequest}
+ * @return An {@link AwsCryptoResult} containing the decrypted data
+ */
+ public AwsCryptoResult decrypt(final DecryptRequest request) {
+ requireNonNull(request, "request is required");
- final byte[] ciphertextBytes = ciphertext.getCiphertext();
- final int contentLen = ciphertextBytes.length - ciphertext.getOffset();
+ final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(
+ request.cryptoMaterialsManager(), request.parsedCiphertext());
+
+ final byte[] ciphertextBytes = request.parsedCiphertext().getCiphertext();
+ final int contentLen = ciphertextBytes.length - request.parsedCiphertext().getOffset();
final int outSizeEstimate = cryptoHandler.estimateOutputSize(contentLen);
final byte[] out = new byte[outSizeEstimate];
- final ProcessingSummary processed = cryptoHandler.processBytes(ciphertextBytes, ciphertext.getOffset(),
- contentLen, out,
- 0);
+ final ProcessingSummary processed = cryptoHandler.processBytes(ciphertextBytes, request.parsedCiphertext().getOffset(),
+ contentLen, out, 0);
if (processed.getBytesProcessed() != contentLen) {
throw new BadCiphertextException("Unable to process entire ciphertext. May have trailing data.");
}
@@ -417,8 +552,23 @@ public > CryptoResult decryptData(
final byte[] outBytes = Utils.truncate(out, outLen);
- //noinspection unchecked
- return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
+ return new AwsCryptoResult<>(outBytes, cryptoHandler.getKeyringTrace(),
+ cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
+ }
+
+ /**
+ * Decrypts the provided {@link ParsedCiphertext} using the {@link CryptoMaterialsManager} or the {@link Keyring}
+ * specified in the {@link DecryptRequest}.
+ *
+ * This is a convenience which creates an instance of the {@link DecryptRequest.Builder} avoiding the need to
+ * create one manually via {@link DecryptRequest#builder()}
+ *
+ *
+ * @param request A Consumer that will call methods on DecryptRequest.Builder to create a request.
+ * @return An {@link AwsCryptoResult} containing the decrypted data
+ */
+ public AwsCryptoResult decrypt(final Consumer request) {
+ return decrypt(DecryptRequest.builder().applyMutation(request).build());
}
/**
@@ -475,7 +625,11 @@ public > CryptoResult decryptString(
*
* @see #encryptData(MasterKeyProvider, byte[], Map)
* @see javax.crypto.CipherOutputStream
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #createEncryptingOutputStream(CreateEncryptingOutputStreamRequest)}
*/
+ @Deprecated
public > CryptoOutputStream createEncryptingStream(
final MasterKeyProvider provider,
final OutputStream os,
@@ -492,20 +646,31 @@ public > CryptoOutputStream createEncryptingStream(
*
* @see #encryptData(MasterKeyProvider, byte[], Map)
* @see javax.crypto.CipherOutputStream
+ *
+ * @deprecated Replaced by {@link #createEncryptingOutputStream(CreateEncryptingOutputStreamRequest)}
*/
+ @Deprecated
public CryptoOutputStream> createEncryptingStream(
final CryptoMaterialsManager materialsManager,
final OutputStream os,
final Map encryptionContext
) {
- return new CryptoOutputStream<>(os, getEncryptingStreamHandler(materialsManager, encryptionContext));
+ return createEncryptingOutputStream(CreateEncryptingOutputStreamRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .outputStream(os)
+ .encryptionContext(encryptionContext)
+ .build()).toCryptoOutputStream();
}
/**
* Returns the equivalent to calling
* {@link #createEncryptingStream(MasterKeyProvider, OutputStream, Map)} with an empty
* {@code encryptionContext}.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #createEncryptingOutputStream(CreateEncryptingOutputStreamRequest)}
*/
+ @Deprecated
public > CryptoOutputStream createEncryptingStream(
final MasterKeyProvider provider,
final OutputStream os) {
@@ -516,7 +681,10 @@ public > CryptoOutputStream createEncryptingStream(
* Returns the equivalent to calling
* {@link #createEncryptingStream(CryptoMaterialsManager, OutputStream, Map)} with an empty
* {@code encryptionContext}.
+ *
+ * @deprecated Replaced by {@link #createEncryptingOutputStream(CreateEncryptingOutputStreamRequest)}
*/
+ @Deprecated
public CryptoOutputStream> createEncryptingStream(
final CryptoMaterialsManager materialsManager,
final OutputStream os
@@ -524,13 +692,44 @@ public CryptoOutputStream> createEncryptingStream(
return createEncryptingStream(materialsManager, os, EMPTY_MAP);
}
+ /**
+ * Returns a {@link AwsCryptoOutputStream} which encrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * @see #encrypt(EncryptRequest)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public AwsCryptoOutputStream createEncryptingOutputStream(final CreateEncryptingOutputStreamRequest request) {
+ return new AwsCryptoOutputStream(request.outputStream(), getEncryptingStreamHandler(
+ request.cryptoMaterialsManager(), request.encryptionContext()));
+ }
+
+ /**
+ * Returns a {@link AwsCryptoOutputStream} which encrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * This is a convenience which creates an instance of the {@link CreateEncryptingOutputStreamRequest.Builder} avoiding the need to
+ * create one manually via {@link CreateEncryptingOutputStreamRequest#builder()}
+ *
+ *
+ * @see #encrypt(EncryptRequest)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public AwsCryptoOutputStream createEncryptingOutputStream(final Consumer request) {
+ return createEncryptingOutputStream(CreateEncryptingOutputStreamRequest.builder().applyMutation(request).build());
+ }
+
/**
* Returns a {@link CryptoInputStream} which encrypts the data after reading it from the
* underlying {@link InputStream}.
*
* @see #encryptData(MasterKeyProvider, byte[], Map)
* @see javax.crypto.CipherInputStream
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #createEncryptingInputStream(CreateEncryptingInputStreamRequest)}
*/
+ @Deprecated
public > CryptoInputStream createEncryptingStream(
final MasterKeyProvider provider,
final InputStream is,
@@ -547,22 +746,30 @@ public > CryptoInputStream createEncryptingStream(
*
* @see #encryptData(MasterKeyProvider, byte[], Map)
* @see javax.crypto.CipherInputStream
+ *
+ * @deprecated Replaced by {@link #createEncryptingInputStream(CreateEncryptingInputStreamRequest)}
*/
+ @Deprecated
public CryptoInputStream> createEncryptingStream(
CryptoMaterialsManager materialsManager,
final InputStream is,
final Map encryptionContext
) {
- final MessageCryptoHandler cryptoHandler = getEncryptingStreamHandler(materialsManager, encryptionContext);
-
- return new CryptoInputStream<>(is, cryptoHandler);
+ return createEncryptingInputStream(CreateEncryptingInputStreamRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .encryptionContext(encryptionContext)
+ .inputStream(is).build()).toCryptoInputStream();
}
/**
* Returns the equivalent to calling
* {@link #createEncryptingStream(MasterKeyProvider, InputStream, Map)} with an empty
* {@code encryptionContext}.
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #createEncryptingInputStream(CreateEncryptingInputStreamRequest)}
*/
+ @Deprecated
public > CryptoInputStream createEncryptingStream(
final MasterKeyProvider provider,
final InputStream is
@@ -574,12 +781,47 @@ public > CryptoInputStream createEncryptingStream(
* Returns the equivalent to calling
* {@link #createEncryptingStream(CryptoMaterialsManager, InputStream, Map)} with an empty
* {@code encryptionContext}.
+ *
+ * @deprecated Replaced by {@link #createEncryptingInputStream(CreateEncryptingInputStreamRequest)}
*/
+ @Deprecated
public CryptoInputStream> createEncryptingStream(
final CryptoMaterialsManager materialsManager,
final InputStream is
) {
- return createEncryptingStream(materialsManager, is, EMPTY_MAP);
+ return createEncryptingInputStream(CreateEncryptingInputStreamRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .inputStream(is).build()).toCryptoInputStream();
+ }
+
+ /**
+ * Returns a {@link AwsCryptoInputStream} which encrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * @see #encrypt(EncryptRequest)
+ * @see javax.crypto.CipherInputStream
+ */
+ public AwsCryptoInputStream createEncryptingInputStream(final CreateEncryptingInputStreamRequest request) {
+ requireNonNull(request, "request is required");
+ final MessageCryptoHandler cryptoHandler = getEncryptingStreamHandler(
+ request.cryptoMaterialsManager(), request.encryptionContext());
+
+ return new AwsCryptoInputStream(request.inputStream(), cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link AwsCryptoInputStream} which encrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * This is a convenience which creates an instance of the {@link CreateEncryptingInputStreamRequest.Builder} avoiding the need to
+ * create one manually via {@link CreateEncryptingInputStreamRequest#builder()}
+ *
+ *
+ * @see #encrypt(EncryptRequest)
+ * @see javax.crypto.CipherInputStream
+ */
+ public AwsCryptoInputStream createEncryptingInputStream(final Consumer request) {
+ return createEncryptingInputStream(CreateEncryptingInputStreamRequest.builder().applyMutation(request).build());
}
/**
@@ -588,7 +830,11 @@ public CryptoInputStream> createEncryptingStream(
*
* @see #decryptData(MasterKeyProvider, byte[])
* @see javax.crypto.CipherOutputStream
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #createDecryptingOutputStream(CreateDecryptingOutputStreamRequest)}
*/
+ @Deprecated
public > CryptoOutputStream createDecryptingStream(
final MasterKeyProvider provider, final OutputStream os) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider);
@@ -601,7 +847,11 @@ public > CryptoOutputStream createDecryptingStream(
*
* @see #decryptData(MasterKeyProvider, byte[])
* @see javax.crypto.CipherInputStream
+ *
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ * Replaced by {@link #createDecryptingInputStream(CreateDecryptingInputStreamRequest)}
*/
+ @Deprecated
public > CryptoInputStream createDecryptingStream(
final MasterKeyProvider provider, final InputStream is) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider);
@@ -614,12 +864,44 @@ public > CryptoInputStream createDecryptingStream(
*
* @see #decryptData(CryptoMaterialsManager, byte[])
* @see javax.crypto.CipherOutputStream
+ *
+ * @deprecated Replaced by {@link #createDecryptingOutputStream(CreateDecryptingOutputStreamRequest)}
*/
+ @Deprecated
public CryptoOutputStream> createDecryptingStream(
final CryptoMaterialsManager materialsManager, final OutputStream os
) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager);
- return new CryptoOutputStream(os, cryptoHandler);
+ return createDecryptingOutputStream(CreateDecryptingOutputStreamRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .outputStream(os).build()).toCryptoOutputStream();
+ }
+
+ /**
+ * Returns a {@link AwsCryptoOutputStream} which decrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * @see #decrypt(DecryptRequest)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public AwsCryptoOutputStream createDecryptingOutputStream(final CreateDecryptingOutputStreamRequest request) {
+ final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(request.cryptoMaterialsManager());
+ return new AwsCryptoOutputStream(request.outputStream(), cryptoHandler);
+ }
+
+
+ /**
+ * Returns a {@link AwsCryptoOutputStream} which decrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * This is a convenience which creates an instance of the {@link CreateDecryptingOutputStreamRequest.Builder} avoiding the need to
+ * create one manually via {@link CreateDecryptingOutputStreamRequest#builder()}
+ *
+ *
+ * @see #decrypt(DecryptRequest)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public AwsCryptoOutputStream createDecryptingOutputStream(final Consumer request) {
+ return createDecryptingOutputStream(CreateDecryptingOutputStreamRequest.builder().applyMutation(request).build());
}
/**
@@ -628,12 +910,45 @@ public CryptoOutputStream> createDecryptingStream(
*
* @see #encryptData(CryptoMaterialsManager, byte[], Map)
* @see javax.crypto.CipherInputStream
+ *
+ * @deprecated Replaced by {@link #createDecryptingInputStream(CreateDecryptingInputStreamRequest)}
*/
+ @Deprecated
public CryptoInputStream> createDecryptingStream(
final CryptoMaterialsManager materialsManager, final InputStream is
) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager);
- return new CryptoInputStream(is, cryptoHandler);
+ return createDecryptingInputStream(CreateDecryptingInputStreamRequest.builder()
+ .cryptoMaterialsManager(materialsManager)
+ .inputStream(is).build()).toCryptoInputStream();
+ }
+
+ /**
+ * Returns a {@link AwsCryptoInputStream} which decrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * @see #encrypt(EncryptRequest)
+ * @see javax.crypto.CipherInputStream
+ */
+ public AwsCryptoInputStream createDecryptingInputStream(final CreateDecryptingInputStreamRequest request) {
+ requireNonNull(request, "request is required");
+
+ final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(request.cryptoMaterialsManager());
+ return new AwsCryptoInputStream(request.inputStream(), cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link AwsCryptoInputStream} which decrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * This is a convenience which creates an instance of the {@link CreateDecryptingInputStreamRequest.Builder} avoiding the need to
+ * create one manually via {@link CreateDecryptingInputStreamRequest#builder()}
+ *
+ *
+ * @see #encrypt(EncryptRequest)
+ * @see javax.crypto.CipherInputStream
+ */
+ public AwsCryptoInputStream createDecryptingInputStream(final Consumer request) {
+ return createDecryptingInputStream(CreateDecryptingInputStreamRequest.builder().applyMutation(request).build());
}
private MessageCryptoHandler getEncryptingStreamHandler(
diff --git a/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoInputStream.java b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoInputStream.java
new file mode 100644
index 000000000..81f56bde4
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoInputStream.java
@@ -0,0 +1,146 @@
+/*
+ * 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.encryptionsdk;
+
+import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
+import com.amazonaws.encryptionsdk.internal.MessageCryptoHandler;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An AwsCryptoInputStream is a subclass of java.io.InputStream. It performs cryptographic
+ * transformation of the bytes passing through it.
+ *
+ *
+ * The AwsCryptoInputStream wraps a provided InputStream object and performs cryptographic
+ * transformation of the bytes read from the wrapped InputStream. It uses the cryptography handler
+ * provided during construction to invoke methods that perform the cryptographic transformations.
+ *
+ *
+ * In short, reading from the AwsCryptoInputStream returns bytes that are the cryptographic
+ * transformations of the bytes read from the wrapped InputStream.
+ *
+ *
+ * For example, if the cryptography handler provides methods for decryption, the AwsCryptoInputStream
+ * will read ciphertext bytes from the wrapped InputStream, decrypt, and return them as plaintext
+ * bytes.
+ *
+ *
+ * This class adheres strictly to the semantics, especially the failure semantics, of its ancestor
+ * class java.io.InputStream. This class overrides all the methods specified in its ancestor class.
+ *
+ *
+ * To instantiate an instance of this class, please see {@link AwsCrypto}.
+ *
+ */
+public class AwsCryptoInputStream extends InputStream {
+
+ private final CryptoInputStream> cryptoInputStream;
+
+ /**
+ * Constructs an AwsCryptoInputStream that wraps the provided InputStream object. It performs
+ * cryptographic transformation of the bytes read from the wrapped InputStream using the methods
+ * provided in the provided CryptoHandler implementation.
+ *
+ * @param inputStream
+ * the inputStream object to be wrapped.
+ * @param cryptoHandler
+ * the cryptoHandler implementation that provides the methods to use in performing
+ * cryptographic transformation of the bytes read from the inputStream.
+ */
+ AwsCryptoInputStream(final InputStream inputStream, final MessageCryptoHandler cryptoHandler) {
+ cryptoInputStream = new CryptoInputStream<>(inputStream, cryptoHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException
+ * This is thrown only during decryption if b contains invalid or corrupt
+ * ciphertext.
+ */
+ @Override
+ public int read(final byte[] b, final int off, final int len) throws IllegalArgumentException, IOException,
+ BadCiphertextException {
+ return cryptoInputStream.read(b, off, len);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException
+ * This is thrown only during decryption if b contains invalid or corrupt
+ * ciphertext.
+ */
+ @Override
+ public int read(final byte[] b) throws IllegalArgumentException, IOException, BadCiphertextException {
+ return cryptoInputStream.read(b);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException
+ * if b contains invalid or corrupt ciphertext. This is thrown only during
+ * decryption.
+ */
+ @Override
+ public int read() throws IOException, BadCiphertextException {
+ return cryptoInputStream.read();
+ }
+
+ @Override
+ public void close() throws IOException {
+ cryptoInputStream.close();
+ }
+
+ /**
+ * Returns metadata associated with the performed cryptographic operation.
+ */
+ @Override
+ public int available() throws IOException {
+ return cryptoInputStream.available();
+ }
+
+ /**
+ * Sets an upper bound on the size of the input data. This method should be called before reading any data from the
+ * stream. If this method is not called prior to reading any data, performance may be reduced (notably, it will not
+ * be possible to cache data keys when encrypting).
+ *
+ * Among other things, this size is used to enforce limits configured on the {@link CachingCryptoMaterialsManager}.
+ *
+ * If the input size set here is exceeded, an exception will be thrown, and the encryption or decryption will fail.
+ *
+ * @param size Maximum input size.
+ */
+ public void setMaxInputLength(long size) {
+ cryptoInputStream.setMaxInputLength(size);
+ }
+
+ /**
+ * Gets the {@link AwsCryptoResult}.
+ *
+ * @return The {@link AwsCryptoResult}
+ * @throws IOException if an input/output exception occurs while processing the result
+ */
+ public AwsCryptoResult getAwsCryptoResult() throws IOException {
+ return cryptoInputStream.getAwsCryptoResult(this);
+ }
+
+ CryptoInputStream> toCryptoInputStream() {
+ return cryptoInputStream;
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoOutputStream.java b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoOutputStream.java
new file mode 100644
index 000000000..13e5f3a2e
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoOutputStream.java
@@ -0,0 +1,153 @@
+/*
+ * 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.encryptionsdk;
+
+import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
+import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
+import com.amazonaws.encryptionsdk.internal.MessageCryptoHandler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An AwsCryptoOutputStream is a subclass of java.io.OutputStream. It performs cryptographic
+ * transformation of the bytes passing through it.
+ *
+ *
+ * The AwsCryptoOutputStream wraps a provided OutputStream object and performs cryptographic
+ * transformation of the bytes written to it. The transformed bytes are then written to the wrapped
+ * OutputStream. It uses the cryptography handler provided during construction to invoke methods
+ * that perform the cryptographic transformations.
+ *
+ *
+ * In short, writing to the AwsCryptoOutputStream results in those bytes being cryptographically
+ * transformed and written to the wrapped OutputStream.
+ *
+ *
+ * For example, if the crypto handler provides methods for decryption, the AwsCryptoOutputStream will
+ * decrypt the provided ciphertext bytes and write the plaintext bytes to the wrapped OutputStream.
+ *
+ *
+ * This class adheres strictly to the semantics, especially the failure semantics, of its ancestor
+ * class java.io.OutputStream. This class overrides all the methods specified in its ancestor class.
+ *
+ *
+ * To instantiate an instance of this class, please see {@link AwsCrypto}.
+ *
+ */
+public class AwsCryptoOutputStream extends OutputStream {
+
+ private final CryptoOutputStream> cryptoOutputStream;
+
+ /**
+ * Constructs an AwsCryptoOutputStream that wraps the provided OutputStream object. It performs
+ * cryptographic transformation of the bytes written to it using the methods provided in the
+ * provided CryptoHandler implementation. The transformed bytes are then written to the wrapped
+ * OutputStream.
+ *
+ * @param outputStream
+ * the outputStream object to be wrapped.
+ * @param cryptoHandler
+ * the cryptoHandler implementation that provides the methods to use in performing
+ * cryptographic transformation of the bytes written to this stream.
+ */
+ AwsCryptoOutputStream(final OutputStream outputStream, final MessageCryptoHandler cryptoHandler) {
+ cryptoOutputStream = new CryptoOutputStream<>(outputStream, cryptoHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException
+ * This is thrown only during decryption if b contains invalid or corrupt
+ * ciphertext.
+ */
+ @Override
+ public void write(final byte[] b) throws IllegalArgumentException, IOException, BadCiphertextException {
+ cryptoOutputStream.write(b);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException
+ * This is thrown only during decryption if b contains invalid or corrupt
+ * ciphertext.
+ */
+ @Override
+ public void write(final byte[] b, final int off, final int len) throws IllegalArgumentException, IOException,
+ BadCiphertextException {
+ cryptoOutputStream.write(b, off, len);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException
+ * This is thrown only during decryption if b contains invalid or corrupt
+ * ciphertext.
+ */
+ @Override
+ public void write(int b) throws IOException, BadCiphertextException {
+ cryptoOutputStream.write(b);
+ }
+
+ /**
+ * Closes this output stream and releases any system resources associated
+ * with this stream.
+ *
+ *
+ * This method writes any final bytes to the underlying stream that complete
+ * the cryptographic transformation of the written bytes. It also calls close
+ * on the wrapped OutputStream.
+ *
+ * @throws IOException
+ * if an I/O error occurs.
+ * @throws BadCiphertextException
+ * This is thrown only during decryption if b contains invalid
+ * or corrupt ciphertext.
+ */
+ @Override
+ public void close() throws IOException, BadCiphertextException {
+ cryptoOutputStream.close();
+ }
+
+ /**
+ * Sets an upper bound on the size of the input data. This method should be called before writing any data to the
+ * stream. If this method is not called prior to writing data, performance may be reduced (notably, it will not
+ * be possible to cache data keys when encrypting).
+ *
+ * Among other things, this size is used to enforce limits configured on the {@link CachingCryptoMaterialsManager}.
+ *
+ * If the size set here is exceeded, an exception will be thrown, and the encryption or decryption will fail.
+ *
+ * @param size Maximum input size.
+ */
+ public void setMaxInputLength(long size) {
+ cryptoOutputStream.setMaxInputLength(size);
+ }
+
+ /**
+ * Gets the {@link AwsCryptoResult}.
+ *
+ * @return The {@link AwsCryptoResult}
+ */
+ public AwsCryptoResult getAwsCryptoResult() {
+ return cryptoOutputStream.getAwsCryptoResult(this);
+ }
+
+ CryptoOutputStream> toCryptoOutputStream() {
+ return cryptoOutputStream;
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoRequest.java b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoRequest.java
new file mode 100644
index 000000000..658c044ad
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoRequest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.encryptionsdk;
+
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+
+import java.util.function.Consumer;
+
+import static java.util.Objects.requireNonNull;
+import static org.apache.commons.lang3.Validate.isTrue;
+
+class AwsCryptoRequest {
+ private final CryptoMaterialsManager cryptoMaterialsManager;
+
+ AwsCryptoRequest(Builder builder) {
+ isTrue(builder.cryptoMaterialsManager != null || builder.keyring != null,
+ "Either a cryptoMaterialsManager or keyring is required");
+ isTrue(builder.cryptoMaterialsManager == null || builder.keyring == null,
+ "Only one of cryptoMaterialsManager or keyring may be specified");
+
+ this.cryptoMaterialsManager = builder.cryptoMaterialsManager == null ?
+ new DefaultCryptoMaterialsManager(builder.keyring) : builder.cryptoMaterialsManager;
+ }
+
+ public CryptoMaterialsManager cryptoMaterialsManager() {
+ return cryptoMaterialsManager;
+ }
+
+ abstract static class Builder> {
+
+ private CryptoMaterialsManager cryptoMaterialsManager;
+ private Keyring keyring;
+
+ /**
+ * Sets the {@link CryptoMaterialsManager}. Either a {@link CryptoMaterialsManager} or a
+ * {@link Keyring} is required.
+ *
+ * @param cryptoMaterialsManager The {@link CryptoMaterialsManager}
+ * @return The Builder, for method chaining
+ */
+ public T cryptoMaterialsManager(CryptoMaterialsManager cryptoMaterialsManager) {
+ requireNonNull(cryptoMaterialsManager, "cryptoMaterialsManager is required");
+ this.cryptoMaterialsManager = cryptoMaterialsManager;
+ return getThis();
+ }
+
+ /**
+ * Sets the {@link Keyring}. Either a {@link CryptoMaterialsManager} or a
+ * {@link Keyring} is required.
+ *
+ * @param keyring The {@link Keyring}
+ * @return The Builder, for method chaining
+ */
+ public T keyring(Keyring keyring) {
+ requireNonNull(keyring, "keyring is required");
+ this.keyring = keyring;
+ return getThis();
+ }
+
+ abstract T getThis();
+
+ T applyMutation(Consumer mutator) {
+ mutator.accept(getThis());
+ return getThis();
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoResult.java b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoResult.java
new file mode 100644
index 000000000..6bd38b78f
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/AwsCryptoResult.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.encryptionsdk;
+
+import com.amazonaws.encryptionsdk.keyrings.KeyringTrace;
+import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents the result of an operation by {@link AwsCrypto}. It not only captures the
+ * {@code result} of the operation but also additional metadata such as the
+ * {@code encryptionContext}, {@code algorithm}, {@link KeyringTrace}, and any other information
+ * captured in the {@link CiphertextHeaders}.
+ *
+ * @param
+ * the type of the underlying {@code result}
+ */
+public class AwsCryptoResult {
+ private final T result;
+ private final KeyringTrace keyringTrace;
+ private final List masterKeys;
+ private final Map encryptionContext;
+ private final CiphertextHeaders headers;
+
+ /**
+ * Note, does not make a defensive copy of any of the data.
+ */
+ AwsCryptoResult(final T result, final KeyringTrace keyringTrace, final List extends MasterKey> masterKeys, final CiphertextHeaders headers) {
+ this.result = result;
+ this.keyringTrace = keyringTrace;
+ this.masterKeys = Collections.unmodifiableList(masterKeys);
+ this.headers = headers;
+ encryptionContext = this.headers.getEncryptionContextMap();
+ }
+
+ /**
+ * The actual result of the cryptographic operation. This is not a defensive copy and callers
+ * should not modify it.
+ *
+ * @return The result
+ */
+ public T getResult() {
+ return result;
+ }
+
+ /**
+ * The {@link KeyringTrace} containing all of the actions that keyrings have taken.
+ *
+ * @return The {@link KeyringTrace}
+ */
+ public KeyringTrace getKeyringTrace() {
+ return keyringTrace;
+ }
+
+ /**
+ * The encryption context.
+ *
+ * @return The encryption context
+ */
+ public Map getEncryptionContext() {
+ return encryptionContext;
+ }
+
+ /**
+ * Convenience method equivalent to {@link #getHeaders()}{@code .getCryptoAlgoId()}.
+ */
+ public CryptoAlgorithm getAlgorithmSuite() {
+ return headers.getCryptoAlgoId();
+ }
+
+ /**
+ * The Ciphertext Headers.
+ *
+ * @return The CiphertextHeaders
+ */
+ public CiphertextHeaders getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Package-private method for converting to a legacy CryptoResult.
+ */
+ CryptoResult toCryptoResult() {
+ return new CryptoResult<>(result, masterKeys, headers);
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CreateDecryptingInputStreamRequest.java b/src/main/java/com/amazonaws/encryptionsdk/CreateDecryptingInputStreamRequest.java
new file mode 100644
index 000000000..989612f0b
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/CreateDecryptingInputStreamRequest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.encryptionsdk;
+
+import java.io.InputStream;
+
+import static java.util.Objects.requireNonNull;
+
+public class CreateDecryptingInputStreamRequest extends AwsCryptoRequest {
+
+ private final InputStream inputStream;
+
+ private CreateDecryptingInputStreamRequest(Builder builder) {
+ super(builder);
+
+ requireNonNull(builder.inputStream, "inputStream is required");
+ this.inputStream = builder.inputStream;
+ }
+
+ /**
+ * The {@link InputStream} to be read from.
+ *
+ * @return The {@link InputStream} to be read from.
+ */
+ public InputStream inputStream() {
+ return this.inputStream;
+ }
+
+ /**
+ * A builder for constructing an instance of {@code CreateDecryptingInputStreamRequest}.
+ *
+ * @return A builder for constructing an instance of {@code CreateDecryptingInputStreamRequest}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private InputStream inputStream;
+
+ /**
+ * Sets the {@link InputStream}
+ *
+ * @param inputStream The {@link InputStream}
+ * @return The Builder, for method chaining
+ */
+ public Builder inputStream(InputStream inputStream) {
+ requireNonNull(inputStream, "inputStream is required");
+ this.inputStream = inputStream;
+ return this;
+ }
+
+ /**
+ * Constructs the CreateEncryptingInputStreamRequest instance.
+ *
+ * @return The CreateEncryptingInputStreamRequest instance
+ */
+ public CreateDecryptingInputStreamRequest build() {
+ return new CreateDecryptingInputStreamRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CreateDecryptingOutputStreamRequest.java b/src/main/java/com/amazonaws/encryptionsdk/CreateDecryptingOutputStreamRequest.java
new file mode 100644
index 000000000..372e9c1b9
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/CreateDecryptingOutputStreamRequest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.encryptionsdk;
+
+import java.io.OutputStream;
+
+import static java.util.Objects.requireNonNull;
+
+public class CreateDecryptingOutputStreamRequest extends AwsCryptoRequest {
+
+ private final OutputStream outputStream;
+
+ private CreateDecryptingOutputStreamRequest(Builder builder) {
+ super(builder);
+
+ requireNonNull(builder.outputStream, "outputStream is required");
+ this.outputStream = builder.outputStream;
+ }
+
+ /**
+ * The {@link OutputStream} to be read from.
+ *
+ * @return The {@link OutputStream} to be read from.
+ */
+ public OutputStream outputStream() {
+ return this.outputStream;
+ }
+
+ /**
+ * A builder for constructing an instance of {@code CreateDecryptingOutputStreamRequest}.
+ *
+ * @return A builder for constructing an instance of {@code CreateDecryptingOutputStreamRequest}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private OutputStream outputStream;
+
+ /**
+ * Sets the {@link OutputStream}
+ *
+ * @param outputStream The {@link OutputStream}
+ * @return The Builder, for method chaining
+ */
+ public Builder outputStream(OutputStream outputStream) {
+ requireNonNull(outputStream, "outputStream is required");
+ this.outputStream = outputStream;
+ return this;
+ }
+
+ /**
+ * Constructs the CreateDecryptingOutputStreamRequest instance.
+ *
+ * @return The CreateDecryptingOutputStreamRequest instance
+ */
+ public CreateDecryptingOutputStreamRequest build() {
+ return new CreateDecryptingOutputStreamRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CreateEncryptingInputStreamRequest.java b/src/main/java/com/amazonaws/encryptionsdk/CreateEncryptingInputStreamRequest.java
new file mode 100644
index 000000000..98926977b
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/CreateEncryptingInputStreamRequest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.encryptionsdk;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+public class CreateEncryptingInputStreamRequest extends AwsCryptoRequest {
+
+ private final InputStream inputStream;
+ private final Map encryptionContext;
+
+ private CreateEncryptingInputStreamRequest(Builder builder) {
+ super(builder);
+
+ requireNonNull(builder.inputStream, "inputStream is required");
+ requireNonNull(builder.encryptionContext, "encryptionContext is required");
+ this.inputStream = builder.inputStream;
+ this.encryptionContext = builder.encryptionContext;
+ }
+
+ /**
+ * The {@link InputStream} to be read from.
+ *
+ * @return The {@link InputStream} to be read from.
+ */
+ public InputStream inputStream() {
+ return this.inputStream;
+ }
+
+ /**
+ * The encryption context associated with this encryption.
+ *
+ * @return The encryption context associated with this encryption.
+ */
+ public Map encryptionContext() {
+ return this.encryptionContext;
+ }
+
+ /**
+ * A builder for constructing an instance of {@code CreateEncryptingInputStreamRequest}.
+ *
+ * @return A builder for constructing an instance of {@code CreateEncryptingInputStreamRequest}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private InputStream inputStream;
+ private Map encryptionContext = Collections.emptyMap();
+
+ /**
+ * Sets the {@link InputStream}
+ *
+ * @param inputStream The {@link InputStream}
+ * @return The Builder, for method chaining
+ */
+ public Builder inputStream(InputStream inputStream) {
+ requireNonNull(inputStream, "inputStream is required");
+ this.inputStream = inputStream;
+ return this;
+ }
+
+ /**
+ * Sets the (optional) encryption context map.
+ *
+ * @param encryptionContext The encryption context
+ * @return The Builder, for method chaining
+ */
+ public Builder encryptionContext(Map encryptionContext) {
+ requireNonNull(encryptionContext, "encryptionContext is required");
+ this.encryptionContext = Collections.unmodifiableMap(new HashMap<>(encryptionContext));
+ return this;
+ }
+
+ /**
+ * Constructs the CreateEncryptingInputStreamRequest instance.
+ *
+ * @return The CreateEncryptingInputStreamRequest instance
+ */
+ public CreateEncryptingInputStreamRequest build() {
+ return new CreateEncryptingInputStreamRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CreateEncryptingOutputStreamRequest.java b/src/main/java/com/amazonaws/encryptionsdk/CreateEncryptingOutputStreamRequest.java
new file mode 100644
index 000000000..bb76b78a8
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/CreateEncryptingOutputStreamRequest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.encryptionsdk;
+
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+public class CreateEncryptingOutputStreamRequest extends AwsCryptoRequest {
+
+ private final OutputStream outputStream;
+ private final Map encryptionContext;
+
+ private CreateEncryptingOutputStreamRequest(Builder builder) {
+ super(builder);
+
+ requireNonNull(builder.outputStream, "outputStream is required");
+ requireNonNull(builder.encryptionContext, "encryptionContext is required");
+ this.outputStream = builder.outputStream;
+ this.encryptionContext = builder.encryptionContext;
+ }
+
+ /**
+ * The {@link OutputStream} to be read from.
+ *
+ * @return The {@link OutputStream} to be read from.
+ */
+ public OutputStream outputStream() {
+ return this.outputStream;
+ }
+
+ /**
+ * The encryption context associated with this encryption.
+ *
+ * @return The encryption context associated with this encryption.
+ */
+ public Map encryptionContext() {
+ return this.encryptionContext;
+ }
+
+ /**
+ * A builder for constructing an instance of {@code CreateEncryptingOutputStreamRequest}.
+ *
+ * @return A builder for constructing an instance of {@code CreateEncryptingOutputStreamRequest}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private OutputStream outputStream;
+ private Map encryptionContext = Collections.emptyMap();
+
+ /**
+ * Sets the {@link OutputStream}
+ *
+ * @param outputStream The {@link OutputStream}
+ * @return The Builder, for method chaining
+ */
+ public Builder outputStream(OutputStream outputStream) {
+ requireNonNull(outputStream, "outputStream is required");
+ this.outputStream = outputStream;
+ return this;
+ }
+
+ /**
+ * Sets the (optional) encryption context map.
+ *
+ * @param encryptionContext The encryption context
+ * @return The Builder, for method chaining
+ */
+ public Builder encryptionContext(Map encryptionContext) {
+ requireNonNull(encryptionContext, "encryptionContext is required");
+ this.encryptionContext = Collections.unmodifiableMap(new HashMap<>(encryptionContext));
+ return this;
+ }
+
+ /**
+ * Constructs the CreateEncryptingOutputStreamRequest instance.
+ *
+ * @return The CreateEncryptingOutputStreamRequest instance
+ */
+ public CreateEncryptingOutputStreamRequest build() {
+ return new CreateEncryptingOutputStreamRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java
index 935e4fbc0..ee1588deb 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java
@@ -51,7 +51,10 @@
*
* @param
* The type of {@link MasterKey}s used to manipulate the data.
+ *
+ * @deprecated Replaced by {@link AwsCryptoInputStream}
*/
+@Deprecated
public class CryptoInputStream> extends InputStream {
private static final int MAX_READ_LEN = 4096;
@@ -254,4 +257,18 @@ public CryptoResult, K> getCryptoResult() throws BadCiphert
(List) cryptoHandler_.getMasterKeys(),
cryptoHandler_.getHeaders());
}
+
+ AwsCryptoResult getAwsCryptoResult(AwsCryptoInputStream awsCryptoInputStream) throws IOException {
+ while (!cryptoHandler_.getHeaders().isComplete()) {
+ if (fillOutBytes() == -1) {
+ throw new BadCiphertextException("No CiphertextHeaders found.");
+ }
+ }
+
+ return new AwsCryptoResult<>(
+ awsCryptoInputStream,
+ cryptoHandler_.getKeyringTrace(),
+ cryptoHandler_.getMasterKeys(),
+ cryptoHandler_.getHeaders());
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java
index de17f4a2f..6a4cda59e 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java
@@ -49,7 +49,10 @@
*
* @param
* The type of {@link MasterKey}s used to manipulate the data.
+ *
+ * @deprecated Replaced by {@link AwsCryptoOutputStream}
*/
+@Deprecated
public class CryptoOutputStream> extends OutputStream {
private final OutputStream outputStream_;
@@ -181,4 +184,16 @@ public CryptoResult, K> getCryptoResult() {
(List) cryptoHandler_.getMasterKeys(),
cryptoHandler_.getHeaders());
}
-}
\ No newline at end of file
+
+ AwsCryptoResult getAwsCryptoResult(AwsCryptoOutputStream awsCryptoOutputStream) {
+ if (!cryptoHandler_.getHeaders().isComplete()) {
+ throw new IllegalStateException("Ciphertext headers not yet written to stream");
+ }
+
+ return new AwsCryptoResult<>(
+ awsCryptoOutputStream,
+ cryptoHandler_.getKeyringTrace(),
+ cryptoHandler_.getMasterKeys(),
+ cryptoHandler_.getHeaders());
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java
index 616ed3756..a550b4687 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java
@@ -30,7 +30,10 @@
* the type of the underlying {@code result}
* @param
* the type of the {@link MasterKey}s used in production of this result
+ *
+ * @deprecated Replaced by {@link AwsCryptoResult}
*/
+@Deprecated
public class CryptoResult> {
private final T result_;
private final List masterKeys_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/DataKey.java b/src/main/java/com/amazonaws/encryptionsdk/DataKey.java
index 21d174f00..a07287447 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/DataKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/DataKey.java
@@ -20,7 +20,10 @@
*
* @param
* the type of {@link MasterKey} used to protect this {@code DataKey}.
+ * @deprecated This type depends on the deprecated {@link MasterKey} type. Use {@link SecretKey} or
+ * {@link EncryptedDataKey} instead.
*/
+@Deprecated
public class DataKey> implements EncryptedDataKey {
private final byte[] providerInformation_;
private final byte[] encryptedDataKey_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/DecryptRequest.java b/src/main/java/com/amazonaws/encryptionsdk/DecryptRequest.java
new file mode 100644
index 000000000..075df54cd
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/DecryptRequest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.encryptionsdk;
+
+import static java.util.Objects.requireNonNull;
+import static org.apache.commons.lang3.Validate.isTrue;
+
+public class DecryptRequest extends AwsCryptoRequest {
+
+ private final ParsedCiphertext parsedCiphertext;
+
+ private DecryptRequest(Builder builder) {
+ super(builder);
+
+ isTrue(builder.ciphertext != null || builder.parsedCiphertext != null,
+ "Either ciphertext or parsedCiphertext is required");
+ isTrue(builder.ciphertext == null || builder.parsedCiphertext == null,
+ "Only one of ciphertext or parsedCiphertext may be specified");
+
+ this.parsedCiphertext = builder.parsedCiphertext == null ?
+ new ParsedCiphertext(builder.ciphertext) : builder.parsedCiphertext;
+ }
+
+ public ParsedCiphertext parsedCiphertext() {
+ return this.parsedCiphertext;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private ParsedCiphertext parsedCiphertext;
+ private byte[] ciphertext;
+
+ /**
+ * Sets the {@link ParsedCiphertext} to decrypt. Either {@link ParsedCiphertext} or a
+ * {@code byte[]} ciphertext is required.
+ *
+ * @param parsedCiphertext The {@link ParsedCiphertext}
+ * @return The Builder, for method chaining
+ */
+ public Builder parsedCiphertext(ParsedCiphertext parsedCiphertext) {
+ requireNonNull(parsedCiphertext, "parsedCiphertext is required");
+ this.parsedCiphertext = parsedCiphertext;
+ return this;
+ }
+
+ /**
+ * Sets the ciphertext byte array to decrypt. Either {@link ParsedCiphertext} or a
+ * {@code byte[]} ciphertext is required. Note that this does not make a defensive
+ * copy of the ciphertext and so any modifications made to the backing array will be
+ * reflected in this Builder.
+ *
+ * @param ciphertext The ciphertext
+ * @return The Builder, for method chaining
+ */
+ public Builder ciphertext(byte[] ciphertext) {
+ requireNonNull(ciphertext, "ciphertext is required");
+ this.ciphertext = ciphertext;
+ return this;
+ }
+
+ /**
+ * Constructs the DecryptRequest instance.
+ *
+ * @return The DecryptRequest instance
+ */
+ public DecryptRequest build() {
+ return new DecryptRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java b/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java
index d31c615b5..781d193f5 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java
@@ -1,9 +1,11 @@
package com.amazonaws.encryptionsdk;
-import static com.amazonaws.encryptionsdk.internal.Utils.assertNonNull;
+import static com.amazonaws.encryptionsdk.AwsCrypto.getDefaultCryptoAlgorithm;
+import static java.util.Objects.requireNonNull;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
+import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
@@ -13,61 +15,91 @@
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
import com.amazonaws.encryptionsdk.internal.Constants;
-import com.amazonaws.encryptionsdk.internal.Utils;
import com.amazonaws.encryptionsdk.internal.TrailingSignatureAlgorithm;
-import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
+import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.KeyBlob;
-import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
/**
* The default implementation of {@link CryptoMaterialsManager}, used implicitly when passing a
- * {@link MasterKeyProvider} to methods in {@link AwsCrypto}.
+ * {@link MasterKeyProvider} or {@link Keyring} to methods in {@link AwsCrypto}.
*
- * This default implementation delegates to a specific {@link MasterKeyProvider} specified at construction time. It also
- * handles generating trailing signature keys when needed, placing them in the encryption context (and extracting them
- * at decrypt time).
+ * This default implementation delegates to a specific {@link MasterKeyProvider} or {@link Keyring} specified at
+ * construction time. It also handles generating trailing signature keys when needed, placing them in the
+ * encryption context (and extracting them at decrypt time).
*/
public class DefaultCryptoMaterialsManager implements CryptoMaterialsManager {
+ // Exactly one of keyring or mkp should be null
+ private final Keyring keyring;
private final MasterKeyProvider> mkp;
/**
- * @param mkp The master key provider to delegate to
+ * @param masterKeyProvider The master key provider to delegate to
+ *
+ * @deprecated Replaced by {@link #DefaultCryptoMaterialsManager(Keyring)}
*/
- public DefaultCryptoMaterialsManager(MasterKeyProvider> mkp) {
- Utils.assertNonNull(mkp, "mkp");
- this.mkp = mkp;
+ @Deprecated
+ public DefaultCryptoMaterialsManager(MasterKeyProvider> masterKeyProvider) {
+ requireNonNull(masterKeyProvider, "masterKeyProvider is required");
+ this.mkp = masterKeyProvider;
+ this.keyring = null;
}
- @Override public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
- Map context = request.getContext();
+ /**
+ * @param keyring The keyring to delegate to
+ */
+ public DefaultCryptoMaterialsManager(Keyring keyring) {
+ requireNonNull(keyring, "keyring is required");
+ this.keyring = keyring;
+ this.mkp = null;
+ }
- CryptoAlgorithm algo = request.getRequestedAlgorithm();
- if (algo == null) {
- algo = AwsCrypto.getDefaultCryptoAlgorithm();
+ @Override
+ public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
+ if(keyring != null) {
+ return getEncryptionMaterialsForKeyring(request);
}
- KeyPair trailingKeys = null;
- if (algo.getTrailingSignatureLength() > 0) {
- try {
- trailingKeys = generateTrailingSigKeyPair(algo);
- if (context.containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
- throw new IllegalArgumentException("EncryptionContext contains reserved field "
- + Constants.EC_PUBLIC_KEY_FIELD);
- }
- // make mutable
- context = new HashMap<>(context);
- context.put(Constants.EC_PUBLIC_KEY_FIELD, serializeTrailingKeyForEc(algo, trailingKeys));
- } catch (final GeneralSecurityException ex) {
- throw new AwsCryptoException(ex);
- }
+ return getEncryptionMaterialsForMasterKeyProvider(request);
+ }
+
+ @Override
+ public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
+ if(keyring != null) {
+ return getDecryptionMaterialsForKeyring(request);
}
- final MasterKeyRequest.Builder mkRequestBuilder = MasterKeyRequest.newBuilder();
- mkRequestBuilder.setEncryptionContext(context);
+ return getDecryptionMaterialsForMasterKeyProvider(request);
+ }
+
+ private EncryptionMaterials getEncryptionMaterialsForKeyring(EncryptionMaterialsRequest request) {
+ final CryptoAlgorithm algorithmSuite = request.getRequestedAlgorithm() != null ?
+ request.getRequestedAlgorithm() : getDefaultCryptoAlgorithm();
+ final Map encryptionContext = new HashMap<>(request.getContext());
+ final PrivateKey signingKey = getSigningKey(algorithmSuite, encryptionContext);
+
+ final EncryptionMaterials encryptionMaterials =
+ EncryptionMaterials.newBuilder()
+ .setAlgorithm(algorithmSuite)
+ .setEncryptionContext(encryptionContext)
+ .setTrailingSignatureKey(signingKey)
+ .build();
- mkRequestBuilder.setStreaming(request.getPlaintextSize() == -1);
+ return keyring.onEncrypt(encryptionMaterials);
+ }
+
+ private EncryptionMaterials getEncryptionMaterialsForMasterKeyProvider(EncryptionMaterialsRequest request) {
+ final Map encryptionContext = new HashMap<>(request.getContext());
+ final CryptoAlgorithm algorithmSuite = request.getRequestedAlgorithm() != null ?
+ request.getRequestedAlgorithm() : getDefaultCryptoAlgorithm();
+ final PrivateKey trailingSignatureKey = getSigningKey(algorithmSuite, encryptionContext);
+
+ final MasterKeyRequest.Builder mkRequestBuilder = MasterKeyRequest.newBuilder()
+ .setEncryptionContext(encryptionContext)
+ .setStreaming(request.getPlaintextSize() == -1);
if (request.getPlaintext() != null) {
mkRequestBuilder.setPlaintext(request.getPlaintext());
} else {
@@ -75,69 +107,102 @@ public DefaultCryptoMaterialsManager(MasterKeyProvider> mkp) {
}
@SuppressWarnings("unchecked")
- final List mks
- = (List)assertNonNull(mkp, "provider")
- .getMasterKeysForEncryption(mkRequestBuilder.build());
+ final List masterKeys = (List) mkp.getMasterKeysForEncryption(mkRequestBuilder.build());
- if (mks.isEmpty()) {
+ if (masterKeys.isEmpty()) {
throw new IllegalArgumentException("No master keys provided");
}
- DataKey> dataKey = mks.get(0).generateDataKey(algo, context);
+ final DataKey> dataKey = masterKeys.get(0).generateDataKey(algorithmSuite, encryptionContext);
- List keyBlobs = new ArrayList<>(mks.size());
+ final List keyBlobs = new ArrayList<>(masterKeys.size());
keyBlobs.add(new KeyBlob(dataKey));
- for (int i = 1; i < mks.size(); i++) {
+ for (int i = 1; i < masterKeys.size(); i++) {
//noinspection unchecked
- keyBlobs.add(new KeyBlob(mks.get(i).encryptDataKey(algo, context, dataKey)));
+ keyBlobs.add(new KeyBlob(masterKeys.get(i).encryptDataKey(algorithmSuite, encryptionContext, dataKey)));
}
- //noinspection unchecked
return EncryptionMaterials.newBuilder()
- .setAlgorithm(algo)
- .setCleartextDataKey(dataKey.getKey())
- .setEncryptedDataKeys(keyBlobs)
- .setEncryptionContext(context)
- .setTrailingSignatureKey(trailingKeys == null ? null : trailingKeys.getPrivate())
- .setMasterKeys(mks)
- .build();
+ .setAlgorithm(algorithmSuite)
+ .setCleartextDataKey(dataKey.getKey())
+ .setEncryptedDataKeys(keyBlobs)
+ .setEncryptionContext(encryptionContext)
+ .setTrailingSignatureKey(trailingSignatureKey)
+ .setMasterKeys(masterKeys)
+ .build();
}
- @Override public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
- DataKey> dataKey = mkp.decryptDataKey(
+ private DecryptionMaterials getDecryptionMaterialsForKeyring(DecryptionMaterialsRequest request) {
+ final PublicKey verificationKey = getVerificationKey(request);
+
+ final DecryptionMaterials decryptionMaterials =
+ DecryptionMaterials.newBuilder()
+ .setAlgorithm(request.getAlgorithm())
+ .setEncryptionContext(request.getEncryptionContext())
+ .setTrailingSignatureKey(verificationKey)
+ .build();
+
+ final DecryptionMaterials result = keyring.onDecrypt(decryptionMaterials, request.getEncryptedDataKeys());
+
+ if(!result.hasCleartextDataKey()) {
+ throw new CannotUnwrapDataKeyException("Could not decrypt any data keys");
+ }
+
+ return result;
+ }
+
+ private DecryptionMaterials getDecryptionMaterialsForMasterKeyProvider(DecryptionMaterialsRequest request) {
+ final DataKey> dataKey = mkp.decryptDataKey(
request.getAlgorithm(),
request.getEncryptedDataKeys(),
- request.getEncryptionContext()
- );
+ request.getEncryptionContext());
if (dataKey == null) {
throw new CannotUnwrapDataKeyException("Could not decrypt any data keys");
}
- PublicKey pubKey = null;
+ return DecryptionMaterials.newBuilder()
+ .setDataKey(dataKey)
+ .setTrailingSignatureKey(getVerificationKey(request))
+ .build();
+ }
+
+ private PrivateKey getSigningKey(CryptoAlgorithm algorithmSuite, Map encryptionContext) {
+ if (algorithmSuite.getTrailingSignatureLength() > 0) {
+ try {
+ final KeyPair trailingKeys = generateTrailingSigKeyPair(algorithmSuite);
+ if (encryptionContext.containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
+ throw new IllegalArgumentException("EncryptionContext contains reserved field "
+ + Constants.EC_PUBLIC_KEY_FIELD);
+ }
+ encryptionContext.put(Constants.EC_PUBLIC_KEY_FIELD, serializeTrailingKeyForEc(algorithmSuite, trailingKeys));
+ return trailingKeys.getPrivate();
+ } catch (final GeneralSecurityException ex) {
+ throw new AwsCryptoException(ex);
+ }
+ }
+
+ return null;
+ }
+
+ private PublicKey getVerificationKey(DecryptionMaterialsRequest request) {
if (request.getAlgorithm().getTrailingSignatureLength() > 0) {
try {
- String serializedPubKey = request.getEncryptionContext().get(Constants.EC_PUBLIC_KEY_FIELD);
+ final String serializedPubKey = request.getEncryptionContext().get(Constants.EC_PUBLIC_KEY_FIELD);
if (serializedPubKey == null) {
throw new AwsCryptoException("Missing trailing signature public key");
}
- pubKey = deserializeTrailingKeyFromEc(request.getAlgorithm(), serializedPubKey);
+ return TrailingSignatureAlgorithm.forCryptoAlgorithm(
+ request.getAlgorithm()).deserializePublicKey(serializedPubKey);
} catch (final IllegalStateException ex) {
throw new AwsCryptoException(ex);
}
}
- return DecryptionMaterials.newBuilder()
- .setDataKey(dataKey)
- .setTrailingSignatureKey(pubKey)
- .build();
- }
-
- private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey) {
- return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).deserializePublicKey(pubKey);
+ return null;
}
private static String serializeTrailingKeyForEc(CryptoAlgorithm algo, KeyPair trailingKeys) {
diff --git a/src/main/java/com/amazonaws/encryptionsdk/EncryptRequest.java b/src/main/java/com/amazonaws/encryptionsdk/EncryptRequest.java
new file mode 100644
index 000000000..60890d802
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/EncryptRequest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.encryptionsdk;
+
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+public class EncryptRequest extends AwsCryptoRequest {
+
+ private final byte[] plaintext;
+ private final Map encryptionContext;
+
+ private EncryptRequest(Builder builder) {
+ super(builder);
+
+ requireNonNull(builder.plaintext, "plaintext is required");
+ requireNonNull(builder.encryptionContext, "encryptionContext is required");
+ this.plaintext = builder.plaintext;
+ this.encryptionContext = builder.encryptionContext;
+ }
+
+ public byte[] plaintext() {
+ return this.plaintext;
+ }
+
+ public Map encryptionContext() {
+ return this.encryptionContext;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private byte[] plaintext;
+ private Map encryptionContext = Collections.emptyMap();
+
+ /**
+ * Sets the plaintext byte array to encrypt. Note that this does not make a defensive copy of the
+ * plaintext and so any modifications made to the backing array will be reflected in this Builder.
+ *
+ * @param plaintext The {@link Keyring}
+ * @return The Builder, for method chaining
+ */
+ public Builder plaintext(byte[] plaintext) {
+ requireNonNull(plaintext, "plaintext is required");
+ this.plaintext = plaintext;
+ return this;
+ }
+
+ /**
+ * Sets the (optional) encryption context map.
+ *
+ * @param encryptionContext The encryption context
+ * @return The Builder, for method chaining
+ */
+ public Builder encryptionContext(Map encryptionContext) {
+ requireNonNull(encryptionContext, "encryptionContext is required");
+ this.encryptionContext = Collections.unmodifiableMap(new HashMap<>(encryptionContext));
+ return this;
+ }
+
+ /**
+ * Constructs the EncryptRequest instance.
+ *
+ * @return The EncryptRequest instance
+ */
+ public EncryptRequest build() {
+ return new EncryptRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/EstimateCiphertextSizeRequest.java b/src/main/java/com/amazonaws/encryptionsdk/EstimateCiphertextSizeRequest.java
new file mode 100644
index 000000000..56b67380d
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/EstimateCiphertextSizeRequest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.encryptionsdk;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+public class EstimateCiphertextSizeRequest extends AwsCryptoRequest {
+
+ private final int plaintextSize;
+ private final Map encryptionContext;
+
+ private EstimateCiphertextSizeRequest(Builder builder) {
+ super(builder);
+ requireNonNull(builder.encryptionContext, "encryptionContext is required");
+
+ this.plaintextSize = builder.plaintextSize;
+ this.encryptionContext = builder.encryptionContext;
+ }
+
+ public int plaintextSize() {
+ return plaintextSize;
+ }
+
+ public Map encryptionContext() {
+ return encryptionContext;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends AwsCryptoRequest.Builder {
+
+ private int plaintextSize;
+ private Map encryptionContext = Collections.emptyMap();
+
+ /**
+ * Sets the plaintextSize.
+ *
+ * @param plaintextSize The plaintext size
+ * @return The Builder, for method chaining
+ */
+ public Builder plaintextSize(int plaintextSize) {
+ this.plaintextSize = plaintextSize;
+ return this;
+ }
+
+ /**
+ * Sets the (optional) encryption context map.
+ *
+ * @param encryptionContext The encryption context
+ * @return The Builder, for method chaining
+ */
+ public Builder encryptionContext(Map encryptionContext) {
+ requireNonNull(encryptionContext, "encryptionContext is required");
+ this.encryptionContext = Collections.unmodifiableMap(new HashMap<>(encryptionContext));
+ return this;
+ }
+
+ /**
+ * Constructs the EstimateCiphertextSizeRequest instance.
+ *
+ * @return The EstimateCiphertextSizeRequest instance
+ */
+ public EstimateCiphertextSizeRequest build() {
+ return new EstimateCiphertextSizeRequest(this);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java b/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java
index ae64e752e..9a247a463 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java
@@ -20,6 +20,7 @@
import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
/**
* Represents the cryptographic key used to protect the {@link DataKey} (which, in turn, protects
@@ -31,7 +32,10 @@
*
* @param
* the concrete type of the {@link MasterKey}
+ *
+ * @deprecated Replaced by {@link Keyring}
*/
+@Deprecated
public abstract class MasterKey> extends MasterKeyProvider {
public abstract String getProviderId();
diff --git a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java
index 978586f2e..216841d4e 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java
@@ -22,6 +22,7 @@
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
/**
* Represents the logic necessary to select and construct {@link MasterKey}s for encrypting and
@@ -29,7 +30,10 @@
*
* @param
* the type of {@link MasterKey} returned by this provider
+ *
+ * @deprecated Replaced by {@link Keyring}
*/
+@Deprecated
public abstract class MasterKeyProvider> {
/**
* ProviderId used by this instance when no other is specified.
diff --git a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java
index 650fd2ee4..6dffe40b6 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java
@@ -17,12 +17,16 @@
import java.util.HashMap;
import java.util.Map;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Contains information which {@link MasterKeyProvider}s can use to select which {@link MasterKey}s
* to use to protect a given plaintext. This class is immutable.
+ *
+ * @deprecated MasterKeys and MasterKeyProviders have been deprecated in favor of {@link Keyring}s
*/
+@Deprecated
public final class MasterKeyRequest {
private final Map encryptionContext_;
private final boolean isStreaming_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/caching/CachingCryptoMaterialsManager.java b/src/main/java/com/amazonaws/encryptionsdk/caching/CachingCryptoMaterialsManager.java
index daf5c549e..28daf9e50 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/caching/CachingCryptoMaterialsManager.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/caching/CachingCryptoMaterialsManager.java
@@ -14,6 +14,7 @@
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.internal.EncryptionContextSerializer;
import com.amazonaws.encryptionsdk.internal.Utils;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
@@ -22,16 +23,16 @@
/**
* The CachingCryptoMaterialsManager wraps another {@link CryptoMaterialsManager}, and caches its results. This helps reduce
- * the number of calls made to the underlying {@link CryptoMaterialsManager} and/or {@link MasterKeyProvider}, which may
- * help reduce cost and/or improve performance.
+ * the number of calls made to the underlying {@link CryptoMaterialsManager}, {@link MasterKeyProvider} and/or
+ * {@link Keyring}, which may help reduce cost and/or improve performance.
*
* The CachingCryptoMaterialsManager helps enforce a number of usage limits on encrypt. Specifically, it limits the number of
* individual messages encrypted with a particular data key, and the number of plaintext bytes encrypted with the same
* data key. It also allows you to configure a maximum time-to-live for cache entries.
*
* Note that when performing streaming encryption operations, unless you set the stream size before writing any data
- * using {@link com.amazonaws.encryptionsdk.CryptoOutputStream#setMaxInputLength(long)} or
- * {@link com.amazonaws.encryptionsdk.CryptoInputStream#setMaxInputLength(long)}, the size of the message will not be
+ * using {@link com.amazonaws.encryptionsdk.AwsCryptoOutputStream#setMaxInputLength(long)} or
+ * {@link com.amazonaws.encryptionsdk.AwsCryptoInputStream#setMaxInputLength(long)}, the size of the message will not be
* known, and to avoid exceeding byte use limits, caching will not be performed.
*
* By default, two different {@link CachingCryptoMaterialsManager}s will not share cached entries, even when using the same
@@ -79,8 +80,8 @@ private Builder() {}
* Sets the {@link CryptoMaterialsManager} that should be queried when the {@link CachingCryptoMaterialsManager}
* incurs a cache miss.
*
- * You can set either a MasterKeyProvider or a CryptoMaterialsManager to back the CCMM - the last value set will
- * be used.
+ * You can set either a MasterKeyProvider, a Keyring, or a CryptoMaterialsManager to back the CCMM - the last
+ * value set will be used.
*
* @param backingCMM The CryptoMaterialsManager to invoke on cache misses
* @return this builder
@@ -94,19 +95,39 @@ public Builder withBackingMaterialsManager(CryptoMaterialsManager backingCMM) {
* Sets the {@link MasterKeyProvider} that should be queried when the {@link CachingCryptoMaterialsManager}
* incurs a cache miss.
*
- * You can set either a MasterKeyProvider or a CryptoMaterialsManager to back the CCMM - the last value set will
- * be used.
+ * You can set either a MasterKeyProvider, a Keyring, or a CryptoMaterialsManager to back the CCMM - the last
+ * value set will be used.
*
* This method is equivalent to calling {@link #withBackingMaterialsManager(CryptoMaterialsManager)} passing a
* {@link DefaultCryptoMaterialsManager} constructed using your {@link MasterKeyProvider}.
*
+ * @deprecated {@link MasterKeyProvider}s have been deprecated in favor of {@link Keyring}s.
+ *
* @param mkp The MasterKeyProvider to invoke on cache misses
* @return this builder
*/
+ @Deprecated
public Builder withMasterKeyProvider(MasterKeyProvider mkp) {
return withBackingMaterialsManager(new DefaultCryptoMaterialsManager(mkp));
}
+ /**
+ * Sets the {@link Keyring} that should be queried when the {@link CachingCryptoMaterialsManager}
+ * incurs a cache miss.
+ *
+ * You can set either a MasterKeyProvider, a Keyring, or a CryptoMaterialsManager to back the CCMM - the last
+ * value set will be used.
+ *
+ * This method is equivalent to calling {@link #withBackingMaterialsManager(CryptoMaterialsManager)} passing a
+ * {@link DefaultCryptoMaterialsManager} constructed using your {@link Keyring}.
+ *
+ * @param keyring The Keyring to invoke on cache misses
+ * @return this builder
+ */
+ public Builder withKeyring(Keyring keyring) {
+ return withBackingMaterialsManager(new DefaultCryptoMaterialsManager(keyring));
+ }
+
/**
* Sets the cache to which this {@link CryptoMaterialsManager} will be bound.
* @param cache The cache to associate with the CMM
diff --git a/src/main/java/com/amazonaws/encryptionsdk/exception/UnsupportedRegionException.java b/src/main/java/com/amazonaws/encryptionsdk/exception/UnsupportedRegionException.java
index 2c06a602c..1a3be9350 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/exception/UnsupportedRegionException.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/exception/UnsupportedRegionException.java
@@ -15,7 +15,7 @@
/**
* This exception is thrown when a region that is not allowed to be used by
- * a given KmsClientSupplier is specified.
+ * a given AwsKmsClientSupplier is specified.
*/
public class UnsupportedRegionException extends AwsCryptoException {
diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/Constants.java b/src/main/java/com/amazonaws/encryptionsdk/internal/Constants.java
index e580f7c79..684515d37 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/internal/Constants.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/internal/Constants.java
@@ -62,4 +62,9 @@ private Constants() {
public static final long MAX_FRAME_NUMBER = (1L << 32) - 1;
public static final String EC_PUBLIC_KEY_FIELD = "aws-crypto-public-key";
+
+ /**
+ * The provider ID used for the AwsKmsKeyring
+ */
+ public static final String AWS_KMS_PROVIDER_ID = "aws-kms";
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java b/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java
index 386a4d867..38628d010 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java
@@ -34,6 +34,8 @@
import com.amazonaws.encryptionsdk.MasterKeyProvider;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.KeyringTrace;
import com.amazonaws.encryptionsdk.model.CiphertextFooters;
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
import com.amazonaws.encryptionsdk.model.CiphertextType;
@@ -62,6 +64,7 @@ public class DecryptionHandler> implements MessageCryptoH
private CryptoHandler contentCryptoHandler_;
private DataKey dataKey_;
+ private KeyringTrace keyringTrace_;
private SecretKey decryptionKey_;
private CryptoAlgorithm cryptoAlgo_;
private Signature trailingSig_;
@@ -109,8 +112,11 @@ private DecryptionHandler(final CryptoMaterialsManager materialsManager, final C
* the key blobs encoded in the provided ciphertext.
* @throws AwsCryptoException
* if the master key is null.
+ * @deprecated MasterKeyProviders have been deprecated in favor of {@link Keyring}s.
+ * Use {@link #create(CryptoMaterialsManager)} instead.
*/
@SuppressWarnings("unchecked")
+ @Deprecated
public static > DecryptionHandler create(
final MasterKeyProvider customerMasterKeyProvider
) throws AwsCryptoException {
@@ -134,8 +140,11 @@ public static > DecryptionHandler create(
* {@link #processBytes(byte[], int, int, byte[], int)}
* @throws AwsCryptoException
* if the master key is null.
+ * @deprecated MasterKeyProviders have been deprecated in favor of {@link Keyring}s.
+ * Use {@link #create(CryptoMaterialsManager, CiphertextHeaders)} instead.
*/
@SuppressWarnings("unchecked")
+ @Deprecated
public static > DecryptionHandler create(
final MasterKeyProvider customerMasterKeyProvider, final CiphertextHeaders headers
) throws AwsCryptoException {
@@ -456,6 +465,7 @@ private void readHeaderFields(final CiphertextHeaders ciphertextHeaders) {
//noinspection unchecked
dataKey_ = (DataKey)result.getDataKey();
+ keyringTrace_ = result.getKeyringTrace();
PublicKey trailingPublicKey = result.getTrailingSignatureKey();
try {
@@ -534,11 +544,28 @@ public CiphertextHeaders getHeaders() {
return ciphertextHeaders_;
}
+ /**
+ * The master key that was used to decrypt the encrypted data key. This returns an
+ * empty list if Keyrings are in use.
+ *
+ * @deprecated MasterKeys have been deprecated in favor of {@link Keyring}s.
+ * Use {@link #getKeyringTrace()} to view which key was used in decryption.
+ */
@Override
+ @Deprecated
public List getMasterKeys() {
+ if(dataKey_.getMasterKey() == null) {
+ return Collections.emptyList();
+ }
+
return Collections.singletonList(dataKey_.getMasterKey());
}
+ @Override
+ public KeyringTrace getKeyringTrace() {
+ return keyringTrace_;
+ }
+
@Override
public boolean isComplete() {
return complete_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java b/src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java
index 843704a87..b2f18021a 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java
@@ -21,12 +21,15 @@
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.ECPrivateKey;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.KeyringTrace;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
@@ -58,6 +61,7 @@ public class EncryptionHandler implements MessageCryptoHandler {
private final Map encryptionContext_;
private final CryptoAlgorithm cryptoAlgo_;
private final List masterKeys_;
+ private final KeyringTrace keyringTrace_;
private final List keyBlobs_;
private final SecretKey encryptionKey_;
private final byte version_;
@@ -90,6 +94,7 @@ public EncryptionHandler(int frameSize, EncryptionMaterials result) throws AwsCr
this.encryptionContext_ = result.getEncryptionContext();
this.cryptoAlgo_ = result.getAlgorithm();
this.masterKeys_ = result.getMasterKeys();
+ this.keyringTrace_ = result.getKeyringTrace();
this.keyBlobs_ = result.getEncryptedDataKeys();
this.trailingSignaturePrivateKey_ = result.getTrailingSignatureKey();
@@ -405,12 +410,28 @@ private CiphertextHeaders signCiphertextHeaders(final CiphertextHeaders unsigned
return unsignedHeaders;
}
+ /**
+ * The master keys that were used to encrypt the data key. This returns an
+ * empty list if Keyrings are in use.
+ *
+ * @deprecated MasterKeys have been deprecated in favor of {@link Keyring}s.
+ * Use {@link #getKeyringTrace()} to view which keys were used in encryption.
+ */
@Override
+ @Deprecated
public List extends MasterKey>> getMasterKeys() {
+ if(masterKeys_ == null) {
+ return Collections.emptyList();
+ }
//noinspection unchecked
return (List)masterKeys_; // This is unmodifiable
}
+ @Override
+ public KeyringTrace getKeyringTrace() {
+ return keyringTrace_;
+ }
+
private void updateTrailingSignature(byte[] input, int offset, int len) {
if (trailingDigest_ != null) {
trailingDigest_.update(input, offset, len);
diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/LazyMessageCryptoHandler.java b/src/main/java/com/amazonaws/encryptionsdk/internal/LazyMessageCryptoHandler.java
index bc50183a4..1d6dab5af 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/internal/LazyMessageCryptoHandler.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/internal/LazyMessageCryptoHandler.java
@@ -6,6 +6,7 @@
import java.util.function.Function;
import com.amazonaws.encryptionsdk.MasterKey;
+import com.amazonaws.encryptionsdk.keyrings.KeyringTrace;
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
/**
@@ -93,6 +94,11 @@ public List extends MasterKey>> getMasterKeys() {
return getDelegate().getMasterKeys();
}
+ @Override
+ public KeyringTrace getKeyringTrace() {
+ return getDelegate().getKeyringTrace();
+ }
+
@Override
public int doFinal(byte[] out, int outOff) {
return getDelegate().doFinal(out, outOff);
diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/MessageCryptoHandler.java b/src/main/java/com/amazonaws/encryptionsdk/internal/MessageCryptoHandler.java
index 34445f867..1d2c7c93d 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/internal/MessageCryptoHandler.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/internal/MessageCryptoHandler.java
@@ -17,6 +17,8 @@
import java.util.Map;
import com.amazonaws.encryptionsdk.MasterKey;
+import com.amazonaws.encryptionsdk.keyrings.Keyring;
+import com.amazonaws.encryptionsdk.keyrings.KeyringTrace;
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
public interface MessageCryptoHandler extends CryptoHandler {
@@ -48,6 +50,17 @@ public interface MessageCryptoHandler extends CryptoHandler {
* All used {@link MasterKey}s. For encryption flows, these are all the
* {@link MasterKey}s used to protect the data. In the decryption flow, it is the single
* {@link MasterKey} actually used to decrypt the data.
+ *
+ * @deprecated MasterKeys and MasterKeyProviders have been deprecated in favor of {@link Keyring}s
*/
+ @Deprecated
List extends MasterKey>> getMasterKeys();
+
+ /**
+ * Gets the KeyringTrace containing all actions {@link Keyring}s have taken as part of
+ * encryption or decryption.
+ *
+ * @return the KeyringTrace
+ */
+ KeyringTrace getKeyringTrace();
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/jce/JceMasterKey.java b/src/main/java/com/amazonaws/encryptionsdk/jce/JceMasterKey.java
index 6c6e03e34..bf632356c 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/jce/JceMasterKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/jce/JceMasterKey.java
@@ -21,6 +21,7 @@
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
import com.amazonaws.encryptionsdk.internal.JceKeyCipher;
import com.amazonaws.encryptionsdk.internal.Utils;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@@ -37,7 +38,10 @@
* Represents a {@link MasterKey} backed by one (or more) JCE {@link Key}s. Instances of this should
* only be acquired using {@link #getInstance(SecretKey, String, String, String)} or
* {@link #getInstance(PublicKey, PrivateKey, String, String, String)}.
+ *
+ * @deprecated Replaced by {@code RawAesKeyring} and {@code RawRsaKeyring}. See {@link StandardKeyrings}.
*/
+@Deprecated
public class JceMasterKey extends MasterKey {
private final String providerName_;
private final String keyId_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/jce/KeyStoreProvider.java b/src/main/java/com/amazonaws/encryptionsdk/jce/KeyStoreProvider.java
index 1e92cd2bd..f220aea8b 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/jce/KeyStoreProvider.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/jce/KeyStoreProvider.java
@@ -38,13 +38,17 @@
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
/**
* This {@link MasterKeyProvider} provides keys backed by a JCE {@link KeyStore}. Please see
* {@link #decryptDataKey(CryptoAlgorithm, Collection, Map)} for an of how decryption is managed and
* see {@link #getMasterKeysForEncryption(MasterKeyRequest)} for an explanation of how encryption is
* managed.
+ *
+ * @deprecated Replaced by {@code RawAesKeyring} and {@code RawRsaKeyring}. See {@link StandardKeyrings}.
*/
+@Deprecated
public class KeyStoreProvider extends MasterKeyProvider {
private final String providerName_;
private final KeyStore keystore_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/KmsKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/AwsKmsKeyring.java
similarity index 60%
rename from src/main/java/com/amazonaws/encryptionsdk/keyrings/KmsKeyring.java
rename to src/main/java/com/amazonaws/encryptionsdk/keyrings/AwsKmsKeyring.java
index 528b4a1dc..76cd8a9b7 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/KmsKeyring.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/AwsKmsKeyring.java
@@ -16,14 +16,14 @@
import com.amazonaws.encryptionsdk.EncryptedDataKey;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
-import com.amazonaws.encryptionsdk.exception.MalformedArnException;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao;
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao.DecryptDataKeyResult;
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao.GenerateDataKeyResult;
-import com.amazonaws.encryptionsdk.kms.KmsUtils;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.KeyBlob;
+import org.apache.commons.lang3.Validate;
import java.util.ArrayList;
import java.util.HashSet;
@@ -31,65 +31,66 @@
import java.util.Set;
import static com.amazonaws.encryptionsdk.EncryptedDataKey.PROVIDER_ENCODING;
-import static com.amazonaws.encryptionsdk.kms.KmsUtils.KMS_PROVIDER_ID;
-import static com.amazonaws.encryptionsdk.kms.KmsUtils.isArnWellFormed;
+import static com.amazonaws.encryptionsdk.internal.Constants.AWS_KMS_PROVIDER_ID;
+import static com.amazonaws.encryptionsdk.kms.AwsKmsCmkId.isKeyIdWellFormed;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
/**
* A keyring which interacts with AWS Key Management Service (KMS) to create,
- * encrypt, and decrypt data keys using KMS defined Customer Master Keys (CMKs).
+ * encrypt, and decrypt data keys using AWS KMS defined Customer Master Keys (CMKs).
*/
-class KmsKeyring implements Keyring {
+class AwsKmsKeyring implements Keyring {
private final DataKeyEncryptionDao dataKeyEncryptionDao;
- private final List keyIds;
- private final String generatorKeyId;
+ private final List keyIds;
+ private final AwsKmsCmkId generatorKeyId;
private final boolean isDiscovery;
- KmsKeyring(DataKeyEncryptionDao dataKeyEncryptionDao, List keyIds, String generatorKeyId) {
+ AwsKmsKeyring(DataKeyEncryptionDao dataKeyEncryptionDao, List keyIds, AwsKmsCmkId generatorKeyId, boolean isDiscovery) {
requireNonNull(dataKeyEncryptionDao, "dataKeyEncryptionDao is required");
this.dataKeyEncryptionDao = dataKeyEncryptionDao;
this.keyIds = keyIds == null ? emptyList() : unmodifiableList(new ArrayList<>(keyIds));
this.generatorKeyId = generatorKeyId;
- this.isDiscovery = this.generatorKeyId == null && this.keyIds.isEmpty();
-
- if (!this.keyIds.stream().allMatch(KmsUtils::isArnWellFormed)) {
- throw new MalformedArnException("keyIds must contain only CMK aliases and well formed ARNs");
+ this.isDiscovery = isDiscovery;
+
+ if(isDiscovery) {
+ Validate.isTrue(generatorKeyId == null && this.keyIds.isEmpty(),
+ "AWS KMS Discovery keyrings cannot specify any key IDs");
+ } else {
+ Validate.isTrue(generatorKeyId != null || !this.keyIds.isEmpty(),
+ "GeneratorKeyId or KeyIds are required for non-discovery AWS KMS Keyrings.");
}
- if (generatorKeyId != null) {
- if (!isArnWellFormed(generatorKeyId)) {
- throw new MalformedArnException("generatorKeyId must be either a CMK alias or a well formed ARN");
- }
- if (this.keyIds.contains(generatorKeyId)) {
- throw new IllegalArgumentException("KeyIds should not contain the generatorKeyId");
- }
+ if (this.keyIds.contains(generatorKeyId)) {
+ throw new IllegalArgumentException("KeyIds should not contain the generatorKeyId");
}
}
@Override
- public void onEncrypt(EncryptionMaterials encryptionMaterials) {
+ public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) {
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
// If this keyring is a discovery keyring, OnEncrypt MUST return the input encryption materials unmodified.
if (isDiscovery) {
- return;
+ return encryptionMaterials;
}
+ EncryptionMaterials resultMaterials = encryptionMaterials;
+
// If the input encryption materials do not contain a plaintext data key and this keyring does not
// have a generator defined, OnEncrypt MUST not modify the encryption materials and MUST fail.
if (!encryptionMaterials.hasCleartextDataKey() && generatorKeyId == null) {
throw new AwsCryptoException("Encryption materials must contain either a plaintext data key or a generator");
}
- final List keyIdsToEncrypt = new ArrayList<>(keyIds);
+ final List keyIdsToEncrypt = new ArrayList<>(keyIds);
// If the input encryption materials do not contain a plaintext data key and a generator is defined onEncrypt
// MUST attempt to generate a new plaintext data key and encrypt that data key by calling KMS GenerateDataKey.
if (!encryptionMaterials.hasCleartextDataKey()) {
- generateDataKey(encryptionMaterials);
+ resultMaterials = generateDataKey(encryptionMaterials);
} else if (generatorKeyId != null) {
// If this keyring's generator is defined and was not used to generate a data key, OnEncrypt
// MUST also attempt to encrypt the plaintext data key using the CMK specified by the generator.
@@ -98,39 +99,45 @@ public void onEncrypt(EncryptionMaterials encryptionMaterials) {
// Given a plaintext data key in the encryption materials, OnEncrypt MUST attempt
// to encrypt the plaintext data key using each CMK specified in it's key IDs list.
- for (String keyId : keyIdsToEncrypt) {
- encryptDataKey(keyId, encryptionMaterials);
+ for (AwsKmsCmkId keyId : keyIdsToEncrypt) {
+ resultMaterials = encryptDataKey(keyId, resultMaterials);
}
+
+ return resultMaterials;
}
- private void generateDataKey(final EncryptionMaterials encryptionMaterials) {
+ private EncryptionMaterials generateDataKey(final EncryptionMaterials encryptionMaterials) {
final GenerateDataKeyResult result = dataKeyEncryptionDao.generateDataKey(generatorKeyId,
encryptionMaterials.getAlgorithm(), encryptionMaterials.getEncryptionContext());
- encryptionMaterials.setCleartextDataKey(result.getPlaintextDataKey(),
- new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.GENERATED_DATA_KEY));
- encryptionMaterials.addEncryptedDataKey(new KeyBlob(result.getEncryptedDataKey()),
- new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
+ return encryptionMaterials
+ .withCleartextDataKey(result.getPlaintextDataKey(),
+ new KeyringTraceEntry(AWS_KMS_PROVIDER_ID, generatorKeyId.toString(),
+ KeyringTraceFlag.GENERATED_DATA_KEY))
+ .withEncryptedDataKey(new KeyBlob(result.getEncryptedDataKey()),
+ new KeyringTraceEntry(AWS_KMS_PROVIDER_ID, generatorKeyId.toString(),
+ KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
}
- private void encryptDataKey(final String keyId, final EncryptionMaterials encryptionMaterials) {
+ private EncryptionMaterials encryptDataKey(final AwsKmsCmkId keyId, final EncryptionMaterials encryptionMaterials) {
final EncryptedDataKey encryptedDataKey = dataKeyEncryptionDao.encryptDataKey(keyId,
encryptionMaterials.getCleartextDataKey(), encryptionMaterials.getEncryptionContext());
- encryptionMaterials.addEncryptedDataKey(new KeyBlob(encryptedDataKey),
- new KeyringTraceEntry(KMS_PROVIDER_ID, keyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
+ return encryptionMaterials.withEncryptedDataKey(new KeyBlob(encryptedDataKey),
+ new KeyringTraceEntry(AWS_KMS_PROVIDER_ID, keyId.toString(),
+ KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
}
@Override
- public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys) {
+ public DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys) {
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
if (decryptionMaterials.hasCleartextDataKey() || encryptedDataKeys.isEmpty()) {
- return;
+ return decryptionMaterials;
}
- final Set configuredKeyIds = new HashSet<>(keyIds);
+ final Set configuredKeyIds = new HashSet<>(keyIds);
if (generatorKeyId != null) {
configuredKeyIds.add(generatorKeyId);
@@ -142,25 +149,26 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends En
final DecryptDataKeyResult result = dataKeyEncryptionDao.decryptDataKey(encryptedDataKey,
decryptionMaterials.getAlgorithm(), decryptionMaterials.getEncryptionContext());
- decryptionMaterials.setCleartextDataKey(result.getPlaintextDataKey(),
- new KeyringTraceEntry(KMS_PROVIDER_ID, result.getKeyArn(),
+ return decryptionMaterials.withCleartextDataKey(result.getPlaintextDataKey(),
+ new KeyringTraceEntry(AWS_KMS_PROVIDER_ID, result.getKeyArn(),
KeyringTraceFlag.DECRYPTED_DATA_KEY, KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT));
- return;
} catch (CannotUnwrapDataKeyException e) {
continue;
}
}
}
+
+ return decryptionMaterials;
}
- private boolean okToDecrypt(EncryptedDataKey encryptedDataKey, Set configuredKeyIds) {
+ private boolean okToDecrypt(EncryptedDataKey encryptedDataKey, Set configuredKeyIds) {
// Only attempt to decrypt keys provided by KMS
- if (!encryptedDataKey.getProviderId().equals(KMS_PROVIDER_ID)) {
+ if (!encryptedDataKey.getProviderId().equals(AWS_KMS_PROVIDER_ID)) {
return false;
}
- // If the key ARN cannot be parsed, skip it
- if(!isArnWellFormed(new String(encryptedDataKey.getProviderInformation(), PROVIDER_ENCODING)))
+ // If the key ID cannot be parsed, skip it
+ if(!isKeyIdWellFormed(new String(encryptedDataKey.getProviderInformation(), PROVIDER_ENCODING)))
{
return false;
}
@@ -174,6 +182,7 @@ private boolean okToDecrypt(EncryptedDataKey encryptedDataKey, Set confi
// OnDecrypt MUST attempt to decrypt each input encrypted data key in the input
// encrypted data key list where the key provider info has a value equal to one
// of the ARNs in this keyring's key IDs or the generator
- return configuredKeyIds.contains(new String(encryptedDataKey.getProviderInformation(), PROVIDER_ENCODING));
+ return configuredKeyIds.contains(
+ AwsKmsCmkId.fromString(new String(encryptedDataKey.getProviderInformation(), PROVIDER_ENCODING)));
}
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/AwsKmsKeyringBuilder.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/AwsKmsKeyringBuilder.java
new file mode 100644
index 000000000..483a13506
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/AwsKmsKeyringBuilder.java
@@ -0,0 +1,121 @@
+/*
+ * 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.encryptionsdk.keyrings;
+
+import com.amazonaws.encryptionsdk.kms.AwsKmsClientSupplier;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
+import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao;
+
+import java.util.List;
+
+public class AwsKmsKeyringBuilder {
+ private AwsKmsClientSupplier awsKmsClientSupplier;
+ private List grantTokens;
+ private List keyIds;
+ private AwsKmsCmkId generatorKeyId;
+ private final boolean isDiscovery;
+
+ private AwsKmsKeyringBuilder(boolean isDiscovery) {
+ // Use AwsKmsKeyringBuilder.standard() or StandardKeyrings.awsKmsBuilder() to instantiate
+ // a standard Aws Kms Keyring builder. If an Aws Kms Discovery Keyring builder is needed use
+ // AwsKmsKeyringBuilder.discovery() or StandardKeyrings.awsKmsDiscoveryBuilder().
+ this.isDiscovery = isDiscovery;
+ }
+
+ /**
+ * Constructs a new instance of {@code AwsKmsKeyringBuilder}
+ *
+ * @return The {@code AwsKmsKeyringBuilder}
+ */
+ public static AwsKmsKeyringBuilder standard() {
+ return new AwsKmsKeyringBuilder(false);
+ }
+
+ /**
+ * Constructs a new instance of {@code AwsKmsKeyringBuilder} that produces an AWS KMS Discovery keyring.
+ * AWS KMS Discovery keyrings do not specify any CMKs to decrypt with, and thus will attempt to decrypt
+ * using any encrypted data key in an encrypted message. AWS KMS Discovery keyrings do not perform encryption.
+ *
+ * @return The {@code AwsKmsKeyringBuilder}
+ */
+ public static AwsKmsKeyringBuilder discovery() {
+ return new AwsKmsKeyringBuilder(true);
+ }
+
+ /**
+ * A function that returns an AWS KMS client that can make GenerateDataKey, Encrypt, and Decrypt calls in
+ * a particular AWS region. If this is not supplied, the default AwsKmsClientSupplier will
+ * be used. AwsKmsClientSupplier.builder() can be used to construct this type.
+ *
+ * @param awsKmsClientSupplier The AWS KMS client supplier
+ * @return The AwsKmsKeyringBuilder, for method chaining
+ */
+ public AwsKmsKeyringBuilder awsKmsClientSupplier(AwsKmsClientSupplier awsKmsClientSupplier) {
+ this.awsKmsClientSupplier = awsKmsClientSupplier;
+ return this;
+ }
+
+ /**
+ * A list of string grant tokens to be included in all KMS calls.
+ *
+ * @param grantTokens The list of grant tokens
+ * @return The AwsKmsKeyringBuilder, for method chaining
+ */
+ public AwsKmsKeyringBuilder grantTokens(List grantTokens) {
+ this.grantTokens = grantTokens;
+ return this;
+ }
+
+ /**
+ * A list of {@link AwsKmsCmkId}s in ARN, CMK Alias, or ARN Alias format identifying AWS KMS CMKs
+ * used for encrypting and decrypting data keys.
+ *
+ * @param keyIds The list of AWS KMS CMKs
+ * @return The AwsKmsKeyringBuilder, for method chaining
+ */
+ public AwsKmsKeyringBuilder keyIds(List keyIds) {
+ this.keyIds = keyIds;
+ return this;
+ }
+
+ /**
+ * An {@link AwsKmsCmkId} in ARN, CMK Alias, or ARN Alias format that identifies a
+ * AWS KMS CMK responsible for generating a data key, as well as encrypting and
+ * decrypting data keys .
+ *
+ * @param generatorKeyId An {@link AwsKmsCmkId} in ARN, CMK Alias, or ARN Alias format that identifies a
+ * AWS KMS CMK responsible for generating a data key, as well as encrypting and
+ * decrypting data keys.
+ * @return The AwsKmsKeyringBuilder, for method chaining
+ */
+ public AwsKmsKeyringBuilder generatorKeyId(AwsKmsCmkId generatorKeyId) {
+ this.generatorKeyId = generatorKeyId;
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Keyring} instance.
+ *
+ * @return The {@link Keyring} instance
+ */
+ public Keyring build() {
+ if (awsKmsClientSupplier == null) {
+ awsKmsClientSupplier = AwsKmsClientSupplier.builder().build();
+ }
+
+ return new AwsKmsKeyring(DataKeyEncryptionDao.awsKms(awsKmsClientSupplier, grantTokens),
+ keyIds, generatorKeyId, isDiscovery);
+ }
+
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java
index 93002270d..0930b8406 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java
@@ -27,16 +27,18 @@ public interface Keyring {
/**
* Attempt to encrypt either the given data key (if present) or one that may be generated
*
- * @param encryptionMaterials Materials needed for encryption that the keyring may modify.
+ * @param encryptionMaterials Materials needed for encryption.
+ * @return Encryption materials with added information provided by this keyring.
*/
- void onEncrypt(EncryptionMaterials encryptionMaterials);
+ EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials);
/**
* Attempt to decrypt the encrypted data keys
*
- * @param decryptionMaterials Materials needed for decryption that the keyring may modify.
+ * @param decryptionMaterials Materials needed for decryption.
* @param encryptedDataKeys List of encrypted data keys.
+ * @return Decryption materials with added information provided by this keyring.
*/
- void onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys);
+ DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys);
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/KeyringTrace.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/KeyringTrace.java
index c9528c08f..9298194e6 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/KeyringTrace.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/KeyringTrace.java
@@ -17,35 +17,34 @@
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
/**
* A keyring trace containing all of the actions that keyrings have taken on a set of encryption materials.
*/
-public class KeyringTrace {
+public final class KeyringTrace {
- private final List entries = new ArrayList<>();
+ private final List entries;
+ public static final KeyringTrace EMPTY_TRACE = new KeyringTrace(emptyList());
- /**
- * Add a new entry to the keyring trace.
- *
- * @param keyNamespace The namespace for the key.
- * @param keyName The name of the key.
- * @param flags A set of one or more KeyringTraceFlag enums
- * indicating what actions were taken by a keyring.
- */
- public void add(String keyNamespace, String keyName, KeyringTraceFlag... flags) {
- add(new KeyringTraceEntry(keyNamespace, keyName, flags));
+ public KeyringTrace(final List entries) {
+ this.entries = unmodifiableList(new ArrayList<>(entries));
}
/**
- * Add a new entry to the keyring trace.
+ * Creates a new instance of {@code KeyringTrace} with the provided {@link KeyringTraceEntry}.
*
- * @param entry The entry to add.
+ * @param entry The entry to include in the new {@code KeyringTrace}.
+ * @return The new {@code KeyringTrace} instance.
*/
- public void add(KeyringTraceEntry entry) {
- entries.add(entry);
+ public KeyringTrace with(KeyringTraceEntry entry) {
+ final List newEntries = new ArrayList<>(entries);
+ newEntries.add(entry);
+ return new KeyringTrace(newEntries);
}
/**
@@ -56,7 +55,7 @@ public void add(KeyringTraceEntry entry) {
* @return An unmodifiable list of `KeyringTraceEntry`s
*/
public List getEntries() {
- return Collections.unmodifiableList(entries);
+ return entries;
}
@Override
@@ -65,4 +64,17 @@ public String toString() {
.append("entries", entries)
.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ KeyringTrace that = (KeyringTrace) o;
+ return Objects.equals(entries, that.entries);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(entries);
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/MultiKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/MultiKeyring.java
index ce6d9600c..de00c7c85 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/MultiKeyring.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/MultiKeyring.java
@@ -45,30 +45,34 @@ class MultiKeyring implements Keyring {
}
@Override
- public void onEncrypt(EncryptionMaterials encryptionMaterials) {
+ public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) {
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
+ EncryptionMaterials resultMaterials = encryptionMaterials;
+
if (generatorKeyring != null) {
- generatorKeyring.onEncrypt(encryptionMaterials);
+ resultMaterials = generatorKeyring.onEncrypt(encryptionMaterials);
}
- if (!encryptionMaterials.hasCleartextDataKey()) {
+ if (!resultMaterials.hasCleartextDataKey()) {
throw new AwsCryptoException("Either a generator keyring must be supplied that produces a cleartext " +
"data key or a cleartext data key must already be present in the encryption materials.");
}
for (Keyring keyring : childrenKeyrings) {
- keyring.onEncrypt(encryptionMaterials);
+ resultMaterials = keyring.onEncrypt(resultMaterials);
}
+
+ return resultMaterials;
}
@Override
- public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys) {
+ public DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys) {
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
if (decryptionMaterials.hasCleartextDataKey()) {
- return;
+ return decryptionMaterials;
}
final List keyringsToDecryptWith = new ArrayList<>();
@@ -83,11 +87,11 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends En
for (Keyring keyring : keyringsToDecryptWith) {
try {
- keyring.onDecrypt(decryptionMaterials, encryptedDataKeys);
+ final DecryptionMaterials resultMaterials = keyring.onDecrypt(decryptionMaterials, encryptedDataKeys);
- if (decryptionMaterials.hasCleartextDataKey()) {
+ if (resultMaterials.hasCleartextDataKey()) {
// Decryption succeeded, return immediately
- return;
+ return resultMaterials;
}
} catch (Exception e) {
exceptions.add(e);
@@ -100,5 +104,7 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends En
exceptions.forEach(exception::addSuppressed);
throw exception;
}
+
+ return decryptionMaterials;
}
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java
index bbd2a7495..1503bdd88 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java
@@ -23,7 +23,7 @@
* A {@code Keyring} which does local AES-GCM encryption
* decryption of data keys using the provided wrapping key.
*
- * Instantiate by using the {@code StandardKeyrings.rawAes(...)} factory method.
+ * Instantiate by using the {@code StandardKeyrings.rawAesBuilder(...)} factory method.
*/
class RawAesKeyring extends RawKeyring {
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyringBuilder.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyringBuilder.java
new file mode 100644
index 000000000..8f8929419
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyringBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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.encryptionsdk.keyrings;
+
+import javax.crypto.SecretKey;
+
+public class RawAesKeyringBuilder {
+ private String keyNamespace;
+ private String keyName;
+ private SecretKey wrappingKey;
+
+ private RawAesKeyringBuilder() {
+ // Use RawAesKeyringBuilder.standard() or StandardKeyrings.rawAes() to instantiate
+ }
+
+ /**
+ * Constructs a new instance of {@code RawAesKeyringBuilder}
+ *
+ * @return The {@code RawAesKeyringBuilder}
+ */
+ public static RawAesKeyringBuilder standard() {
+ return new RawAesKeyringBuilder();
+ }
+
+ /**
+ * A value that, together with the key name, identifies the wrapping key (required).
+ *
+ * @param keyNamespace The key namespace
+ * @return The RawAesKeyringBuilder, for method chaining
+ */
+ public RawAesKeyringBuilder keyNamespace(String keyNamespace) {
+ this.keyNamespace = keyNamespace;
+ return this;
+ }
+
+ /**
+ * A value that, together with the key namespace, identifies the wrapping key (required).
+ *
+ * @param keyName The key name
+ * @return The RawAesKeyringBuilder, for method chaining
+ */
+ public RawAesKeyringBuilder keyName(String keyName) {
+ this.keyName = keyName;
+ return this;
+ }
+
+ /**
+ * The AES key input to AES-GCM to encrypt plaintext data keys (required).
+ *
+ * @param wrappingKey The wrapping key
+ * @return The RawAesKeyringBuilder, for method chaining
+ */
+ public RawAesKeyringBuilder wrappingKey(SecretKey wrappingKey) {
+ this.wrappingKey = wrappingKey;
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Keyring} instance.
+ *
+ * @return The {@link Keyring} instance
+ */
+ public Keyring build() {
+ return new RawAesKeyring(keyNamespace, keyName, wrappingKey);
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java
index 18600a4d7..f26604840 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java
@@ -65,27 +65,29 @@ abstract class RawKeyring implements Keyring {
abstract boolean validToDecrypt(EncryptedDataKey encryptedDataKey);
@Override
- public void onEncrypt(EncryptionMaterials encryptionMaterials) {
+ public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) {
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
+ EncryptionMaterials resultMaterials = encryptionMaterials;
+
if (!encryptionMaterials.hasCleartextDataKey()) {
- generateDataKey(encryptionMaterials);
+ resultMaterials = generateDataKey(encryptionMaterials);
}
final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey(
- encryptionMaterials.getCleartextDataKey().getEncoded(),
- keyName, keyNamespace, encryptionMaterials.getEncryptionContext());
- encryptionMaterials.addEncryptedDataKey(new KeyBlob(encryptedDataKey),
+ resultMaterials.getCleartextDataKey().getEncoded(),
+ keyName, keyNamespace, resultMaterials.getEncryptionContext());
+ return resultMaterials.withEncryptedDataKey(new KeyBlob(encryptedDataKey),
new KeyringTraceEntry(keyNamespace, keyName, encryptTraceFlags()));
}
@Override
- public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys) {
+ public DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List extends EncryptedDataKey> encryptedDataKeys) {
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
if (decryptionMaterials.hasCleartextDataKey() || encryptedDataKeys.isEmpty()) {
- return;
+ return decryptionMaterials;
}
for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) {
@@ -93,25 +95,24 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List extends En
try {
final byte[] decryptedKey = jceKeyCipher.decryptKey(
encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext());
- decryptionMaterials.setCleartextDataKey(
+ return decryptionMaterials.withCleartextDataKey(
new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithm().getDataKeyAlgo()),
new KeyringTraceEntry(keyNamespace, keyName, decryptTraceFlags()));
- return;
} catch (Exception e) {
LOGGER.info("Could not decrypt key due to: " + e.getMessage());
}
}
}
- LOGGER.warning("Could not decrypt any data keys");
+ return decryptionMaterials;
}
- private void generateDataKey(EncryptionMaterials encryptionMaterials) {
+ private EncryptionMaterials generateDataKey(EncryptionMaterials encryptionMaterials) {
final byte[] rawKey = new byte[encryptionMaterials.getAlgorithm().getDataKeyLength()];
Utils.getSecureRandom().nextBytes(rawKey);
final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithm().getDataKeyAlgo());
- encryptionMaterials.setCleartextDataKey(key, new KeyringTraceEntry(keyNamespace, keyName, GENERATED_DATA_KEY));
+ return encryptionMaterials.withCleartextDataKey(key, new KeyringTraceEntry(keyNamespace, keyName, GENERATED_DATA_KEY));
}
private KeyringTraceFlag[] encryptTraceFlags() {
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java
index 0a5eaf2d1..d41f6d683 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java
@@ -24,7 +24,7 @@
* A {@link Keyring} which does local RSA encryption and decryption of data keys using the
* provided public and private keys.
*
- * Instantiate by using the {@code StandardKeyrings.rawRsa(...)} factory method.
+ * Instantiate by using the {@code StandardKeyrings.rawRsaBuilder(...)} factory method.
*/
class RawRsaKeyring extends RawKeyring {
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringBuilder.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringBuilder.java
new file mode 100644
index 000000000..190143954
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringBuilder.java
@@ -0,0 +1,102 @@
+/*
+ * 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.encryptionsdk.keyrings;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+public class RawRsaKeyringBuilder {
+ private String keyNamespace;
+ private String keyName;
+ private PublicKey publicKey;
+ private PrivateKey privateKey;
+ private String wrappingAlgorithm;
+
+ private RawRsaKeyringBuilder() {
+ // Use RawRsaKeyringBuilder.standard() or StandardKeyrings.rawRsa() to instantiate
+ }
+
+ /**
+ * Constructs a new instance of {@code RawRsaKeyringBuilder}
+ *
+ * @return The {@code RawRsaKeyringBuilder}
+ */
+ public static RawRsaKeyringBuilder standard() {
+ return new RawRsaKeyringBuilder();
+ }
+
+ /**
+ * A value that, together with the key name, identifies the wrapping key (required).
+ *
+ * @param keyNamespace The key namespace
+ * @return The RawAesKeyringBuilder, for method chaining
+ */
+ public RawRsaKeyringBuilder keyNamespace(String keyNamespace) {
+ this.keyNamespace = keyNamespace;
+ return this;
+ }
+
+ /**
+ * A value that, together with the key namespace, identifies the wrapping key (required).
+ *
+ * @param keyName The key name
+ * @return The RawAesKeyringBuilder, for method chaining
+ */
+ public RawRsaKeyringBuilder keyName(String keyName) {
+ this.keyName = keyName;
+ return this;
+ }
+
+ /**
+ * The RSA public key used by this keyring to encrypt data keys. Not required when used for decryption.
+ *
+ * @param publicKey The public key
+ * @return The RawRsaKeyringBuilder, for method chaining
+ */
+ public RawRsaKeyringBuilder publicKey(PublicKey publicKey) {
+ this.publicKey = publicKey;
+ return this;
+ }
+
+ /**
+ * The RSA private key used by this keyring to decrypt data keys. Not required when used for encryption.
+ *
+ * @param privateKey The public key
+ * @return The RawRsaKeyringBuilder, for method chaining
+ */
+ public RawRsaKeyringBuilder privateKey(PrivateKey privateKey) {
+ this.privateKey = privateKey;
+ return this;
+ }
+
+ /**
+ * The RSA algorithm to use with this keyring (required).
+ *
+ * @param wrappingAlgorithm The algorithm
+ * @return The RawRsaKeyringBuilder, for method chaining
+ */
+ public RawRsaKeyringBuilder wrappingAlgorithm(String wrappingAlgorithm) {
+ this.wrappingAlgorithm = wrappingAlgorithm;
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Keyring} instance.
+ *
+ * @return The {@link Keyring} instance
+ */
+ public Keyring build() {
+ return new RawRsaKeyring(keyNamespace, keyName, publicKey, privateKey, wrappingAlgorithm);
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/StandardKeyrings.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/StandardKeyrings.java
index 9219cf17c..9cf37ea12 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/keyrings/StandardKeyrings.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/StandardKeyrings.java
@@ -13,12 +13,9 @@
package com.amazonaws.encryptionsdk.keyrings;
-import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao;
-import com.amazonaws.encryptionsdk.kms.KmsClientSupplier;
+import com.amazonaws.encryptionsdk.kms.AwsKmsClientSupplier;
+import com.amazonaws.encryptionsdk.kms.AwsKmsCmkId;
-import javax.crypto.SecretKey;
-import java.security.PrivateKey;
-import java.security.PublicKey;
import java.util.Arrays;
import java.util.List;
@@ -31,48 +28,74 @@ private StandardKeyrings() {
}
/**
- * Constructs a {@code Keyring} which does local AES-GCM encryption
- * decryption of data keys using the provided wrapping key.
+ * Returns a {@link RawAesKeyringBuilder} for use in constructing a keyring which does local AES-GCM encryption
+ * decryption of data keys using a provided wrapping key.
*
- * @param keyNamespace A value that, together with the key name, identifies the wrapping key.
- * @param keyName A value that, together with the key namespace, identifies the wrapping key.
- * @param wrappingKey The AES key input to AES-GCM to encrypt plaintext data keys.
- * @return The {@link Keyring}
+ * @return The {@link RawAesKeyringBuilder}
*/
- public static Keyring rawAes(String keyNamespace, String keyName, SecretKey wrappingKey) {
- return new RawAesKeyring(keyNamespace, keyName, wrappingKey);
+ public static RawAesKeyringBuilder rawAesBuilder() {
+ return RawAesKeyringBuilder.standard();
}
/**
- * Constructs a {@code Keyring} which does local RSA encryption and decryption of data keys using the
+ * Constructs a {@code RawRsaKeyringBuilder} which does local RSA encryption and decryption of data keys using the
* provided public and private keys. If {@code privateKey} is {@code null} then the returned {@code Keyring}
* can only be used for encryption.
*
- * @param keyNamespace A value that, together with the key name, identifies the wrapping key.
- * @param keyName A value that, together with the key namespace, identifies the wrapping key.
- * @param publicKey The RSA public key used by this keyring to encrypt data keys.
- * @param privateKey The RSA private key used by this keyring to decrypt data keys.
- * @param wrappingAlgorithm The RSA algorithm to use with this keyring.
- * @return The {@link Keyring}
+ * @return The {@link RawRsaKeyringBuilder}
*/
- public static Keyring rawRsa(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, String wrappingAlgorithm) {
- return new RawRsaKeyring(keyNamespace, keyName, publicKey, privateKey, wrappingAlgorithm);
+ public static RawRsaKeyringBuilder rawRsaBuilder() {
+ return RawRsaKeyringBuilder.standard();
}
/**
* Constructs a {@code Keyring} which interacts with AWS Key Management Service (KMS) to create,
- * encrypt, and decrypt data keys using KMS defined Customer Master Keys (CMKs).
+ * encrypt, and decrypt data keys using the supplied AWS KMS defined Customer Master Key (CMK).
+ * Use {@link #awsKmsBuilder()} for more advanced configuration using an {@link AwsKmsKeyringBuilder}
*
- * @param clientSupplier A function that returns a KMS client that can make GenerateDataKey,
- * Encrypt, and Decrypt calls in a particular AWS region.
- * @param grantTokens A list of string grant tokens to be included in all KMS calls.
- * @param keyIds A list of strings identifying KMS CMKs, in ARN, CMK Alias, or ARN Alias format.
- * @param generator A string that identifies a KMS CMK responsible for generating a data key,
- * as well as encrypting and decrypting data keys in ARN, CMK Alias, or ARN Alias format.
+ * @param generatorKeyId An {@link AwsKmsCmkId} in ARN, CMK Alias, ARN Alias or Key Id format that identifies a
+ * AWS KMS CMK responsible for generating a data key, as well as encrypting and
+ * decrypting data keys .
* @return The {@code Keyring}
*/
- public static Keyring kms(KmsClientSupplier clientSupplier, List grantTokens, List keyIds, String generator) {
- return new KmsKeyring(DataKeyEncryptionDao.kms(clientSupplier, grantTokens), keyIds, generator);
+ public static Keyring awsKms(AwsKmsCmkId generatorKeyId) {
+ return AwsKmsKeyringBuilder.standard()
+ .generatorKeyId(generatorKeyId)
+ .build();
+ }
+
+ /**
+ * Returns a {@link AwsKmsKeyringBuilder} for use in constructing a keyring which interacts with
+ * AWS Key Management Service (KMS) to create, encrypt, and decrypt data keys using AWS KMS defined
+ * Customer Master Keys (CMKs).
+ *
+ * @return The {@link AwsKmsKeyringBuilder}
+ */
+ public static AwsKmsKeyringBuilder awsKmsBuilder() {
+ return AwsKmsKeyringBuilder.standard();
+ }
+
+ /**
+ * Returns an {@link AwsKmsKeyringBuilder} for use in constructing an AWS KMS Discovery keyring.
+ * AWS KMS Discovery keyrings do not specify any CMKs to decrypt with, and thus will attempt to decrypt
+ * using any encrypted data key in an encrypted message. AWS KMS Discovery keyrings do not perform encryption.
+ *
+ * To create an AWS KMS Regional Discovery Keyring, construct an {@link AwsKmsClientSupplier} using
+ * {@link AwsKmsClientSupplier#builder()} to specify which regions to include/exclude.
+ *
+ * For example, to include only CMKs in the us-east-1 region:
+ *
+ * StandardKeyrings.awsKmsDiscovery()
+ * .awsKmsClientSupplier(
+ * AwsKmsClientSupplier.builder()
+ * .allowedRegions(Collections.singleton("us-east-1")).build())
+ * .build();
+ *
+ *
+ * @return The {@code AwsKmsKeyringBuilder}
+ */
+ public static AwsKmsKeyringBuilder awsKmsDiscoveryBuilder() {
+ return AwsKmsKeyringBuilder.discovery();
}
/**
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsClientSupplier.java b/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsClientSupplier.java
similarity index 75%
rename from src/main/java/com/amazonaws/encryptionsdk/kms/KmsClientSupplier.java
rename to src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsClientSupplier.java
index 6182e161c..ceccb5936 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsClientSupplier.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsClientSupplier.java
@@ -14,6 +14,7 @@
package com.amazonaws.encryptionsdk.kms;
import com.amazonaws.ClientConfiguration;
+import com.amazonaws.arn.Arn;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.encryptionsdk.exception.UnsupportedRegionException;
import com.amazonaws.services.kms.AWSKMS;
@@ -38,7 +39,7 @@
* function should be able to handle when the region is null.
*/
@FunctionalInterface
-public interface KmsClientSupplier {
+public interface AwsKmsClientSupplier {
/**
* Gets an {@code AWSKMS} client for the given regionId.
@@ -51,7 +52,7 @@ public interface KmsClientSupplier {
AWSKMS getClient(@Nullable String regionId) throws UnsupportedRegionException;
/**
- * Gets a Builder for constructing a KmsClientSupplier
+ * Gets a Builder for constructing an AwsKmsClientSupplier
*
* @return The builder
*/
@@ -60,7 +61,26 @@ static Builder builder() {
}
/**
- * Builder to construct a KmsClientSupplier given various
+ * Parses region from the given key id (if possible) and passes that region to the
+ * given clientSupplier to produce an {@code AWSKMS} client.
+ *
+ * @param keyId The Amazon Resource Name, Key Alias, Alias ARN or KeyId
+ * @param clientSupplier The client supplier
+ * @return AWSKMS The client
+ */
+ static AWSKMS getClientByKeyId(AwsKmsCmkId keyId, AwsKmsClientSupplier clientSupplier) {
+ requireNonNull(keyId, "keyId is required");
+ requireNonNull(clientSupplier, "clientSupplier is required");
+
+ if(keyId.isArn()) {
+ return clientSupplier.getClient(Arn.fromString(keyId.toString()).getRegion());
+ }
+
+ return clientSupplier.getClient(null);
+ }
+
+ /**
+ * Builder to construct an AwsKmsClientSupplier given various
* optional settings.
*/
class Builder {
@@ -69,22 +89,22 @@ class Builder {
private ClientConfiguration clientConfiguration;
private Set allowedRegions = Collections.emptySet();
private Set excludedRegions = Collections.emptySet();
- private boolean clientCachingEnabled = false;
+ private boolean clientCachingEnabled = true;
private final Map clientsCache = new HashMap<>();
- private static final Set KMS_METHODS = new HashSet<>();
- private AWSKMSClientBuilder kmsClientBuilder;
+ private static final Set AWSKMS_METHODS = new HashSet<>();
+ private AWSKMSClientBuilder awsKmsClientBuilder;
static {
- KMS_METHODS.add("generateDataKey");
- KMS_METHODS.add("encrypt");
- KMS_METHODS.add("decrypt");
+ AWSKMS_METHODS.add("generateDataKey");
+ AWSKMS_METHODS.add("encrypt");
+ AWSKMS_METHODS.add("decrypt");
}
- Builder(AWSKMSClientBuilder kmsClientBuilder) {
- this.kmsClientBuilder = kmsClientBuilder;
+ Builder(AWSKMSClientBuilder awsKmsClientBuilder) {
+ this.awsKmsClientBuilder = awsKmsClientBuilder;
}
- public KmsClientSupplier build() {
+ public AwsKmsClientSupplier build() {
isTrue(allowedRegions.isEmpty() || excludedRegions.isEmpty(),
"Either allowed regions or excluded regions may be set, not both.");
@@ -104,18 +124,18 @@ public KmsClientSupplier build() {
}
if (credentialsProvider != null) {
- kmsClientBuilder = kmsClientBuilder.withCredentials(credentialsProvider);
+ awsKmsClientBuilder = awsKmsClientBuilder.withCredentials(credentialsProvider);
}
if (clientConfiguration != null) {
- kmsClientBuilder = kmsClientBuilder.withClientConfiguration(clientConfiguration);
+ awsKmsClientBuilder = awsKmsClientBuilder.withClientConfiguration(clientConfiguration);
}
if (regionId != null) {
- kmsClientBuilder = kmsClientBuilder.withRegion(regionId);
+ awsKmsClientBuilder = awsKmsClientBuilder.withRegion(regionId);
}
- AWSKMS client = kmsClientBuilder.build();
+ AWSKMS client = awsKmsClientBuilder.build();
if (clientCachingEnabled) {
client = newCachingProxy(client, regionId);
@@ -168,7 +188,8 @@ public Builder excludedRegions(Set regions) {
}
/**
- * When set to true, allows for the AWSKMS client for each region to be cached and reused.
+ * When set to false, disables the AWSKMS client for each region from being cached and reused.
+ * By default, client caching is enabled.
*
* @param enabled Whether or not caching is enabled.
*/
@@ -179,7 +200,7 @@ public Builder clientCaching(boolean enabled) {
/**
* Creates a proxy for the AWSKMS client that will populate the client into the client cache
- * after a KMS method successfully completes or a KMS exception occurs. This is to prevent a
+ * after an AWS KMS method successfully completes or an AWS KMS exception occurs. This is to prevent a
* a malicious user from causing a local resource DOS by sending ciphertext with a large number
* of spurious regions, thereby filling the cache with regions and exhausting resources.
*
@@ -194,13 +215,13 @@ private AWSKMS newCachingProxy(AWSKMS client, String regionId) {
(proxy, method, methodArgs) -> {
try {
final Object result = method.invoke(client, methodArgs);
- if (KMS_METHODS.contains(method.getName())) {
+ if (AWSKMS_METHODS.contains(method.getName())) {
clientsCache.put(regionId, client);
}
return result;
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof AWSKMSException &&
- KMS_METHODS.contains(method.getName())) {
+ AWSKMS_METHODS.contains(method.getName())) {
clientsCache.put(regionId, client);
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsCmkId.java b/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsCmkId.java
new file mode 100644
index 000000000..200308514
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsCmkId.java
@@ -0,0 +1,138 @@
+/*
+ * 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.encryptionsdk.kms;
+
+import com.amazonaws.arn.Arn;
+import com.amazonaws.encryptionsdk.exception.MalformedArnException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.util.Objects;
+
+/**
+ * A representation of an AWS KMS Customer Master Key Identifier, which may be one either a
+ * key ID, key Amazon Resource Name (ARN), alias name, or alias ARN.
+ */
+public final class AwsKmsCmkId {
+
+ private static final String ARN_PREFIX = "arn:";
+ private String keyId;
+
+ private AwsKmsCmkId(String keyId) throws MalformedArnException {
+ Validate.notBlank(keyId, "keyId must be neither null, empty nor whitespace");
+
+ if (keyId.startsWith(ARN_PREFIX)) {
+ try {
+ Arn.fromString(keyId);
+ } catch (IllegalArgumentException e) {
+ throw new MalformedArnException(e);
+ }
+ }
+
+ this.keyId = keyId;
+ }
+
+ /**
+ *
+ * Constructs an {@code AwsKmsCmkId} from the given String id.
+ *
+ *
+ * Valid identifiers must be either a key ID, key Amazon Resource Name (ARN), alias name, or alias ARN. When using
+ * an alias name, prefix it with "alias/". To specify a CMK in a different AWS account, you must use the key ARN or
+ * alias ARN. When using decryption operations, you must use the key ARN.
+ *
+ *
+ * For example:
+ *
+ *
+ *
+ *
+ * Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab
+ *
+ *
+ *
+ *
+ * Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
+ *
+ *
+ *
+ *
+ * Alias name: alias/ExampleAlias
+ *
+ *
+ *
+ *
+ * Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias
+ *
+ *
+ *
+ *
+ * @param keyId The key ID, key Amazon Resource Name (ARN), alias name, or alias ARN
+ * @return The {@code AwsKmsCmkId}
+ * @throws MalformedArnException if the given keyId is an ARN (starts with 'arn:') and cannot be parsed
+ */
+ public static AwsKmsCmkId fromString(String keyId) throws MalformedArnException {
+ return new AwsKmsCmkId(keyId);
+ }
+
+ /**
+ * Returns true if the given keyId is a well formed Amazon Resource Name or is a Key Alias or raw Key Id.
+ *
+ * @param keyId The key ID, key Amazon Resource Name (ARN), alias name, or alias ARN
+ * @return True if well formed, false otherwise
+ */
+ public static boolean isKeyIdWellFormed(String keyId) {
+ if (StringUtils.isBlank(keyId)) {
+ return false;
+ }
+
+ if (!keyId.startsWith(ARN_PREFIX)) {
+ return true;
+ }
+
+ try {
+ Arn.fromString(keyId);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if this AwsKmsCmkId is in the Amazon Resource Name (ARN) format.
+ *
+ * @return True if in the ARN format, false otherwise
+ */
+ public boolean isArn() {
+ return keyId.startsWith(ARN_PREFIX);
+ }
+
+ @Override
+ public String toString() {
+ return keyId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AwsKmsCmkId that = (AwsKmsCmkId) o;
+ return keyId.equals(that.keyId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(keyId);
+ }
+}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsDataKeyEncryptionDao.java b/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsDataKeyEncryptionDao.java
similarity index 84%
rename from src/main/java/com/amazonaws/encryptionsdk/kms/KmsDataKeyEncryptionDao.java
rename to src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsDataKeyEncryptionDao.java
index a00a1f1c7..cefbf97f3 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsDataKeyEncryptionDao.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/kms/AwsKmsDataKeyEncryptionDao.java
@@ -36,8 +36,8 @@
import java.util.Map;
import static com.amazonaws.encryptionsdk.EncryptedDataKey.PROVIDER_ENCODING;
-import static com.amazonaws.encryptionsdk.kms.KmsUtils.KMS_PROVIDER_ID;
-import static com.amazonaws.encryptionsdk.kms.KmsUtils.getClientByArn;
+import static com.amazonaws.encryptionsdk.internal.Constants.AWS_KMS_PROVIDER_ID;
+import static com.amazonaws.encryptionsdk.kms.AwsKmsClientSupplier.getClientByKeyId;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.Validate.isTrue;
@@ -46,12 +46,12 @@
* generation, encryption, and decryption of data keys. The KmsMethods interface is implemented
* to allow usage in KmsMasterKey.
*/
-class KmsDataKeyEncryptionDao implements DataKeyEncryptionDao, KmsMethods {
+class AwsKmsDataKeyEncryptionDao implements DataKeyEncryptionDao, KmsMethods {
- private final KmsClientSupplier clientSupplier;
+ private final AwsKmsClientSupplier clientSupplier;
private List grantTokens;
- KmsDataKeyEncryptionDao(KmsClientSupplier clientSupplier, List grantTokens) {
+ AwsKmsDataKeyEncryptionDao(AwsKmsClientSupplier clientSupplier, List grantTokens) {
requireNonNull(clientSupplier, "clientSupplier is required");
this.clientSupplier = clientSupplier;
@@ -59,7 +59,7 @@ class KmsDataKeyEncryptionDao implements DataKeyEncryptionDao, KmsMethods {
}
@Override
- public GenerateDataKeyResult generateDataKey(String keyId, CryptoAlgorithm algorithmSuite, Map encryptionContext) {
+ public GenerateDataKeyResult generateDataKey(AwsKmsCmkId keyId, CryptoAlgorithm algorithmSuite, Map encryptionContext) {
requireNonNull(keyId, "keyId is required");
requireNonNull(algorithmSuite, "algorithmSuite is required");
requireNonNull(encryptionContext, "encryptionContext is required");
@@ -67,10 +67,10 @@ public GenerateDataKeyResult generateDataKey(String keyId, CryptoAlgorithm algor
final com.amazonaws.services.kms.model.GenerateDataKeyResult kmsResult;
try {
- kmsResult = getClientByArn(keyId, clientSupplier)
+ kmsResult = getClientByKeyId(keyId, clientSupplier)
.generateDataKey(updateUserAgent(
new GenerateDataKeyRequest()
- .withKeyId(keyId)
+ .withKeyId(keyId.toString())
.withNumberOfBytes(algorithmSuite.getDataKeyLength())
.withEncryptionContext(encryptionContext)
.withGrantTokens(grantTokens)));
@@ -87,11 +87,11 @@ public GenerateDataKeyResult generateDataKey(String keyId, CryptoAlgorithm algor
kmsResult.getCiphertextBlob().get(encryptedKey);
return new GenerateDataKeyResult(new SecretKeySpec(rawKey, algorithmSuite.getDataKeyAlgo()),
- new KeyBlob(KMS_PROVIDER_ID, kmsResult.getKeyId().getBytes(PROVIDER_ENCODING), encryptedKey));
+ new KeyBlob(AWS_KMS_PROVIDER_ID, kmsResult.getKeyId().getBytes(PROVIDER_ENCODING), encryptedKey));
}
@Override
- public EncryptedDataKey encryptDataKey(final String keyId, SecretKey plaintextDataKey, Map encryptionContext) {
+ public EncryptedDataKey encryptDataKey(final AwsKmsCmkId keyId, SecretKey plaintextDataKey, Map encryptionContext) {
requireNonNull(keyId, "keyId is required");
requireNonNull(plaintextDataKey, "plaintextDataKey is required");
requireNonNull(encryptionContext, "encryptionContext is required");
@@ -100,9 +100,9 @@ public EncryptedDataKey encryptDataKey(final String keyId, SecretKey plaintextDa
final com.amazonaws.services.kms.model.EncryptResult kmsResult;
try {
- kmsResult = getClientByArn(keyId, clientSupplier)
+ kmsResult = getClientByKeyId(keyId, clientSupplier)
.encrypt(updateUserAgent(new EncryptRequest()
- .withKeyId(keyId)
+ .withKeyId(keyId.toString())
.withPlaintext(ByteBuffer.wrap(plaintextDataKey.getEncoded()))
.withEncryptionContext(encryptionContext)
.withGrantTokens(grantTokens)));
@@ -112,7 +112,7 @@ public EncryptedDataKey encryptDataKey(final String keyId, SecretKey plaintextDa
final byte[] encryptedDataKey = new byte[kmsResult.getCiphertextBlob().remaining()];
kmsResult.getCiphertextBlob().get(encryptedDataKey);
- return new KeyBlob(KMS_PROVIDER_ID, kmsResult.getKeyId().getBytes(PROVIDER_ENCODING), encryptedDataKey);
+ return new KeyBlob(AWS_KMS_PROVIDER_ID, kmsResult.getKeyId().getBytes(PROVIDER_ENCODING), encryptedDataKey);
}
@@ -126,7 +126,7 @@ public DecryptDataKeyResult decryptDataKey(EncryptedDataKey encryptedDataKey, Cr
final com.amazonaws.services.kms.model.DecryptResult kmsResult;
try {
- kmsResult = getClientByArn(providerInformation, clientSupplier)
+ kmsResult = getClientByKeyId(AwsKmsCmkId.fromString(providerInformation), clientSupplier)
.decrypt(updateUserAgent(new DecryptRequest()
.withCiphertextBlob(ByteBuffer.wrap(encryptedDataKey.getEncryptedDataKey()))
.withEncryptionContext(encryptionContext)
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/DataKeyEncryptionDao.java b/src/main/java/com/amazonaws/encryptionsdk/kms/DataKeyEncryptionDao.java
index 4267ba6f8..a68227b2b 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/kms/DataKeyEncryptionDao.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/kms/DataKeyEncryptionDao.java
@@ -31,7 +31,7 @@ public interface DataKeyEncryptionDao {
* @param encryptionContext The encryption context.
* @return GenerateDataKeyResult containing the plaintext data key and the encrypted data key.
*/
- GenerateDataKeyResult generateDataKey(String keyId, CryptoAlgorithm algorithmSuite, Map encryptionContext);
+ GenerateDataKeyResult generateDataKey(AwsKmsCmkId keyId, CryptoAlgorithm algorithmSuite, Map encryptionContext);
/**
* Encrypts the given plaintext data key using the customer aster key specified by the given keyId.
@@ -41,7 +41,7 @@ public interface DataKeyEncryptionDao {
* @param encryptionContext The encryption context.
* @return The encrypted data key.
*/
- EncryptedDataKey encryptDataKey(final String keyId, SecretKey plaintextDataKey, Map encryptionContext);
+ EncryptedDataKey encryptDataKey(final AwsKmsCmkId keyId, SecretKey plaintextDataKey, Map encryptionContext);
/**
* Decrypted the given encrypted data key.
@@ -61,8 +61,8 @@ public interface DataKeyEncryptionDao {
* @param grantTokens A list of grant tokens to supply to KMS
* @return The DataKeyEncryptionDao
*/
- static DataKeyEncryptionDao kms(KmsClientSupplier clientSupplier, List grantTokens) {
- return new KmsDataKeyEncryptionDao(clientSupplier, grantTokens);
+ static DataKeyEncryptionDao awsKms(AwsKmsClientSupplier clientSupplier, List grantTokens) {
+ return new AwsKmsDataKeyEncryptionDao(clientSupplier, grantTokens);
}
class GenerateDataKeyResult {
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKey.java b/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKey.java
index 60c69445c..7cea5cd58 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKey.java
@@ -30,6 +30,7 @@
import com.amazonaws.encryptionsdk.MasterKeyProvider;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
import com.amazonaws.services.kms.AWSKMS;
import static java.util.Collections.emptyList;
@@ -37,9 +38,12 @@
/**
* Represents a single Customer Master Key (CMK) and is used to encrypt/decrypt data with
* {@link AwsCrypto}.
+ *
+ * @deprecated Replaced by {@code KmsKeyring}. See {@link StandardKeyrings}.
*/
+@Deprecated
public final class KmsMasterKey extends MasterKey implements KmsMethods {
- private final KmsDataKeyEncryptionDao dataKeyEncryptionDao_;
+ private final AwsKmsDataKeyEncryptionDao dataKeyEncryptionDao_;
private final MasterKeyProvider sourceProvider_;
private final String id_;
@@ -63,10 +67,10 @@ public static KmsMasterKey getInstance(final AWSCredentialsProvider creds, final
static KmsMasterKey getInstance(final Supplier kms, final String id,
final MasterKeyProvider provider) {
- return new KmsMasterKey(new KmsDataKeyEncryptionDao(s -> kms.get(), emptyList()), id, provider);
+ return new KmsMasterKey(new AwsKmsDataKeyEncryptionDao(s -> kms.get(), emptyList()), id, provider);
}
- KmsMasterKey(final KmsDataKeyEncryptionDao dataKeyEncryptionDao, final String id, final MasterKeyProvider provider) {
+ KmsMasterKey(final AwsKmsDataKeyEncryptionDao dataKeyEncryptionDao, final String id, final MasterKeyProvider provider) {
dataKeyEncryptionDao_ = dataKeyEncryptionDao;
id_ = id;
sourceProvider_ = provider;
@@ -86,7 +90,7 @@ public String getKeyId() {
public DataKey generateDataKey(final CryptoAlgorithm algorithm,
final Map encryptionContext) {
final DataKeyEncryptionDao.GenerateDataKeyResult gdkResult = dataKeyEncryptionDao_.generateDataKey(
- getKeyId(), algorithm, encryptionContext);
+ AwsKmsCmkId.fromString(getKeyId()), algorithm, encryptionContext);
return new DataKey<>(gdkResult.getPlaintextDataKey(),
gdkResult.getEncryptedDataKey().getEncryptedDataKey(),
gdkResult.getEncryptedDataKey().getProviderInformation(),
@@ -113,7 +117,8 @@ public DataKey encryptDataKey(final CryptoAlgorithm algorithm,
final Map encryptionContext,
final DataKey> dataKey) {
final SecretKey key = dataKey.getKey();
- final EncryptedDataKey encryptedDataKey = dataKeyEncryptionDao_.encryptDataKey(id_, key, encryptionContext);
+ final EncryptedDataKey encryptedDataKey = dataKeyEncryptionDao_.encryptDataKey(
+ AwsKmsCmkId.fromString(id_), key, encryptionContext);
return new DataKey<>(dataKey.getKey(),
encryptedDataKey.getEncryptedDataKey(),
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKeyProvider.java b/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKeyProvider.java
index 9f0d91b42..36e48a635 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKeyProvider.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/kms/KmsMasterKeyProvider.java
@@ -43,6 +43,7 @@
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
+import com.amazonaws.encryptionsdk.keyrings.StandardKeyrings;
import com.amazonaws.handlers.RequestHandler2;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
@@ -54,7 +55,10 @@
/**
* Provides {@link MasterKey}s backed by the AWS Key Management Service. This object is regional and
* if you want to use keys from multiple regions, you'll need multiple copies of this object.
+ *
+ * @deprecated Replaced by {@code KmsKeyring}. See {@link StandardKeyrings}.
*/
+@Deprecated
public class KmsMasterKeyProvider extends MasterKeyProvider implements KmsMethods {
private static final String PROVIDER_NAME = "aws-kms";
private final List keyIds_;
diff --git a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsUtils.java b/src/main/java/com/amazonaws/encryptionsdk/kms/KmsUtils.java
deleted file mode 100644
index f6aab16eb..000000000
--- a/src/main/java/com/amazonaws/encryptionsdk/kms/KmsUtils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2019 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.encryptionsdk.kms;
-
-import com.amazonaws.arn.Arn;
-import com.amazonaws.encryptionsdk.exception.MalformedArnException;
-import com.amazonaws.services.kms.AWSKMS;
-
-public class KmsUtils {
-
- private static final String ALIAS_PREFIX = "alias/";
- private static final String ARN_PREFIX = "arn:";
- /**
- * The provider ID used for the KmsKeyring
- */
- public static final String KMS_PROVIDER_ID = "aws-kms";
-
- /**
- * Parses region from the given arn (if possible) and passes that region to the
- * given clientSupplier to produce an {@code AWSKMS} client.
- *
- * @param arn The Amazon Resource Name or Key Alias
- * @param clientSupplier The client supplier
- * @return AWSKMS The client
- * @throws MalformedArnException if the arn is malformed
- */
- public static AWSKMS getClientByArn(String arn, KmsClientSupplier clientSupplier) throws MalformedArnException {
- if (isKeyAlias(arn)) {
- return clientSupplier.getClient(null);
- }
-
- if(isArn(arn)) {
- try {
- return clientSupplier.getClient(Arn.fromString(arn).getRegion());
- } catch (IllegalArgumentException e) {
- throw new MalformedArnException(e);
- }
- }
-
- // Not an alias or an ARN, must be a raw Key ID
- return clientSupplier.getClient(null);
- }
-
- /**
- * Returns true if the given arn is a well formed Amazon Resource Name or Key Alias. Does
- * not return true for raw key IDs.
- *
- * @param arn The Amazon Resource Name or Key Alias
- * @return True if well formed, false otherwise
- */
- public static boolean isArnWellFormed(String arn) {
- if (isKeyAlias(arn)) {
- return true;
- }
-
- try {
- Arn.fromString(arn);
- return true;
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
-
- private static boolean isKeyAlias(String arn) {
- return arn.startsWith(ALIAS_PREFIX);
- }
-
- private static boolean isArn(String arn) {
- return arn.startsWith(ARN_PREFIX);
- }
-}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java b/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java
index 722246237..0ad194cc2 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java
@@ -19,7 +19,7 @@
public final class DecryptionMaterials {
private final CryptoAlgorithm algorithm;
private final Map