Skip to content

Commit 60a273f

Browse files
committed
add enhanced tls configs
1 parent 2542cd9 commit 60a273f

File tree

11 files changed

+533
-23
lines changed

11 files changed

+533
-23
lines changed

runtime/protocol/http-client-engines/http-client-engine-crt/api/http-client-engine-crt.api

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,41 @@ public final class aws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngine$Compa
1515
public final class aws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngineConfig : aws/smithy/kotlin/runtime/http/engine/HttpClientEngineConfigImpl {
1616
public static final field Companion Laws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngineConfig$Companion;
1717
public synthetic fun <init> (Laws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngineConfig$Builder;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
18+
public final fun getCaDir ()Ljava/lang/String;
19+
public final fun getCaFile ()Ljava/lang/String;
20+
public final fun getCaRoot ()Ljava/lang/String;
21+
public final fun getCipherPreference ()Laws/sdk/kotlin/crt/io/TlsCipherPreference;
1822
public final fun getClientBootstrap ()Laws/sdk/kotlin/crt/io/ClientBootstrap;
1923
public final fun getInitialWindowSizeBytes ()I
2024
public final fun getMaxConnections-pVg5ArA ()I
25+
public final fun getVerifyPeer ()Z
26+
public final fun setCaDir (Ljava/lang/String;)V
27+
public final fun setCaFile (Ljava/lang/String;)V
28+
public final fun setCaRoot (Ljava/lang/String;)V
29+
public final fun setCipherPreference (Laws/sdk/kotlin/crt/io/TlsCipherPreference;)V
2130
public final fun setClientBootstrap (Laws/sdk/kotlin/crt/io/ClientBootstrap;)V
31+
public final fun setVerifyPeer (Z)V
2232
public fun toBuilderApplicator ()Lkotlin/jvm/functions/Function1;
2333
}
2434

2535
public final class aws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngineConfig$Builder : aws/smithy/kotlin/runtime/http/engine/HttpClientEngineConfigImpl$BuilderImpl {
2636
public fun <init> ()V
37+
public final fun getCaDir ()Ljava/lang/String;
38+
public final fun getCaFile ()Ljava/lang/String;
39+
public final fun getCaRoot ()Ljava/lang/String;
40+
public final fun getCipherPreference ()Laws/sdk/kotlin/crt/io/TlsCipherPreference;
2741
public final fun getClientBootstrap ()Laws/sdk/kotlin/crt/io/ClientBootstrap;
2842
public final fun getInitialWindowSizeBytes ()I
2943
public final fun getMaxConnections-pVg5ArA ()I
44+
public final fun getVerifyPeer ()Z
45+
public final fun setCaDir (Ljava/lang/String;)V
46+
public final fun setCaFile (Ljava/lang/String;)V
47+
public final fun setCaRoot (Ljava/lang/String;)V
48+
public final fun setCipherPreference (Laws/sdk/kotlin/crt/io/TlsCipherPreference;)V
3049
public final fun setClientBootstrap (Laws/sdk/kotlin/crt/io/ClientBootstrap;)V
3150
public final fun setInitialWindowSizeBytes (I)V
3251
public final fun setMaxConnections-WZ4Q5Ns (I)V
52+
public final fun setVerifyPeer (Z)V
3353
}
3454

3555
public final class aws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngineConfig$Companion {

runtime/protocol/http-client-engines/http-client-engine-crt/jvm/src/aws/smithy/kotlin/runtime/http/engine/crt/ConnectionManager.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@ internal class ConnectionManager(
3232

3333
private val crtTlsContext: TlsContext = TlsContextOptionsBuilder()
3434
.apply {
35-
verifyPeer = true
3635
alpn = config.tlsContext.alpn.joinToString(separator = ";") { it.protocolId }
3736
minTlsVersion = toCrtTlsVersion(config.tlsContext.minVersion)
37+
caRoot = config.caRoot
38+
caFile = config.caFile
39+
caDir = config.caDir
40+
tlsCipherPreference = config.cipherPreference
41+
verifyPeer = config.verifyPeer
3842
}
3943
.build()
4044
.let(::CrtTlsContext)

runtime/protocol/http-client-engines/http-client-engine-crt/jvm/src/aws/smithy/kotlin/runtime/http/engine/crt/CrtHttpEngineConfig.kt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package aws.smithy.kotlin.runtime.http.engine.crt
77

88
import aws.sdk.kotlin.crt.io.ClientBootstrap
9+
import aws.sdk.kotlin.crt.io.TlsCipherPreference
910
import aws.smithy.kotlin.runtime.http.engine.HttpClientEngineConfig
1011
import aws.smithy.kotlin.runtime.http.engine.HttpClientEngineConfigImpl
1112

@@ -44,13 +45,44 @@ public class CrtHttpEngineConfig private constructor(builder: Builder) : HttpCli
4445
*/
4546
public var clientBootstrap: ClientBootstrap? = builder.clientBootstrap
4647

48+
/**
49+
* Certificate Authority content in PEM format
50+
*/
51+
public var caRoot: String? = builder.caRoot
52+
53+
/**
54+
* Path to the root certificate. Must be in PEM format.
55+
*/
56+
public var caFile: String? = builder.caFile
57+
58+
/**
59+
* Path to the local trust store. Can be null.
60+
*/
61+
public var caDir: String? = builder.caDir
62+
63+
/**
64+
* TLS cipher suite preference for connections.
65+
* Controls which cipher suites are available during TLS negotiation.
66+
*/
67+
public var cipherPreference: TlsCipherPreference = builder.cipherPreference
68+
69+
/**
70+
* Whether to verify the peer's certificate during TLS handshake.
71+
* When false, accepts any certificate (insecure, for testing only).
72+
*/
73+
public var verifyPeer: Boolean = builder.verifyPeer
74+
4775
override fun toBuilderApplicator(): HttpClientEngineConfig.Builder.() -> Unit = {
4876
super.toBuilderApplicator()()
4977

5078
if (this is Builder) {
5179
maxConnections = this@CrtHttpEngineConfig.maxConnections
5280
initialWindowSizeBytes = this@CrtHttpEngineConfig.initialWindowSizeBytes
5381
clientBootstrap = this@CrtHttpEngineConfig.clientBootstrap
82+
caRoot = this@CrtHttpEngineConfig.caRoot
83+
caFile = this@CrtHttpEngineConfig.caFile
84+
cipherPreference = this@CrtHttpEngineConfig.cipherPreference
85+
verifyPeer = this@CrtHttpEngineConfig.verifyPeer
5486
}
5587
}
5688

@@ -73,5 +105,32 @@ public class CrtHttpEngineConfig private constructor(builder: Builder) : HttpCli
73105
* Set the [ClientBootstrap] to use for the engine. By default it is a shared instance.
74106
*/
75107
public var clientBootstrap: ClientBootstrap? = null
108+
109+
/**
110+
* Certificate Authority content in PEM format
111+
*/
112+
public var caRoot: String? = null
113+
114+
/**
115+
* Path to the root certificate. Must be in PEM format.
116+
*/
117+
public var caFile: String? = null
118+
119+
/**
120+
* Path to the local trust store. Can be null.
121+
*/
122+
public var caDir: String? = null
123+
124+
/**
125+
* TLS cipher suite preference for connections.
126+
* Controls which cipher suites are available during TLS negotiation.
127+
*/
128+
public var cipherPreference: TlsCipherPreference = TlsCipherPreference.SYSTEM_DEFAULT
129+
130+
/**
131+
* Whether to verify the peer's certificate during TLS handshake.
132+
* When false, accepts any certificate (insecure, for testing only).
133+
*/
134+
public var verifyPeer: Boolean = true
76135
}
77136
}

runtime/protocol/http-client-engines/http-client-engine-okhttp/api/http-client-engine-okhttp.api

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,36 @@ public final class aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngine$Com
6666
public final class aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngineConfig : aws/smithy/kotlin/runtime/http/engine/HttpClientEngineConfigImpl {
6767
public static final field Companion Laws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngineConfig$Companion;
6868
public synthetic fun <init> (Laws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngineConfig$Builder;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
69+
public final fun getCertificatePinner ()Lokhttp3/CertificatePinner;
70+
public final fun getCipherSuites ()Ljava/util/List;
6971
public final fun getConnectionIdlePollingInterval-FghU774 ()Lkotlin/time/Duration;
72+
public final fun getHostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;
73+
public final fun getKeyManagerProvider ()Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsKeyManagersProvider;
7074
public final fun getMaxConcurrencyPerHost-pVg5ArA ()I
75+
public final fun getTrustManagerProvider ()Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsTrustManagersProvider;
76+
public final fun setKeyManagerProvider (Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsKeyManagersProvider;)V
77+
public final fun setTrustManagerProvider (Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsTrustManagersProvider;)V
7178
public fun toBuilderApplicator ()Lkotlin/jvm/functions/Function1;
7279
}
7380

7481
public final class aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngineConfig$Builder : aws/smithy/kotlin/runtime/http/engine/HttpClientEngineConfigImpl$BuilderImpl {
7582
public fun <init> ()V
83+
public final fun getCertificatePinner ()Lokhttp3/CertificatePinner;
84+
public final fun getCipherSuites ()Ljava/util/List;
7685
public final fun getConnectionIdlePollingInterval-FghU774 ()Lkotlin/time/Duration;
86+
public final fun getHostnameVerifier ()Ljavax/net/ssl/HostnameVerifier;
87+
public final fun getKeyManagerProvider ()Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsKeyManagersProvider;
7788
public final fun getMaxConcurrencyPerHost-0hXNFcg ()Lkotlin/UInt;
7889
public fun getTelemetryProvider ()Laws/smithy/kotlin/runtime/telemetry/TelemetryProvider;
90+
public final fun getTrustManagerProvider ()Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsTrustManagersProvider;
91+
public final fun setCertificatePinner (Lokhttp3/CertificatePinner;)V
92+
public final fun setCipherSuites (Ljava/util/List;)V
7993
public final fun setConnectionIdlePollingInterval-BwNAW2A (Lkotlin/time/Duration;)V
94+
public final fun setHostnameVerifier (Ljavax/net/ssl/HostnameVerifier;)V
95+
public final fun setKeyManagerProvider (Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsKeyManagersProvider;)V
8096
public final fun setMaxConcurrencyPerHost-ExVfyTY (Lkotlin/UInt;)V
8197
public fun setTelemetryProvider (Laws/smithy/kotlin/runtime/telemetry/TelemetryProvider;)V
98+
public final fun setTrustManagerProvider (Laws/smithy/kotlin/runtime/http/engine/okhttp/TlsTrustManagersProvider;)V
8299
}
83100

84101
public final class aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngineConfig$Companion {
@@ -134,3 +151,11 @@ public final class aws/smithy/kotlin/runtime/http/engine/okhttp/StreamingRequest
134151
public fun writeTo (Lokio/BufferedSink;)V
135152
}
136153

154+
public abstract interface class aws/smithy/kotlin/runtime/http/engine/okhttp/TlsKeyManagersProvider {
155+
public abstract fun keyManagers ()[Ljavax/net/ssl/KeyManager;
156+
}
157+
158+
public abstract interface class aws/smithy/kotlin/runtime/http/engine/okhttp/TlsTrustManagersProvider {
159+
public abstract fun trustManagers ()[Ljavax/net/ssl/TrustManager;
160+
}
161+

runtime/protocol/http-client-engines/http-client-engine-okhttp/jvm/src/aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngine.kt

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import kotlinx.coroutines.job
2424
import okhttp3.*
2525
import okhttp3.coroutines.executeAsync
2626
import java.util.concurrent.TimeUnit
27+
import javax.net.ssl.SSLContext
28+
import javax.net.ssl.X509TrustManager
2729
import kotlin.time.toJavaDuration
2830
import aws.smithy.kotlin.runtime.net.TlsVersion as SdkTlsVersion
2931
import okhttp3.TlsVersion as OkHttpTlsVersion
@@ -103,7 +105,20 @@ public fun OkHttpEngineConfig.buildClient(
103105
followRedirects(false)
104106
followSslRedirects(false)
105107

106-
connectionSpecs(listOf(minTlsConnectionSpec(config.tlsContext), ConnectionSpec.CLEARTEXT))
108+
connectionSpecs(listOf(tlsConnectionSpec(config.tlsContext, config.cipherSuites), ConnectionSpec.CLEARTEXT))
109+
110+
if (config.trustManagerProvider != null) {
111+
val (sslContext, trustManager) = createSslContext(config.trustManagerProvider, config.keyManagerProvider)
112+
sslSocketFactory(sslContext.socketFactory, trustManager)
113+
}
114+
115+
if (config.certificatePinner != null) {
116+
certificatePinner(config.certificatePinner)
117+
}
118+
119+
if (config.hostnameVerifier != null) {
120+
hostnameVerifier(config.hostnameVerifier)
121+
}
107122

108123
// Transient connection errors are handled by retry strategy (exceptions are wrapped and marked retryable
109124
// appropriately internally). We don't want inner retry logic that inflates the number of retries.
@@ -159,7 +174,7 @@ public fun OkHttpEngineConfig.buildClient(
159174
}.build()
160175
}
161176

162-
private fun minTlsConnectionSpec(tlsContext: TlsContext): ConnectionSpec {
177+
private fun tlsConnectionSpec(tlsContext: TlsContext, cipherSuites: List<String>?): ConnectionSpec {
163178
val minVersion = tlsContext.minVersion ?: TlsVersion.TLS_1_2
164179
val okHttpTlsVersions = SdkTlsVersion
165180
.values()
@@ -170,6 +185,11 @@ private fun minTlsConnectionSpec(tlsContext: TlsContext): ConnectionSpec {
170185
return ConnectionSpec
171186
.Builder(ConnectionSpec.MODERN_TLS)
172187
.tlsVersions(*okHttpTlsVersions)
188+
.apply {
189+
if (cipherSuites != null) {
190+
cipherSuites(*cipherSuites.toTypedArray())
191+
}
192+
}
173193
.build()
174194
}
175195

@@ -179,3 +199,22 @@ private fun toOkHttpTlsVersion(sdkTlsVersion: SdkTlsVersion): OkHttpTlsVersion =
179199
SdkTlsVersion.TLS_1_2 -> OkHttpTlsVersion.TLS_1_2
180200
SdkTlsVersion.TLS_1_3 -> OkHttpTlsVersion.TLS_1_3
181201
}
202+
203+
/**
204+
* Creates an SSL context with custom trust and key managers
205+
*/
206+
private fun createSslContext(trustManagerProvider: TlsTrustManagersProvider?, keyManagerProvider: TlsKeyManagersProvider?): Pair<SSLContext, X509TrustManager> {
207+
val trustManagers = trustManagerProvider?.trustManagers()
208+
val keyManagers = keyManagerProvider?.keyManagers()
209+
210+
if (trustManagerProvider != null && (trustManagers.isNullOrEmpty() || trustManagers[0] !is X509TrustManager)) {
211+
throw IllegalStateException("Unexpected trust managers")
212+
}
213+
214+
val sslContext = SSLContext.getInstance("TLS")
215+
sslContext.init(keyManagers, trustManagers, null)
216+
217+
val trustManager = trustManagers!![0] as X509TrustManager
218+
219+
return sslContext to trustManager
220+
}

runtime/protocol/http-client-engines/http-client-engine-okhttp/jvm/src/aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngineConfig.kt

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import aws.smithy.kotlin.runtime.http.engine.HttpClientEngineConfig
99
import aws.smithy.kotlin.runtime.http.engine.HttpClientEngineConfigImpl
1010
import aws.smithy.kotlin.runtime.telemetry.Global
1111
import aws.smithy.kotlin.runtime.telemetry.TelemetryProvider
12+
import okhttp3.CertificatePinner
13+
import javax.net.ssl.HostnameVerifier
1214
import kotlin.time.Duration
1315

1416
/**
@@ -50,12 +52,50 @@ public class OkHttpEngineConfig private constructor(builder: Builder) : HttpClie
5052
*/
5153
public val maxConcurrencyPerHost: UInt = builder.maxConcurrencyPerHost ?: builder.maxConcurrency
5254

55+
/**
56+
* Provider for TLS trust managers used to validate server certificates during TLS handshake.
57+
* Trust managers determine whether to trust the certificate chain presented by a remote server.
58+
* When provided, these trust managers will be used instead of the default system trust store.
59+
*/
60+
public var trustManagerProvider: TlsTrustManagersProvider? = builder.trustManagerProvider
61+
62+
/**
63+
* Provider for KeyManagers that supply client certificates for mutual TLS (mTLS) authentication.
64+
* When provided, the client will present certificates from these KeyManagers when the server
65+
* requests client authentication. Used for scenarios requiring client certificate authentication.
66+
*/
67+
public var keyManagerProvider: TlsKeyManagersProvider? = builder.keyManagerProvider
68+
69+
/**
70+
* List of cipher suites to enable for TLS connections. If null, uses OkHttp defaults.
71+
* When specified, only the listed cipher suites will be enabled.
72+
*/
73+
public val cipherSuites: List<String>? = builder.cipherSuites
74+
75+
/**
76+
* Certificate pinner that validates server certificates against known public key pins.
77+
* Used to prevent man-in-the-middle attacks by ensuring the server presents expected certificates.
78+
*/
79+
public val certificatePinner: CertificatePinner? = builder.certificatePinner
80+
81+
/**
82+
* Custom hostname verifier for validating server hostnames during TLS handshake.
83+
* By default, OkHttp verifies that the certificate's hostname matches the request hostname.
84+
* Use this to implement custom hostname verification logic.
85+
*/
86+
public val hostnameVerifier: HostnameVerifier? = builder.hostnameVerifier
87+
5388
override fun toBuilderApplicator(): HttpClientEngineConfig.Builder.() -> Unit = {
5489
super.toBuilderApplicator()()
5590

5691
if (this is Builder) {
5792
connectionIdlePollingInterval = this@OkHttpEngineConfig.connectionIdlePollingInterval
5893
maxConcurrencyPerHost = this@OkHttpEngineConfig.maxConcurrencyPerHost
94+
trustManagerProvider = this@OkHttpEngineConfig.trustManagerProvider
95+
keyManagerProvider = this@OkHttpEngineConfig.keyManagerProvider
96+
cipherSuites = this@OkHttpEngineConfig.cipherSuites
97+
certificatePinner = this@OkHttpEngineConfig.certificatePinner
98+
hostnameVerifier = this@OkHttpEngineConfig.hostnameVerifier
5999
}
60100
}
61101

@@ -84,6 +124,39 @@ public class OkHttpEngineConfig private constructor(builder: Builder) : HttpClie
84124
*/
85125
public var maxConcurrencyPerHost: UInt? = null
86126

127+
/**
128+
* Provider for TLS trust managers used to validate server certificates during TLS handshake.
129+
* Trust managers determine whether to trust the certificate chain presented by a remote server.
130+
* When provided, these trust managers will be used instead of the default system trust store.
131+
*/
132+
public var trustManagerProvider: TlsTrustManagersProvider? = null
133+
134+
/**
135+
* Provider for KeyManagers that supply client certificates for mutual TLS (mTLS) authentication.
136+
* When provided, the client will present certificates from these KeyManagers when the server
137+
* requests client authentication. Used for scenarios requiring client certificate authentication.
138+
*/
139+
public var keyManagerProvider: TlsKeyManagersProvider? = null
140+
141+
/**
142+
* List of cipher suites to enable for TLS connections. If null, uses OkHttp defaults.
143+
* When specified, only the listed cipher suites will be enabled.
144+
*/
145+
public var cipherSuites: List<String>? = null
146+
147+
/**
148+
* Certificate pinner that validates server certificates against known public key pins.
149+
* Used to prevent man-in-the-middle attacks by ensuring the server presents expected certificates.
150+
*/
151+
public var certificatePinner: CertificatePinner? = null
152+
153+
/**
154+
* Custom hostname verifier for validating server hostnames during TLS handshake.
155+
* By default, OkHttp verifies that the certificate's hostname matches the request hostname.
156+
* Use this to implement custom hostname verification logic.
157+
*/
158+
public var hostnameVerifier: HostnameVerifier? = null
159+
87160
override var telemetryProvider: TelemetryProvider = TelemetryProvider.Global
88161
}
89162
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.http.engine.okhttp
6+
7+
import javax.net.ssl.KeyManager
8+
9+
/**
10+
* Provider for TLS key managers used for client certificate authentication.
11+
*
12+
* Key managers provide the client's private key and certificate chain when
13+
* the server requests client authentication during TLS handshake.
14+
*/
15+
public interface TlsKeyManagersProvider {
16+
/**
17+
* @return The [KeyManager]s used for client authentication.
18+
*/
19+
public fun keyManagers(): Array<KeyManager>
20+
}

0 commit comments

Comments
 (0)