Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSCRTHTTPClient-9fe7b47.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS CRT HTTP Client",
"contributor": "",
"description": "Add `useTrustStoreSystemProperties` to `AwsCrtHttpClient.Builder` and `AwsCrtAsyncHttpClient.Builder`. When enabled, the client trusts the certificates in the trust store specified by the `javax.net.ssl.trustStore*` JSSE system properties instead of the OS trust store. Disabled by default."
}
12 changes: 12 additions & 0 deletions http-clients/aws-crt-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@
<version>${awsjavasdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle-bcpkix.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle-bcprov.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sdk-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,31 @@ AwsCrtAsyncHttpClient.Builder tcpKeepAliveConfiguration(Consumer<TcpKeepAliveCon
* @return this builder for method chaining
*/
AwsCrtAsyncHttpClient.Builder minTlsVersion(TlsVersion minTlsVersion);

/**
* Configure whether the client trusts the certificate authorities in the trust store specified by the JSSE system
* properties {@code javax.net.ssl.trustStore}, {@code javax.net.ssl.trustStorePassword},
* {@code javax.net.ssl.trustStoreType} and {@code javax.net.ssl.trustStoreProvider}, instead of the OS trust store.
*
* <p>
* It's disabled by default, and the client trusts the OS trust store even when these properties are set. When enabled,
* the properties are read once, when the client is built. If {@code javax.net.ssl.trustStore} is not set, the client
* trusts the OS trust store. The trust store also applies to the TLS connection to an HTTPS proxy. The trust store is not
* read when {@link SdkHttpConfigurationOption#TRUST_ALL_CERTIFICATES} is enabled.
*
* <p>
* Unlike the Apache, Netty, and URL connection HTTP clients, which use the JDK's TLS implementation, building the client
* fails with an {@link IllegalStateException} if the trust store cannot be loaded (for example, the file does not exist,
* the password is wrong, or the store contains no certificates or more than 1024 certificates) or if
* {@code javax.net.ssl.trustStore} is {@code NONE}, instead of falling back to a default trust store. Certificates are
* validated by the native TLS implementation, so JDK validation settings such as
* {@code jdk.certpath.disabledAlgorithms} do not apply.
*
* @param useTrustStoreSystemProperties whether to use the trust store specified by the JSSE system properties;
* {@code null} reverts to the default ({@code false})
* @return The builder of the method chaining.
*/
AwsCrtAsyncHttpClient.Builder useTrustStoreSystemProperties(Boolean useTrustStoreSystemProperties);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,31 @@ AwsCrtHttpClient.Builder tcpKeepAliveConfiguration(Consumer<TcpKeepAliveConfigur
* @return this builder for method chaining
*/
AwsCrtHttpClient.Builder minTlsVersion(TlsVersion minTlsVersion);

/**
* Configure whether the client trusts the certificate authorities in the trust store specified by the JSSE system
* properties {@code javax.net.ssl.trustStore}, {@code javax.net.ssl.trustStorePassword},
* {@code javax.net.ssl.trustStoreType} and {@code javax.net.ssl.trustStoreProvider}, instead of the OS trust store.
*
* <p>
* It's disabled by default, and the client trusts the OS trust store even when these properties are set. When enabled,
* the properties are read once, when the client is built. If {@code javax.net.ssl.trustStore} is not set, the client
* trusts the OS trust store. The trust store also applies to the TLS connection to an HTTPS proxy. The trust store is not
* read when {@link SdkHttpConfigurationOption#TRUST_ALL_CERTIFICATES} is enabled.
*
* <p>
* Unlike the Apache, Netty, and URL connection HTTP clients, which use the JDK's TLS implementation, building the client
* fails with an {@link IllegalStateException} if the trust store cannot be loaded (for example, the file does not exist,
* the password is wrong, or the store contains no certificates or more than 1024 certificates) or if
* {@code javax.net.ssl.trustStore} is {@code NONE}, instead of falling back to a default trust store. Certificates are
* validated by the native TLS implementation, so JDK validation settings such as
* {@code jdk.certpath.disabledAlgorithms} do not apply.
*
* @param useTrustStoreSystemProperties whether to use the trust store specified by the JSSE system properties;
* {@code null} reverts to the default ({@code false})
* @return The builder of the method chaining.
*/
AwsCrtHttpClient.Builder useTrustStoreSystemProperties(Boolean useTrustStoreSystemProperties);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.PROTOCOL;
import static software.amazon.awssdk.http.crt.internal.AwsCrtConfigurationUtils.buildSocketOptions;
import static software.amazon.awssdk.http.crt.internal.AwsCrtConfigurationUtils.buildTlsConnectionOptions;
import static software.amazon.awssdk.http.crt.internal.AwsCrtConfigurationUtils.overrideTrustStoreFromSystemProperties;
import static software.amazon.awssdk.http.crt.internal.AwsCrtConfigurationUtils.resolveCipherPreference;
import static software.amazon.awssdk.http.crt.internal.AwsCrtConfigurationUtils.resolveMinTlsVersion;
import static software.amazon.awssdk.http.crt.internal.AwsCrtConfigurationUtils.resolveMonitoringOptions;
Expand Down Expand Up @@ -112,16 +113,21 @@ abstract class AwsCrtHttpClientBase implements SdkAutoCloseable {
clientBootstrap = new ClientBootstrap(eventLoopGroup, null);
clientSocketOptions = buildSocketOptions(builder.getTcpKeepAliveConfiguration(),
config.get(SdkHttpConfigurationOption.CONNECTION_TIMEOUT));
boolean trustAllCertificates = config.get(SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES);
clientTlsContextOptions =
TlsContextOptions.createDefaultClient()
.withCipherPreference(resolveCipherPreference(builder.getPostQuantumTlsEnabled()))
.withMinimumTlsVersion(resolveMinTlsVersion(builder.getMinTlsVersion()))
.withVerifyPeer(!config.get(SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES));
.withVerifyPeer(!trustAllCertificates);
this.protocol = config.get(PROTOCOL);
if (protocol == Protocol.HTTP2) {
clientTlsContextOptions = clientTlsContextOptions.withAlpnList("h2");
}

if (Boolean.TRUE.equals(builder.getUseTrustStoreSystemProperties()) && !trustAllCertificates) {
overrideTrustStoreFromSystemProperties(clientTlsContextOptions);
}

this.tlsContextOptions = registerOwnedResource(clientTlsContextOptions);
clientTlsContext = new TlsContext(clientTlsContextOptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class AwsCrtClientBuilderBase<BuilderT> {
private Boolean postQuantumTlsEnabled;
private TlsVersion minTlsVersion;
private Integer numEventLoopThreads;
private Boolean useTrustStoreSystemProperties;

protected AwsCrtClientBuilderBase() {
}
Expand Down Expand Up @@ -165,6 +166,15 @@ public TlsVersion getMinTlsVersion() {
return this.minTlsVersion;
}

public BuilderT useTrustStoreSystemProperties(Boolean useTrustStoreSystemProperties) {
this.useTrustStoreSystemProperties = useTrustStoreSystemProperties;
return thisBuilder();
}

public Boolean getUseTrustStoreSystemProperties() {
return this.useTrustStoreSystemProperties;
}

public BuilderT proxyConfiguration(Consumer<ProxyConfiguration.Builder> proxyConfigurationBuilderConsumer) {
ProxyConfiguration.Builder builder = ProxyConfiguration.builder();
proxyConfigurationBuilderConsumer.accept(builder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.time.Duration;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.crt.CrtRuntimeException;
import software.amazon.awssdk.crt.http.HttpMonitoringOptions;
import software.amazon.awssdk.crt.io.SocketOptions;
import software.amazon.awssdk.crt.io.TlsCipherPreference;
Expand All @@ -38,6 +39,8 @@ public final class AwsCrtConfigurationUtils {
// CRT rejects a throughput-failure interval below two seconds (HttpMonitoringOptions).
private static final int MIN_MONITORING_FAILURE_INTERVAL_SECONDS = 2;

private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";

private AwsCrtConfigurationUtils() {
}

Expand Down Expand Up @@ -142,4 +145,16 @@ public static TlsContextOptions.TlsVersions resolveMinTlsVersion(TlsVersion minT
}
}

/**
* Replaces the OS trust store in {@code tlsContextOptions} with the trust store specified by the JSSE trust store system
* properties. The CRT keeps the OS trust store when {@code javax.net.ssl.trustStore} is unset or empty.
*/
public static void overrideTrustStoreFromSystemProperties(TlsContextOptions tlsContextOptions) {
try {
tlsContextOptions.overrideDefaultTrustStoreFromJavaSystemProperties();
} catch (CrtRuntimeException | IllegalArgumentException e) {
throw new IllegalStateException("Unable to load the trust store specified by the " + TRUST_STORE_PROPERTY
+ " system property: " + e.getMessage(), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 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 software.amazon.awssdk.http.crt;

import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES;
import static software.amazon.awssdk.http.crt.CrtHttpClientTestUtils.createRequest;

import java.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.http.RecordingResponseHandler;
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.utils.AttributeMap;

class AwsCrtAsyncHttpClientTrustStoreSystemPropertiesTest
extends CrtTrustStoreSystemPropertiesTestSuite<SdkAsyncHttpClient> {

@Override
protected SdkAsyncHttpClient buildClient(Boolean useTrustStoreSystemProperties) {
return AwsCrtAsyncHttpClient.builder()
.useTrustStoreSystemProperties(useTrustStoreSystemProperties)
.connectionAcquisitionTimeout(CONNECTION_ACQUISITION_TIMEOUT)
.build();
}

@Override
protected SdkAsyncHttpClient buildClientTrustingAllCertificates(Boolean useTrustStoreSystemProperties) {
return AwsCrtAsyncHttpClient.builder()
.useTrustStoreSystemProperties(useTrustStoreSystemProperties)
.buildWithDefaults(AttributeMap.builder().put(TRUST_ALL_CERTIFICATES, true).build());
}

@Override
protected SdkAsyncHttpClient buildClientWithPrivateEventLoopGroup(Boolean useTrustStoreSystemProperties) {
return AwsCrtAsyncHttpClient.builder()
.numEventLoopThreads(2)
.useTrustStoreSystemProperties(useTrustStoreSystemProperties)
.build();
}

@Override
protected GetResponse executeGet(SdkAsyncHttpClient client, URI uri) throws Exception {
RecordingResponseHandler recorder = new RecordingResponseHandler();
CompletableFuture<Void> execution = client.execute(AsyncExecuteRequest.builder()
.request(createRequest(uri))
.requestContentPublisher(new EmptyPublisher())
.responseHandler(recorder)
.build());
try {
execution.get(30, TimeUnit.SECONDS);
recorder.completeFuture().get(30, TimeUnit.SECONDS);
} catch (ExecutionException e) {
throw (Exception) e.getCause();
}
return new GetResponse(recorder.responses().get(0).statusCode(), recorder.fullResponseAsString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 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 software.amazon.awssdk.http.crt;

import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES;
import static software.amazon.awssdk.http.crt.CrtHttpClientTestUtils.createRequest;

import java.net.URI;
import software.amazon.awssdk.http.AbortableInputStream;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.IoUtils;

class AwsCrtHttpClientTrustStoreSystemPropertiesTest extends CrtTrustStoreSystemPropertiesTestSuite<SdkHttpClient> {

@Override
protected SdkHttpClient buildClient(Boolean useTrustStoreSystemProperties) {
return AwsCrtHttpClient.builder()
.useTrustStoreSystemProperties(useTrustStoreSystemProperties)
.connectionAcquisitionTimeout(CONNECTION_ACQUISITION_TIMEOUT)
.build();
}

@Override
protected SdkHttpClient buildClientTrustingAllCertificates(Boolean useTrustStoreSystemProperties) {
return AwsCrtHttpClient.builder()
.useTrustStoreSystemProperties(useTrustStoreSystemProperties)
.buildWithDefaults(AttributeMap.builder().put(TRUST_ALL_CERTIFICATES, true).build());
}

@Override
protected SdkHttpClient buildClientWithPrivateEventLoopGroup(Boolean useTrustStoreSystemProperties) {
return AwsCrtHttpClient.builder()
.numEventLoopThreads(2)
.useTrustStoreSystemProperties(useTrustStoreSystemProperties)
.build();
}

@Override
protected GetResponse executeGet(SdkHttpClient client, URI uri) throws Exception {
HttpExecuteResponse response = client.prepareRequest(HttpExecuteRequest.builder()
.request(createRequest(uri))
.build())
.call();
try (AbortableInputStream body = response.responseBody().orElseThrow(IllegalStateException::new)) {
return new GetResponse(response.httpResponse().statusCode(), IoUtils.toUtf8String(body));
}
}
}
Loading
Loading