// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.encryptionsdk.kms; import static com.amazonaws.encryptionsdk.internal.AwsKmsCmkArnInfo.parseInfoFromKeyArn; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import com.amazonaws.AmazonServiceException; import com.amazonaws.Request; import com.amazonaws.Response; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.encryptionsdk.CryptoAlgorithm; import com.amazonaws.encryptionsdk.DataKey; import com.amazonaws.encryptionsdk.EncryptedDataKey; import com.amazonaws.encryptionsdk.MasterKey; import com.amazonaws.encryptionsdk.MasterKeyProvider; import com.amazonaws.encryptionsdk.MasterKeyRequest; import com.amazonaws.encryptionsdk.exception.AwsCryptoException; import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException; import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException; import com.amazonaws.encryptionsdk.internal.AwsKmsCmkArnInfo; import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.kms.AWSKMSClientBuilder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; /** * 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. * *
This component is not multi-Region key aware, and will treat every AWS KMS identifier as
* regionally isolated.
*/
public class KmsMasterKeyProvider extends MasterKeyProvider If the default region is not specified, only full key ARNs will be usable.
*
* @param defaultRegion The default region to use.
* @return
*/
public Builder withDefaultRegion(String defaultRegion) {
this.defaultRegion_ = defaultRegion;
return this;
}
/**
* Provides a custom factory function that will vend KMS clients. This is provided for advanced
* use cases which require complete control over the client construction process.
*
* Because the regional client supplier fully controls the client construction process, it is
* not possible to configure the client through methods such as {@link
* #withCredentials(AWSCredentialsProvider)} or {@link #withClientBuilder(AWSKMSClientBuilder)};
* if you try to use these in combination, an {@link IllegalStateException} will be thrown.
*
* @param regionalClientSupplier
* @return
*/
public Builder withCustomClientFactory(RegionalClientSupplier regionalClientSupplier) {
if (templateBuilder_ != null) {
throw clientSupplierComboException();
}
regionalClientSupplier_ = regionalClientSupplier;
return this;
}
private RuntimeException clientSupplierComboException() {
return new IllegalStateException(
"withCustomClientFactory cannot be used in conjunction with "
+ "withCredentials or withClientBuilder");
}
/**
* Configures the {@link KmsMasterKeyProvider} to use specific credentials. If a builder was
* previously set, this will override whatever credentials it set.
*
* @param credentialsProvider
* @return
*/
public Builder withCredentials(AWSCredentialsProvider credentialsProvider) {
if (regionalClientSupplier_ != null) {
throw clientSupplierComboException();
}
if (templateBuilder_ == null) {
templateBuilder_ = AWSKMSClientBuilder.standard();
}
templateBuilder_.setCredentials(credentialsProvider);
return this;
}
/**
* Configures the {@link KmsMasterKeyProvider} to use specific credentials. If a builder was
* previously set, this will override whatever credentials it set.
*
* @param credentials
* @return
*/
public Builder withCredentials(AWSCredentials credentials) {
return withCredentials(new AWSStaticCredentialsProvider(credentials));
}
/**
* Configures the {@link KmsMasterKeyProvider} to use settings from this {@link
* AWSKMSClientBuilder} to configure KMS clients. Note that the region set on this builder will
* be ignored, but all other settings will be propagated into the regional clients.
*
* This method will overwrite any credentials set using {@link
* #withCredentials(AWSCredentialsProvider)}.
*
* @param builder
* @return
*/
public Builder withClientBuilder(AWSKMSClientBuilder builder) {
if (regionalClientSupplier_ != null) {
throw clientSupplierComboException();
}
final AWSKMSClientBuilder newBuilder = cloneClientBuilder(builder);
this.templateBuilder_ = newBuilder;
return this;
}
AWSKMSClientBuilder cloneClientBuilder(final AWSKMSClientBuilder builder) {
// We need to copy all arguments out of the builder in case it's mutated later on.
// Unfortunately AWSKMSClientBuilder doesn't support .clone() so we'll have to do it by hand.
if (builder.getEndpoint() != null) {
// We won't be able to set the region later if a custom endpoint is set.
throw new IllegalArgumentException(
"Setting endpoint configuration is not compatible with passing a "
+ "builder to the KmsMasterKeyProvider. Use withCustomClientFactory"
+ " instead.");
}
final AWSKMSClientBuilder newBuilder = AWSKMSClient.builder();
newBuilder.setClientConfiguration(builder.getClientConfiguration());
newBuilder.setCredentials(builder.getCredentials());
newBuilder.setEndpointConfiguration(builder.getEndpoint());
newBuilder.setMetricsCollector(builder.getMetricsCollector());
if (builder.getRequestHandlers() != null) {
newBuilder.setRequestHandlers(builder.getRequestHandlers().toArray(new RequestHandler2[0]));
}
return newBuilder;
}
/**
* Builds the master key provider in Discovery Mode. In Discovery Mode the KMS Master Key
* Provider will attempt to decrypt using any key identifier it discovers in the encrypted
* message. KMS Master Key Providers in Discovery Mode will not encrypt data keys.
*
* @return
*/
public KmsMasterKeyProvider buildDiscovery() {
final boolean isDiscovery = true;
RegionalClientSupplier supplier = clientFactory();
return new KmsMasterKeyProvider(
supplier, defaultRegion_, emptyList(), emptyList(), isDiscovery, discoveryFilter_);
}
/**
* Builds the master key provider in Discovery Mode with a {@link DiscoveryFilter}. In Discovery
* Mode the KMS Master Key Provider will attempt to decrypt using any key identifier it
* discovers in the encrypted message that is accepted by the {@code filter}. KMS Master Key
* Providers in Discovery Mode will not encrypt data keys.
*
* @param filter
* @return
*/
public KmsMasterKeyProvider buildDiscovery(DiscoveryFilter filter) {
if (filter == null) {
throw new IllegalArgumentException(
"Discovery filter must not be null if specifying " + "a discovery filter.");
}
discoveryFilter_ = filter;
return buildDiscovery();
}
/**
* Builds the master key provider in Strict Mode. KMS Master Key Providers in Strict Mode will
* only attempt to decrypt using key ARNs listed in {@code keyIds}. KMS Master Key Providers in
* Strict Mode will encrypt data keys using the keys listed in {@code keyIds}
*
* In Strict Mode, one or more CMKs must be provided. For providers that will only be used
* for encryption, you can use any valid KMS key identifier. For providers that will be used for
* decryption, you must use the key ARN; key ids, alias names, and alias ARNs are not supported.
*
* @param keyIds
* @return
*/
public KmsMasterKeyProvider buildStrict(List In Strict Mode, one or more CMKs must be provided. For providers that will only be used
* for encryption, you can use any valid KMS key identifier. For providers that will be used for
* decryption, you must use the key ARN; key ids, alias names, and alias ARNs are not supported.
*
* @param keyIds
* @return
*/
public KmsMasterKeyProvider buildStrict(String... keyIds) {
return buildStrict(asList(keyIds));
}
RegionalClientSupplier clientFactory() {
if (regionalClientSupplier_ != null) {
return regionalClientSupplier_;
}
// Clone again; this MKP builder might be reused to build a second MKP with different creds.
AWSKMSClientBuilder builder =
templateBuilder_ != null
? cloneClientBuilder(templateBuilder_)
: AWSKMSClientBuilder.standard();
ConcurrentHashMap