com.amazonaws.encryptionsdk.AwsCrypto Maven / Gradle / Ivy
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazonaws.encryptionsdk;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
import com.amazonaws.encryptionsdk.internal.*;
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
/**
* 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.
*
*
* The core concepts (and classes) in this SDK are:
*
* - {@link AwsCrypto}
*
- {@link DataKey}
*
- {@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.
*
*
* The main way to get a {@code MasterKey} is through the use of a {@link MasterKeyProvider}. This
* provides a common interface for the AwsEncryptionSdk to find and retrieve {@code MasterKeys}.
* (Some {@code MasterKeys} can also be constructed directly.)
*
*
* {@code AwsCrypto} uses the {@code MasterKeyProvider} to determine which {@code MasterKeys} should
* be used to encrypt the {@code DataKeys} by calling
* {@link MasterKeyProvider#getMasterKeysForEncryption(MasterKeyRequest)} . When more than one
* {@code MasterKey} is returned, the first {@code MasterKeys} is used to create the
* {@code DataKeys} by calling {@link MasterKey#generateDataKey(CryptoAlgorithm,java.util.Map)} .
* All of the other {@code MasterKeys} are then used to re-encrypt that {@code DataKey} with
* {@link MasterKey#encryptDataKey(CryptoAlgorithm,java.util.Map,DataKey)} . This list of
* {@link EncryptedDataKey EncryptedDataKeys} (the same {@code DataKey} possibly encrypted multiple
* times) is stored in the {@link com.amazonaws.encryptionsdk.model.CiphertextHeaders}.
*
*
* {@code AwsCrypto} also uses the {@code MasterKeyProvider} to decrypt one of the
* {@link EncryptedDataKey EncryptedDataKeys} from the header to retrieve the actual {@code DataKey}
* necessary to decrypt the message.
*
*
* Any place a {@code MasterKeyProvider} is used, a {@link MasterKey} can be used instead. The
* {@code MasterKey} will behave as a {@code MasterKeyProvider} which is only capable of providing
* itself. This is often useful when only one {@code MasterKey} is being used.
*
*
* Note regarding the use of generics: This library makes heavy use of generics to provide type
* safety to advanced developers. The great majority of users should be able to just use the
* provided type parameters or the {@code ?} wildcard.
*/
@SuppressWarnings("WeakerAccess") // this is a public API
public class AwsCrypto {
private static final Map EMPTY_MAP = Collections.emptyMap();
// These are volatile because we allow unsynchronized writes via our setters,
// and without setting volatile we could see strange results.
// E.g. copying these to a local might give different values on subsequent reads from the local.
// By setting them volatile we ensure that proper memory barriers are applied
// to ensure things behave in a sensible manner.
private volatile CryptoAlgorithm encryptionAlgorithm_ = null;
private volatile int encryptionFrameSize_ = getDefaultFrameSize();
private static final CommitmentPolicy DEFAULT_COMMITMENT_POLICY = CommitmentPolicy.RequireEncryptRequireDecrypt;
private final CommitmentPolicy commitmentPolicy_;
/**
* The maximum number of encrypted data keys to unwrap (resp. wrap) on decrypt (resp. encrypt), if positive.
* If zero, do not limit EDKs.
*/
private final int maxEncryptedDataKeys_;
private AwsCrypto(Builder builder) {
commitmentPolicy_ = builder.commitmentPolicy_ == null ? DEFAULT_COMMITMENT_POLICY : builder.commitmentPolicy_;
if (builder.encryptionAlgorithm_ != null && !commitmentPolicy_.algorithmAllowedForEncrypt(builder.encryptionAlgorithm_)) {
if (commitmentPolicy_ == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
throw new AwsCryptoException("Configuration conflict. Cannot encrypt due to CommitmentPolicy " +
commitmentPolicy_ + " requiring only non-committed messages. Algorithm ID was " +
builder.encryptionAlgorithm_ +
". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
} else {
throw new AwsCryptoException("Configuration conflict. Cannot encrypt due to CommitmentPolicy " +
commitmentPolicy_ + " requiring only committed messages. Algorithm ID was " +
builder.encryptionAlgorithm_ +
". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
}
}
encryptionAlgorithm_ = builder.encryptionAlgorithm_;
encryptionFrameSize_ = builder.encryptionFrameSize_;
maxEncryptedDataKeys_ = builder.maxEncryptedDataKeys_;
}
public static class Builder {
private CryptoAlgorithm encryptionAlgorithm_;
private int encryptionFrameSize_ = getDefaultFrameSize();
private CommitmentPolicy commitmentPolicy_;
private int maxEncryptedDataKeys_ = CiphertextHeaders.NO_MAX_ENCRYPTED_DATA_KEYS;
private Builder() {}
private Builder(final AwsCrypto client) {
encryptionAlgorithm_ = client.encryptionAlgorithm_;
encryptionFrameSize_ = client.encryptionFrameSize_;
commitmentPolicy_ = client.commitmentPolicy_;
maxEncryptedDataKeys_ = client.maxEncryptedDataKeys_;
}
/**
* Sets the {@link CryptoAlgorithm} to encrypt with.
* The Aws Crypto client will use the last crypto algorithm set with
* either {@link AwsCrypto.Builder#withEncryptionAlgorithm(CryptoAlgorithm)} or
* {@link #setEncryptionAlgorithm(CryptoAlgorithm)} to encrypt with.
*
* @param encryptionAlgorithm The {@link CryptoAlgorithm}
* @return The Builder, for method chaining
*/
public Builder withEncryptionAlgorithm(CryptoAlgorithm encryptionAlgorithm) {
this.encryptionAlgorithm_ = encryptionAlgorithm;
return this;
}
/**
* Sets the frame size of the encrypted messages that the Aws Crypto client produces.
* The Aws Crypto client will use the last frame size set with
* either {@link AwsCrypto.Builder#withEncryptionFrameSize(int)} or
* {@link #setEncryptionFrameSize(int)}.
*
* @param frameSize The frame size to produce encrypted messages with.
* @return The Builder, for method chaining
*/
public Builder withEncryptionFrameSize(int frameSize) {
this.encryptionFrameSize_ = frameSize;
return this;
}
/**
* Sets the {@link CommitmentPolicy} of this Aws Crypto client.
*
* @param commitmentPolicy The commitment policy to enforce during encryption and decryption
* @return The Builder, for method chaining
*/
public Builder withCommitmentPolicy(CommitmentPolicy commitmentPolicy) {
Utils.assertNonNull(commitmentPolicy, "commitmentPolicy");
this.commitmentPolicy_ = commitmentPolicy;
return this;
}
/**
* Sets the maximum number of encrypted data keys that this Aws Crypto client will wrap when
* encrypting, or unwrap when decrypting, a single message.
*
* @param maxEncryptedDataKeys The maximum number of encrypted data keys; must be positive
* @return The Builder, for method chaining
*/
public Builder withMaxEncryptedDataKeys(int maxEncryptedDataKeys) {
if (maxEncryptedDataKeys < 1) {
throw new IllegalArgumentException("maxEncryptedDataKeys must be positive");
}
this.maxEncryptedDataKeys_ = maxEncryptedDataKeys;
return this;
}
public AwsCrypto build() {
return new AwsCrypto(this);
}
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return new Builder(this);
}
public static AwsCrypto standard() {
return AwsCrypto.builder().build();
}
/**
* Returns the frame size to use for encryption when none is explicitly selected. Currently it
* is 4096.
*/
public static int getDefaultFrameSize() {
return 4096;
}
/**
* Sets the {@link CryptoAlgorithm} to use when encrypting data. This has no impact on
* decryption.
*/
public void setEncryptionAlgorithm(final CryptoAlgorithm alg) {
if (!commitmentPolicy_.algorithmAllowedForEncrypt(alg)) {
if (commitmentPolicy_ == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
throw new AwsCryptoException("Configuration conflict. Cannot encrypt due to CommitmentPolicy " +
commitmentPolicy_ + " requiring only non-committed messages. Algorithm ID was " +
alg + ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
} else {
throw new AwsCryptoException("Configuration conflict. Cannot encrypt due to CommitmentPolicy " +
commitmentPolicy_ + " requiring only committed messages. Algorithm ID was " +
alg + ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
}
}
encryptionAlgorithm_ = alg;
}
public CryptoAlgorithm getEncryptionAlgorithm() {
return encryptionAlgorithm_;
}
/**
* Sets the framing size to use when encrypting data. This has no impact on decryption.
* If {@code frameSize} is 0, then framing is disabled and the entire plaintext will be encrypted
* in a single block.
*
* Note that during encryption arrays of this size will be allocated. Using extremely large frame sizes may pose
* compatibility issues when the decryptor is running on 32-bit systems. Additionally, Java VM limits may set a
* platform-specific upper bound to frame sizes.
*/
public void setEncryptionFrameSize(final int frameSize) {
if (frameSize < 0) {
throw new IllegalArgumentException("frameSize must be non-negative");
}
encryptionFrameSize_ = frameSize;
}
public int getEncryptionFrameSize() {
return encryptionFrameSize_;
}
/**
* 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.
*
* This method is equivalent to calling {@link #estimateCiphertextSize(CryptoMaterialsManager, int, Map)} with a
* {@link DefaultCryptoMaterialsManager} based on the given provider.
*/
public > long estimateCiphertextSize(
final MasterKeyProvider provider,
final int plaintextSize,
final Map encryptionContext
) {
return estimateCiphertextSize(new DefaultCryptoMaterialsManager(provider), plaintextSize, encryptionContext);
}
/**
* 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.
*/
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)
.setCommitmentPolicy(commitmentPolicy_)
.build();
final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
getEncryptionFrameSize(),
checkAlgorithm(materialsManager.getMaterialsForEncrypt(request)),
commitmentPolicy_
);
return cryptoHandler.estimateOutputSize(plaintextSize);
}
/**
* Returns the equivalent to calling
* {@link #estimateCiphertextSize(MasterKeyProvider, int, Map)} with an empty
* {@code encryptionContext}.
*/
public > long estimateCiphertextSize(
final MasterKeyProvider provider,
final int plaintextSize
) {
return estimateCiphertextSize(provider, plaintextSize, EMPTY_MAP);
}
/**
* Returns the equivalent to calling
* {@link #estimateCiphertextSize(CryptoMaterialsManager, int, Map)} with an empty
* {@code encryptionContext}.
*/
public long estimateCiphertextSize(
final CryptoMaterialsManager materialsManager,
final int plaintextSize
) {
return estimateCiphertextSize(materialsManager, plaintextSize, EMPTY_MAP);
}
/**
* 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
* {@code provider}.
*
* This method is equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)} using a
* {@link DefaultCryptoMaterialsManager} based on the given provider.
*/
public > CryptoResult encryptData(
final MasterKeyProvider provider,
final byte[] plaintext,
final Map encryptionContext
) {
//noinspection unchecked
return (CryptoResult)
encryptData(new DefaultCryptoMaterialsManager(provider), plaintext, encryptionContext);
}
/**
* Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
* DataKeys} that are in turn protected by the given CryptoMaterialsProvider.
*/
public CryptoResult encryptData(
CryptoMaterialsManager materialsManager,
final byte[] plaintext,
final Map encryptionContext
) {
EncryptionMaterialsRequest request = EncryptionMaterialsRequest.newBuilder()
.setContext(encryptionContext)
.setRequestedAlgorithm(getEncryptionAlgorithm())
.setPlaintext(plaintext)
.setCommitmentPolicy(commitmentPolicy_)
.build();
EncryptionMaterials encryptionMaterials = checkMaxEncryptedDataKeys(checkAlgorithm(materialsManager.getMaterialsForEncrypt(request)));
final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
getEncryptionFrameSize(),
encryptionMaterials,
commitmentPolicy_
);
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());
}
/**
* Returns the equivalent to calling {@link #encryptData(MasterKeyProvider, byte[], Map)} with
* an empty {@code encryptionContext}.
*/
public > CryptoResult encryptData(final MasterKeyProvider provider,
final byte[] plaintext) {
return encryptData(provider, plaintext, EMPTY_MAP);
}
/**
* Returns the equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)} with
* an empty {@code encryptionContext}.
*/
public CryptoResult encryptData(
final CryptoMaterialsManager materialsManager,
final byte[] plaintext
) {
return encryptData(materialsManager, plaintext, EMPTY_MAP);
}
/**
* 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}
* 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.
*/
@Deprecated
public > CryptoResult encryptString(
final MasterKeyProvider provider,
final String plaintext,
final Map encryptionContext
) {
//noinspection unchecked
return (CryptoResult)
encryptString(new DefaultCryptoMaterialsManager(provider), plaintext, encryptionContext);
}
/**
* Calls {@link #encryptData(CryptoMaterialsManager, byte[], Map)} on the UTF-8 encoded bytes of
* {@code plaintext} and base64 encodes the result.
* @deprecated Use the {@link #encryptData(CryptoMaterialsManager, byte[], Map)} and
* {@link #decryptData(CryptoMaterialsManager, byte[])} 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.
*/
@Deprecated
public CryptoResult encryptString(
CryptoMaterialsManager materialsManager,
final String plaintext,
final Map encryptionContext
) {
final CryptoResult ctBytes = encryptData(
materialsManager,
plaintext.getBytes(StandardCharsets.UTF_8),
encryptionContext
);
return new CryptoResult<>(Utils.encodeBase64String(ctBytes.getResult()),
ctBytes.getMasterKeys(), ctBytes.getHeaders());
}
/**
* Returns the equivalent to calling {@link #encryptString(MasterKeyProvider, String, Map)} with
* an empty {@code encryptionContext}.
* @deprecated Use the {@link #encryptData(MasterKeyProvider, byte[])} and
* {@link #decryptData(MasterKeyProvider, byte[])} 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.
*/
@Deprecated
public > CryptoResult encryptString(final MasterKeyProvider provider,
final String plaintext) {
return encryptString(provider, plaintext, EMPTY_MAP);
}
/**
* Returns the equivalent to calling {@link #encryptString(CryptoMaterialsManager, String, Map)} with
* an empty {@code encryptionContext}.
* @deprecated Use the {@link #encryptData(CryptoMaterialsManager, byte[])} and
* {@link #decryptData(CryptoMaterialsManager, byte[])} 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.
*/
@Deprecated
public CryptoResult encryptString(
final CryptoMaterialsManager materialsManager,
final String plaintext
) {
return encryptString(materialsManager, plaintext, EMPTY_MAP);
}
/**
* 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}.
*/
public > CryptoResult decryptData(final MasterKeyProvider provider,
final byte[] ciphertext) {
return decryptData(Utils.assertNonNull(provider, "provider"), new
ParsedCiphertext(ciphertext, maxEncryptedDataKeys_));
}
/**
* Decrypts the provided ciphertext by delegating to the provided materialsManager to obtain the decrypted
* {@link DataKey}.
*
* @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.
*/
public CryptoResult decryptData(
final CryptoMaterialsManager materialsManager,
final byte[] ciphertext
) {
return decryptData(Utils.assertNonNull(materialsManager, "materialsManager"),
new ParsedCiphertext(ciphertext, maxEncryptedDataKeys_));
}
/**
* @see #decryptData(MasterKeyProvider, byte[])
*/
@SuppressWarnings("unchecked")
public > CryptoResult decryptData(
final MasterKeyProvider provider, final ParsedCiphertext ciphertext) {
Utils.assertNonNull(provider, "provider");
return (CryptoResult) decryptData(new DefaultCryptoMaterialsManager(provider), ciphertext);
}
/**
* @see #decryptData(CryptoMaterialsManager, byte[])
*/
public CryptoResult decryptData(
final CryptoMaterialsManager materialsManager,
final ParsedCiphertext ciphertext
) {
Utils.assertNonNull(materialsManager, "materialsManager");
final MessageCryptoHandler cryptoHandler =
DecryptionHandler.create(materialsManager, ciphertext, commitmentPolicy_,
SignaturePolicy.AllowEncryptAllowDecrypt, maxEncryptedDataKeys_);
final byte[] ciphertextBytes = ciphertext.getCiphertext();
final int contentLen = ciphertextBytes.length - ciphertext.getOffset();
final int outSizeEstimate = cryptoHandler.estimateOutputSize(contentLen);
final byte[] out = new byte[outSizeEstimate];
final ProcessingSummary processed = cryptoHandler.processBytes(ciphertextBytes, ciphertext.getOffset(),
contentLen, out,
0);
if (processed.getBytesProcessed() != contentLen) {
throw new BadCiphertextException("Unable to process entire ciphertext. May have trailing data.");
}
int outLen = processed.getBytesWritten();
outLen += cryptoHandler.doFinal(out, outLen);
final byte[] outBytes = Utils.truncate(out, outLen);
//noinspection unchecked
return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
}
/**
* Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
* UTF-8 encoded string.
*
* @see #decryptData(MasterKeyProvider, byte[])
* @deprecated Use the {@link #decryptData(MasterKeyProvider, byte[])} and
* {@link #encryptData(MasterKeyProvider, byte[], Map)} 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.
*/
@Deprecated
@SuppressWarnings("unchecked")
public > CryptoResult decryptString(
final MasterKeyProvider provider,
final String ciphertext
) {
return (CryptoResult) decryptString(new DefaultCryptoMaterialsManager(provider), ciphertext);
}
/**
* Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
* UTF-8 encoded string.
*
* @see #decryptData(CryptoMaterialsManager, byte[])
* @deprecated Use the {@link #decryptData(CryptoMaterialsManager, byte[])} and
* {@link #encryptData(CryptoMaterialsManager, byte[], Map)} 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.
*/
@Deprecated
public CryptoResult decryptString(final CryptoMaterialsManager provider,
final String ciphertext) {
Utils.assertNonNull(provider, "provider");
final byte[] ciphertextBytes;
try {
ciphertextBytes = Utils.decodeBase64String(Utils.assertNonNull(ciphertext, "ciphertext"));
} catch (final IllegalArgumentException ex) {
throw new BadCiphertextException("Invalid base 64", ex);
}
final CryptoResult ptBytes = decryptData(provider, ciphertextBytes);
//noinspection unchecked
return new CryptoResult(
new String(ptBytes.getResult(), StandardCharsets.UTF_8),
ptBytes.getMasterKeys(), ptBytes.getHeaders());
}
/**
* Returns a {@link CryptoOutputStream} which encrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
*
* @see #encryptData(MasterKeyProvider, byte[], Map)
* @see javax.crypto.CipherOutputStream
*/
public > CryptoOutputStream createEncryptingStream(
final MasterKeyProvider provider,
final OutputStream os,
final Map encryptionContext
) {
//noinspection unchecked
return (CryptoOutputStream)
createEncryptingStream(new DefaultCryptoMaterialsManager(provider), os, encryptionContext);
}
/**
* Returns a {@link CryptoOutputStream} which encrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
*
* @see #encryptData(MasterKeyProvider, byte[], Map)
* @see javax.crypto.CipherOutputStream
*/
public CryptoOutputStream> createEncryptingStream(
final CryptoMaterialsManager materialsManager,
final OutputStream os,
final Map encryptionContext
) {
return new CryptoOutputStream<>(os, getEncryptingStreamHandler(materialsManager, encryptionContext));
}
/**
* Returns the equivalent to calling
* {@link #createEncryptingStream(MasterKeyProvider, OutputStream, Map)} with an empty
* {@code encryptionContext}.
*/
public > CryptoOutputStream createEncryptingStream(
final MasterKeyProvider provider,
final OutputStream os) {
return createEncryptingStream(provider, os, EMPTY_MAP);
}
/**
* Returns the equivalent to calling
* {@link #createEncryptingStream(CryptoMaterialsManager, OutputStream, Map)} with an empty
* {@code encryptionContext}.
*/
public CryptoOutputStream> createEncryptingStream(
final CryptoMaterialsManager materialsManager,
final OutputStream os
) {
return createEncryptingStream(materialsManager, os, EMPTY_MAP);
}
/**
* 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
*/
public > CryptoInputStream createEncryptingStream(
final MasterKeyProvider provider,
final InputStream is,
final Map encryptionContext
) {
//noinspection unchecked
return (CryptoInputStream)
createEncryptingStream(new DefaultCryptoMaterialsManager(provider), is, encryptionContext);
}
/**
* 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
*/
public CryptoInputStream> createEncryptingStream(
CryptoMaterialsManager materialsManager,
final InputStream is,
final Map encryptionContext
) {
final MessageCryptoHandler cryptoHandler = getEncryptingStreamHandler(materialsManager, encryptionContext);
return new CryptoInputStream<>(is, cryptoHandler);
}
/**
* Returns the equivalent to calling
* {@link #createEncryptingStream(MasterKeyProvider, InputStream, Map)} with an empty
* {@code encryptionContext}.
*/
public > CryptoInputStream createEncryptingStream(
final MasterKeyProvider provider,
final InputStream is
) {
return createEncryptingStream(provider, is, EMPTY_MAP);
}
/**
* Returns the equivalent to calling
* {@link #createEncryptingStream(CryptoMaterialsManager, InputStream, Map)} with an empty
* {@code encryptionContext}.
*/
public CryptoInputStream> createEncryptingStream(
final CryptoMaterialsManager materialsManager,
final InputStream is
) {
return createEncryptingStream(materialsManager, is, EMPTY_MAP);
}
/**
* Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
* underlying {@link OutputStream}. This version only accepts unsigned messages.
*
* @see #decryptData(MasterKeyProvider, byte[])
* @see javax.crypto.CipherOutputStream
*/
public > CryptoOutputStream createUnsignedMessageDecryptingStream(
final MasterKeyProvider provider, final OutputStream os) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
commitmentPolicy_,
SignaturePolicy.AllowEncryptForbidDecrypt,
maxEncryptedDataKeys_);
return new CryptoOutputStream(os, cryptoHandler);
}
/**
* Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
* underlying {@link InputStream}. This version only accepts unsigned messages.
*
* @see #decryptData(MasterKeyProvider, byte[])
* @see javax.crypto.CipherInputStream
*/
public > CryptoInputStream createUnsignedMessageDecryptingStream(
final MasterKeyProvider provider, final InputStream is) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
commitmentPolicy_,
SignaturePolicy.AllowEncryptForbidDecrypt,
maxEncryptedDataKeys_);
return new CryptoInputStream(is, cryptoHandler);
}
/**
* Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
* underlying {@link OutputStream}. This version only accepts unsigned messages.
*
* @see #decryptData(CryptoMaterialsManager, byte[])
* @see javax.crypto.CipherOutputStream
*/
public CryptoOutputStream> createUnsignedMessageDecryptingStream(
final CryptoMaterialsManager materialsManager, final OutputStream os
) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
commitmentPolicy_,
SignaturePolicy.AllowEncryptForbidDecrypt,
maxEncryptedDataKeys_);
return new CryptoOutputStream(os, cryptoHandler);
}
/**
* Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
* underlying {@link InputStream}. This version only accepts unsigned messages.
*
* @see #encryptData(CryptoMaterialsManager, byte[], Map)
* @see javax.crypto.CipherInputStream
*/
public CryptoInputStream> createUnsignedMessageDecryptingStream(
final CryptoMaterialsManager materialsManager, final InputStream is
) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
commitmentPolicy_,
SignaturePolicy.AllowEncryptForbidDecrypt,
maxEncryptedDataKeys_);
return new CryptoInputStream(is, cryptoHandler);
}
/**
* Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
*
* Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
* after the decrypted plaintext has been released to the underlying {@link OutputStream}! This behavior can
* be avoided by using the non-streaming #decryptData(MasterKeyProvider, byte[]) method instead, or
* #createUnsignedMessageDecryptingStream(MasterKeyProvider, OutputStream) if you do not need to decrypt
* signed messages.
*
* @see #decryptData(MasterKeyProvider, byte[])
* @see #createUnsignedMessageDecryptingStream(MasterKeyProvider, OutputStream)
* @see javax.crypto.CipherOutputStream
*/
public > CryptoOutputStream createDecryptingStream(
final MasterKeyProvider provider, final OutputStream os) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
commitmentPolicy_,
SignaturePolicy.AllowEncryptAllowDecrypt,
maxEncryptedDataKeys_);
return new CryptoOutputStream(os, cryptoHandler);
}
/**
* Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
* underlying {@link InputStream}.
*
* Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
* after the decrypted plaintext has been produced from the {@link InputStream}! This behavior can
* be avoided by using the non-streaming #decryptData(MasterKeyProvider, byte[]) method instead, or
* #createUnsignedMessageDecryptingStream(MasterKeyProvider, InputStream) if you do not need to decrypt
* signed messages.
*
* @see #decryptData(MasterKeyProvider, byte[])
* @see #createUnsignedMessageDecryptingStream(MasterKeyProvider, InputStream)
* @see javax.crypto.CipherInputStream
*/
public > CryptoInputStream createDecryptingStream(
final MasterKeyProvider provider, final InputStream is) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
commitmentPolicy_,
SignaturePolicy.AllowEncryptAllowDecrypt,
maxEncryptedDataKeys_);
return new CryptoInputStream(is, cryptoHandler);
}
/**
* Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
* underlying {@link OutputStream}.
*
* Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
* after the decrypted plaintext has been released to the underlying {@link OutputStream}! This behavior can
* be avoided by using the non-streaming #decryptData(CryptoMaterialsManager, byte[]) method instead, or
* #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, OutputStream) if you do not need to decrypt
* signed messages.
*
* @see #decryptData(CryptoMaterialsManager, byte[])
* @see #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, OutputStream)
* @see javax.crypto.CipherOutputStream
*/
public CryptoOutputStream> createDecryptingStream(
final CryptoMaterialsManager materialsManager, final OutputStream os
) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
commitmentPolicy_,
SignaturePolicy.AllowEncryptAllowDecrypt,
maxEncryptedDataKeys_);
return new CryptoOutputStream(os, cryptoHandler);
}
/**
* Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
* underlying {@link InputStream}.
*
* Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
* after the decrypted plaintext has been produced from the {@link InputStream}! This behavior can
* be avoided by using the non-streaming #decryptData(CryptoMaterialsManager, byte[]) method instead, or
* #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, InputStream) if you do not need to decrypt
* signed messages.
*
* @see #decryptData(CryptoMaterialsManager, byte[])
* @see #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, InputStream)
* @see javax.crypto.CipherInputStream
*/
public CryptoInputStream> createDecryptingStream(
final CryptoMaterialsManager materialsManager, final InputStream is
) {
final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
commitmentPolicy_,
SignaturePolicy.AllowEncryptAllowDecrypt,
maxEncryptedDataKeys_);
return new CryptoInputStream(is, cryptoHandler);
}
private MessageCryptoHandler getEncryptingStreamHandler(
CryptoMaterialsManager materialsManager, Map encryptionContext
) {
Utils.assertNonNull(materialsManager, "materialsManager");
Utils.assertNonNull(encryptionContext, "encryptionContext");
EncryptionMaterialsRequest.Builder requestBuilder = EncryptionMaterialsRequest.newBuilder()
.setContext(encryptionContext)
.setRequestedAlgorithm(getEncryptionAlgorithm())
.setCommitmentPolicy(commitmentPolicy_);
return new LazyMessageCryptoHandler(info -> {
// Hopefully we know the input size now, so we can pass it along to the CMM.
if (info.getMaxInputSize() != -1) {
requestBuilder.setPlaintextSize(info.getMaxInputSize());
}
return new EncryptionHandler(
getEncryptionFrameSize(),
checkMaxEncryptedDataKeys(checkAlgorithm(materialsManager.getMaterialsForEncrypt(requestBuilder.build()))),
commitmentPolicy_
);
});
}
private EncryptionMaterials checkAlgorithm(EncryptionMaterials result) {
if (encryptionAlgorithm_ != null && result.getAlgorithm() != encryptionAlgorithm_) {
throw new AwsCryptoException(
String.format("Materials manager ignored requested algorithm; algorithm %s was set on AwsCrypto " +
"but %s was selected", encryptionAlgorithm_, result.getAlgorithm())
);
}
return result;
}
private EncryptionMaterials checkMaxEncryptedDataKeys(EncryptionMaterials materials) {
if (maxEncryptedDataKeys_ > 0 && materials.getEncryptedDataKeys().size() > maxEncryptedDataKeys_) {
throw new AwsCryptoException("Encrypted data keys exceed maxEncryptedDataKeys");
}
return materials;
}
}