Skip to content

Commit 7b4d85f

Browse files
committed
Enable client TLS auth for Netty
This commit enables customers to configure a TlsKeyManagersProvider on the Netty client which will be used to authenticate the client during client TLS authentication.
1 parent fd8387d commit 7b4d85f

File tree

7 files changed

+413
-0
lines changed

7 files changed

+413
-0
lines changed

http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
import software.amazon.awssdk.http.Protocol;
4949
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
5050
import software.amazon.awssdk.http.SdkHttpRequest;
51+
import software.amazon.awssdk.http.SystemPropertyTlsKeyManagersProvider;
52+
import software.amazon.awssdk.http.TlsKeyManagersProvider;
5153
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
5254
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
5355
import software.amazon.awssdk.http.nio.netty.internal.AwaitCloseChannelPoolMap;
@@ -92,6 +94,7 @@ private NettyNioAsyncHttpClient(DefaultBuilder builder, AttributeMap serviceDefa
9294
.sdkEventLoopGroup(sdkEventLoopGroup)
9395
.sslProvider(resolveSslProvider(builder))
9496
.proxyConfiguration(builder.proxyConfiguration)
97+
.tlsKeyManagersProvider(builder.tlsKeyManagersProvider)
9598
.build();
9699
}
97100

@@ -354,6 +357,17 @@ public interface Builder extends SdkAsyncHttpClient.Builder<NettyNioAsyncHttpCli
354357
* @see ProxyConfiguration#nonProxyHosts()
355358
*/
356359
Builder proxyConfiguration(ProxyConfiguration proxyConfiguration);
360+
361+
/**
362+
* Set the {@link TlsKeyManagersProvider} for this client. The {@code KeyManager}s will be used by the client to
363+
* authenticate itself with the remote server if necessary when establishing the TLS connection.
364+
* <p>
365+
* By default the configured provider is {@link SystemPropertyTlsKeyManagersProvider}.
366+
*
367+
* @param keyManagersProvider The {@code TlsKeyManagersProvider}.
368+
* @return The builder for method chaining.
369+
*/
370+
Builder tlsKeyManagersProvider(TlsKeyManagersProvider keyManagersProvider);
357371
}
358372

359373
/**
@@ -371,6 +385,7 @@ private static final class DefaultBuilder implements Builder {
371385
private Integer maxHttp2Streams;
372386
private SslProvider sslProvider;
373387
private ProxyConfiguration proxyConfiguration;
388+
private TlsKeyManagersProvider tlsKeyManagersProvider = SystemPropertyTlsKeyManagersProvider.create();
374389

375390
private DefaultBuilder() {
376391
}
@@ -537,6 +552,12 @@ public void setProxyConfiguration(ProxyConfiguration proxyConfiguration) {
537552
proxyConfiguration(proxyConfiguration);
538553
}
539554

555+
@Override
556+
public Builder tlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider) {
557+
this.tlsKeyManagersProvider = tlsKeyManagersProvider;
558+
return this;
559+
}
560+
540561
@Override
541562
public SdkAsyncHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
542563
return new NettyNioAsyncHttpClient(this, standardOptions.build()

http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@
4040
import java.util.concurrent.TimeUnit;
4141
import java.util.concurrent.TimeoutException;
4242
import java.util.concurrent.atomic.AtomicReference;
43+
import javax.net.ssl.KeyManager;
44+
import javax.net.ssl.KeyManagerFactory;
4345
import javax.net.ssl.SSLException;
4446
import javax.net.ssl.TrustManagerFactory;
4547
import software.amazon.awssdk.annotations.SdkInternalApi;
4648
import software.amazon.awssdk.annotations.SdkTestInternalApi;
4749
import software.amazon.awssdk.http.Protocol;
50+
import software.amazon.awssdk.http.TlsKeyManagersProvider;
4851
import software.amazon.awssdk.http.nio.netty.ProxyConfiguration;
4952
import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup;
5053
import software.amazon.awssdk.http.nio.netty.internal.http2.HttpOrHttp2ChannelPool;
@@ -82,6 +85,7 @@ public void channelCreated(Channel ch) throws Exception {
8285
private final long maxStreams;
8386
private final SslProvider sslProvider;
8487
private final ProxyConfiguration proxyConfiguration;
88+
private final TlsKeyManagersProvider tlsKeyManagersProvider;
8589

8690
private AwaitCloseChannelPoolMap(Builder builder) {
8791
this.sdkChannelOptions = builder.sdkChannelOptions;
@@ -91,6 +95,7 @@ private AwaitCloseChannelPoolMap(Builder builder) {
9195
this.maxStreams = builder.maxStreams;
9296
this.sslProvider = builder.sslProvider;
9397
this.proxyConfiguration = builder.proxyConfiguration;
98+
this.tlsKeyManagersProvider = builder.tlsKeyManagersProvider;
9499
}
95100

96101
@SdkTestInternalApi
@@ -260,6 +265,7 @@ private SslContext sslContext(URI targetAddress) {
260265
.sslProvider(sslProvider)
261266
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
262267
.trustManager(getTrustManager())
268+
.keyManager(getKeyManager())
263269
.build();
264270
} catch (SSLException e) {
265271
throw new RuntimeException(e);
@@ -270,6 +276,16 @@ private TrustManagerFactory getTrustManager() {
270276
return configuration.trustAllCertificates() ? InsecureTrustManagerFactory.INSTANCE : null;
271277
}
272278

279+
private KeyManagerFactory getKeyManager() {
280+
if (tlsKeyManagersProvider != null) {
281+
KeyManager[] keyManagers = tlsKeyManagersProvider.keyManagers();
282+
if (keyManagers != null) {
283+
return StaticKeyManagerFactory.create(keyManagers);
284+
}
285+
}
286+
return null;
287+
}
288+
273289
public static class Builder {
274290

275291
private SdkChannelOptions sdkChannelOptions;
@@ -279,6 +295,7 @@ public static class Builder {
279295
private long maxStreams;
280296
private SslProvider sslProvider;
281297
private ProxyConfiguration proxyConfiguration;
298+
private TlsKeyManagersProvider tlsKeyManagersProvider;
282299

283300
private Builder() {
284301
}
@@ -318,6 +335,11 @@ public Builder proxyConfiguration(ProxyConfiguration proxyConfiguration) {
318335
return this;
319336
}
320337

338+
public Builder tlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider) {
339+
this.tlsKeyManagersProvider = tlsKeyManagersProvider;
340+
return this;
341+
}
342+
321343
public AwaitCloseChannelPoolMap build() {
322344
return new AwaitCloseChannelPoolMap(this);
323345
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.nio.netty.internal;
17+
18+
import javax.net.ssl.KeyManager;
19+
import javax.net.ssl.KeyManagerFactory;
20+
import software.amazon.awssdk.annotations.SdkInternalApi;
21+
22+
/**
23+
* Factory that simply returns a statically provided set of {@link KeyManager}s.
24+
*/
25+
@SdkInternalApi
26+
public final class StaticKeyManagerFactory extends KeyManagerFactory {
27+
private StaticKeyManagerFactory(KeyManager[] keyManagers) {
28+
super(new StaticKeyManagerFactorySpi(keyManagers), null, null);
29+
}
30+
31+
public static StaticKeyManagerFactory create(KeyManager[] keyManagers) {
32+
return new StaticKeyManagerFactory(keyManagers);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.nio.netty.internal;
17+
18+
import java.security.KeyStore;
19+
import javax.net.ssl.KeyManager;
20+
import javax.net.ssl.KeyManagerFactorySpi;
21+
import javax.net.ssl.ManagerFactoryParameters;
22+
import software.amazon.awssdk.annotations.SdkInternalApi;
23+
import software.amazon.awssdk.utils.Validate;
24+
25+
26+
/**
27+
* Factory SPI that simply returns a statically provided set of {@link KeyManager}s.
28+
*/
29+
@SdkInternalApi
30+
public final class StaticKeyManagerFactorySpi extends KeyManagerFactorySpi {
31+
private final KeyManager[] keyManagers;
32+
33+
public StaticKeyManagerFactorySpi(KeyManager[] keyManagers) {
34+
Validate.paramNotNull(keyManagers, "keyManagers");
35+
this.keyManagers = new KeyManager[keyManagers.length];
36+
System.arraycopy(keyManagers, 0, this.keyManagers, 0, keyManagers.length);
37+
}
38+
39+
@Override
40+
protected void engineInit(KeyStore ks, char[] password) {
41+
}
42+
43+
@Override
44+
protected void engineInit(ManagerFactoryParameters spec) {
45+
}
46+
47+
@Override
48+
protected KeyManager[] engineGetKeyManagers() {
49+
return keyManagers;
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.nio.netty;
17+
18+
import java.io.IOException;
19+
import java.io.InputStream;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
import org.junit.AfterClass;
23+
import org.junit.BeforeClass;
24+
25+
abstract class ClientTlsAuthTestBase {
26+
protected static final String STORE_PASSWORD = "password";
27+
protected static final String CLIENT_STORE_TYPE = "pkcs12";
28+
protected static final String TEST_KEY_STORE = "/software/amazon/awssdk/http/netty/server-keystore";
29+
protected static final String CLIENT_KEY_STORE = "/software/amazon/awssdk/http/netty/client1.p12";
30+
31+
protected static Path tempDir;
32+
protected static Path serverKeyStore;
33+
protected static Path clientKeyStore;
34+
35+
@BeforeClass
36+
public static void setUp() throws IOException {
37+
tempDir = Files.createTempDirectory(ClientTlsAuthTestBase.class.getSimpleName());
38+
copyCertsToTmpDir();
39+
}
40+
41+
@AfterClass
42+
public static void teardown() throws IOException {
43+
Files.deleteIfExists(serverKeyStore);
44+
Files.deleteIfExists(clientKeyStore);
45+
Files.deleteIfExists(tempDir);
46+
}
47+
48+
private static void copyCertsToTmpDir() throws IOException {
49+
InputStream sksStream = ClientTlsAuthTestBase.class.getResourceAsStream(TEST_KEY_STORE);
50+
Path sks = copyToTmpDir(sksStream, "server-keystore");
51+
52+
InputStream cksStream = ClientTlsAuthTestBase.class.getResourceAsStream(CLIENT_KEY_STORE);
53+
Path cks = copyToTmpDir(cksStream, "client1.p12");
54+
55+
serverKeyStore = sks;
56+
clientKeyStore = cks;
57+
}
58+
59+
private static Path copyToTmpDir(InputStream srcStream, String name) throws IOException {
60+
Path dst = tempDir.resolve(name);
61+
Files.copy(srcStream, dst);
62+
return dst;
63+
}
64+
}

0 commit comments

Comments
 (0)