Skip to content

Incorporate Keyrings into AwsCrypto and deprecate MasterKeyProviders. #151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
Expand Down Expand Up @@ -197,7 +197,7 @@
</profile>

<profile>
<id>full-test-suite</id>
<id>test-suite</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
Expand All @@ -208,30 +208,50 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<includes>
<include>**/AllTestsSuite.java</include>
</includes>
<excludedGroups>ad_hoc</excludedGroups>
</configuration>
</plugin>
</plugins>
</build>
</profile>

<!-- This test profile is intended to assist in rapid development; it filters out some of the slower,
more exhaustive tests in the overall test suite to allow for a rapid edit-test cycle. -->
<profile>
<id>fast-tests-only</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<includes>
<include>**/FastTestsOnlySuite.java</include>
</includes>
<excludedGroups>ad_hoc, integration</excludedGroups>
<systemPropertyVariables>
<fastTestsOnly>true</fastTestsOnly>
</systemPropertyVariables>
<!-- Require that this fast suite completes relatively quickly. If you're seeing
this timeout get hit, it's time to pare down tests some more. As a general rule of
thumb, we should avoid any single test taking more than 10s, and try to keep the
number of such slow tests to a minimum. -->
<forkedProcessTimeoutInSeconds>120</forkedProcessTimeoutInSeconds>
</configuration>
</plugin>
</plugins>
</build>
</profile>

<!-- This test profile will run only the integration tests. -->
<profile>
<id>integration</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<groups>integration</groups>
</configuration>
</plugin>
</plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private static byte[] standardEncrypt(final AwsKmsCmkId kmsArn, final PublicKey

// 3. Instantiate a RawRsaKeyring
// Because the user does not have access to the private escrow key,
// they pass in "null" for the private key parameter.
// they do not provide the private key parameter.
final Keyring rsaKeyring = StandardKeyrings.rawRsa()
.keyNamespace("Escrow")
.keyName("Escrow")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static void encryptAndDecrypt(final File srcFile, final File encryptedFile, fina
}

// 7. Create the decrypting input stream with the keyring.
try(final AwsCryptoInputStream decryptingStream = crypto.createDecryptingInputStream(
try (final AwsCryptoInputStream decryptingStream = crypto.createDecryptingInputStream(
CreateDecryptingInputStreamRequest.builder()
.keyring(keyring)
.inputStream(new FileInputStream(encryptedFile)).build())) {
Expand Down Expand Up @@ -143,7 +143,6 @@ private static void compareFiles(File file1, File file2) throws IOException {
(file2Line = file2Reader.readLine()) != null) {
assert Objects.equals(file1Line, file2Line);
}

}
}

Expand Down
111 changes: 109 additions & 2 deletions src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -54,8 +55,8 @@
* 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 a copy of the {@code EncryptedDataKey} protected by a usable {@code MasterKey} or {@code Keyring},
* decrypting the {@code EncryptedDataKey}, and then decrypting the message.
* 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.
*
* <p>
* Note:
Expand Down Expand Up @@ -255,6 +256,21 @@ public long estimateCiphertextSize(final EstimateCiphertextSizeRequest request)
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.
* <p>
* This is a convenience which creates an instance of the {@link EstimateCiphertextSizeRequest.Builder} avoiding the need to
* create one manually via {@link EstimateCiphertextSizeRequest#builder()}
* </p>
*
* @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<EstimateCiphertextSizeRequest.Builder> 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
Expand Down Expand Up @@ -355,6 +371,21 @@ public AwsCryptoResult<byte[]> encrypt(final EncryptRequest request) {
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}.
* <p>
* This is a convenience which creates an instance of the {@link EncryptRequest.Builder} avoiding the need to
* create one manually via {@link EncryptRequest#builder()}
* </p>
*
* @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<byte[]> encrypt(final Consumer<EncryptRequest.Builder> 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.
Expand Down Expand Up @@ -525,6 +556,21 @@ public AwsCryptoResult<byte[]> decrypt(final DecryptRequest request) {
cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
}

/**
* Decrypts the provided {@link ParsedCiphertext} using the {@link CryptoMaterialsManager} or the {@link Keyring}
* specified in the {@link DecryptRequest}.
* <p>
* This is a convenience which creates an instance of the {@link DecryptRequest.Builder} avoiding the need to
* create one manually via {@link DecryptRequest#builder()}
* </p>
*
* @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<byte[]> decrypt(final Consumer<DecryptRequest.Builder> request) {
return decrypt(DecryptRequest.builder().applyMutation(request).build());
}

/**
* Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
* UTF-8 encoded string.
Expand Down Expand Up @@ -658,6 +704,21 @@ public AwsCryptoOutputStream createEncryptingOutputStream(final CreateEncrypting
request.cryptoMaterialsManager(), request.encryptionContext()));
}

/**
* Returns a {@link AwsCryptoOutputStream} which encrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
* <p>
* This is a convenience which creates an instance of the {@link CreateEncryptingOutputStreamRequest.Builder} avoiding the need to
* create one manually via {@link CreateEncryptingOutputStreamRequest#builder()}
* </p>
*
* @see #encrypt(EncryptRequest)
* @see javax.crypto.CipherOutputStream
*/
public AwsCryptoOutputStream createEncryptingOutputStream(final Consumer<CreateEncryptingOutputStreamRequest.Builder> request) {
return createEncryptingOutputStream(CreateEncryptingOutputStreamRequest.builder().applyMutation(request).build());
}

/**
* Returns a {@link CryptoInputStream} which encrypts the data after reading it from the
* underlying {@link InputStream}.
Expand Down Expand Up @@ -748,6 +809,21 @@ public AwsCryptoInputStream createEncryptingInputStream(final CreateEncryptingIn
return new AwsCryptoInputStream(request.inputStream(), cryptoHandler);
}

/**
* Returns a {@link AwsCryptoInputStream} which encrypts the data after reading it from the
* underlying {@link InputStream}.
* <p>
* This is a convenience which creates an instance of the {@link CreateEncryptingInputStreamRequest.Builder} avoiding the need to
* create one manually via {@link CreateEncryptingInputStreamRequest#builder()}
* </p>
*
* @see #encrypt(EncryptRequest)
* @see javax.crypto.CipherInputStream
*/
public AwsCryptoInputStream createEncryptingInputStream(final Consumer<CreateEncryptingInputStreamRequest.Builder> request) {
return createEncryptingInputStream(CreateEncryptingInputStreamRequest.builder().applyMutation(request).build());
}

/**
* Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
Expand Down Expand Up @@ -812,6 +888,22 @@ public AwsCryptoOutputStream createDecryptingOutputStream(final CreateDecrypting
return new AwsCryptoOutputStream(request.outputStream(), cryptoHandler);
}


/**
* Returns a {@link AwsCryptoOutputStream} which decrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
* <p>
* This is a convenience which creates an instance of the {@link CreateDecryptingOutputStreamRequest.Builder} avoiding the need to
* create one manually via {@link CreateDecryptingOutputStreamRequest#builder()}
* </p>
*
* @see #decrypt(DecryptRequest)
* @see javax.crypto.CipherOutputStream
*/
public AwsCryptoOutputStream createDecryptingOutputStream(final Consumer<CreateDecryptingOutputStreamRequest.Builder> request) {
return createDecryptingOutputStream(CreateDecryptingOutputStreamRequest.builder().applyMutation(request).build());
}

/**
* Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
* underlying {@link InputStream}.
Expand Down Expand Up @@ -844,6 +936,21 @@ public AwsCryptoInputStream createDecryptingInputStream(final CreateDecryptingIn
return new AwsCryptoInputStream(request.inputStream(), cryptoHandler);
}

/**
* Returns a {@link AwsCryptoInputStream} which decrypts the data after reading it from the
* underlying {@link InputStream}.
* <p>
* This is a convenience which creates an instance of the {@link CreateDecryptingInputStreamRequest.Builder} avoiding the need to
* create one manually via {@link CreateDecryptingInputStreamRequest#builder()}
* </p>
*
* @see #encrypt(EncryptRequest)
* @see javax.crypto.CipherInputStream
*/
public AwsCryptoInputStream createDecryptingInputStream(final Consumer<CreateDecryptingInputStreamRequest.Builder> request) {
return createDecryptingInputStream(CreateDecryptingInputStreamRequest.builder().applyMutation(request).build());
}

private MessageCryptoHandler getEncryptingStreamHandler(
CryptoMaterialsManager materialsManager, Map<String, String> encryptionContext
) {
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/com/amazonaws/encryptionsdk/AwsCryptoRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
* specific language governing permissions and limitations under the License.
*/

package com.amazonaws.encryptionsdk;import com.amazonaws.encryptionsdk.keyrings.Keyring;
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;
Expand Down Expand Up @@ -65,5 +69,10 @@ public T keyring(Keyring keyring) {
}

abstract T getThis();

T applyMutation(Consumer<T> mutator) {
mutator.accept(getThis());
return getThis();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public Map<String, String> getEncryptionContext() {
}

/**
* Convenience method equivalent to {@link #getHeaders()}.{@code getCryptoAlgoId()}.
* Convenience method equivalent to {@link #getHeaders()}{@code .getCryptoAlgoId()}.
*/
public CryptoAlgorithm getAlgorithmSuite() {
return headers.getCryptoAlgoId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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;
Expand Down Expand Up @@ -111,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 <K extends MasterKey<K>> DecryptionHandler<K> create(
final MasterKeyProvider<K> customerMasterKeyProvider
) throws AwsCryptoException {
Expand All @@ -136,8 +140,11 @@ public static <K extends MasterKey<K>> DecryptionHandler<K> 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 <K extends MasterKey<K>> DecryptionHandler<K> create(
final MasterKeyProvider<K> customerMasterKeyProvider, final CiphertextHeaders headers
) throws AwsCryptoException {
Expand Down Expand Up @@ -537,7 +544,15 @@ 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<K> getMasterKeys() {
if(dataKey_.getMasterKey() == null) {
return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
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;
Expand Down Expand Up @@ -408,8 +410,19 @@ 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 key 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
}
Expand Down
Loading