Skip to content

Commit 2aaabf7

Browse files
committed
Add mTLSConfiguration option for TLSConfiguration
Motivation: When configuring for mTLS, there is really only one right way to do that. This adds that behaviour. Modifications: - Add mTLSConfiguration option - Add tests Result: Better behaviour for mTLS. Resolves #542
1 parent f19bb99 commit 2aaabf7

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

Sources/NIOSSL/TLSConfiguration.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,9 @@ public struct TLSConfiguration {
449449
self.signingSignatureAlgorithms = signingSignatureAlgorithms
450450
self.minimumTLSVersion = minimumTLSVersion
451451
self.maximumTLSVersion = maximumTLSVersion
452-
self.certificateVerification = certificateVerification
453452
self.trustRoots = trustRoots
454453
self.additionalTrustRoots = additionalTrustRoots
454+
self.certificateVerification = certificateVerification
455455
self.certificateChain = certificateChain
456456
self.privateKey = privateKey
457457
self.encodedApplicationProtocols = []
@@ -642,6 +642,47 @@ extension TLSConfiguration {
642642
pskHint: nil
643643
)
644644
}
645+
646+
/// Create a TLS configuration for use with server-side contexts that expect to validate a client
647+
/// certificate (often called mTLS).
648+
///
649+
/// This provides sensible defaults while requiring that you provide any data that is necessary
650+
/// for server-side function. For servers that don't need mTLS, try
651+
/// ``TLSConfiguration/makeServerConfiguration()`` instead.
652+
///
653+
/// This configuration is very similar to ``TLSConfiguration/makeServerConfiguration()`` but
654+
/// adds a `trustRoots` requirement. These roots will be used to validate the certificate
655+
/// presented by the peer. It also sets the ``certificateVerification`` field to
656+
/// ``CertificateVerification/noHostnameVerification``, which enables verification but disables
657+
/// any hostname checking, which cannot succeed in a server context.
658+
///
659+
/// For customising fields, modify the returned TLSConfiguration object.
660+
public static func makeServerConfigurationWithMTLS(
661+
certificateChain: [NIOSSLCertificateSource],
662+
privateKey: NIOSSLPrivateKeySource,
663+
trustRoots: NIOSSLTrustRoots
664+
) -> TLSConfiguration {
665+
TLSConfiguration(
666+
cipherSuites: defaultCipherSuites,
667+
verifySignatureAlgorithms: nil,
668+
signingSignatureAlgorithms: nil,
669+
minimumTLSVersion: .tlsv1,
670+
maximumTLSVersion: nil,
671+
certificateVerification: .noHostnameVerification,
672+
trustRoots: trustRoots,
673+
certificateChain: certificateChain,
674+
privateKey: privateKey,
675+
applicationProtocols: [],
676+
shutdownTimeout: .seconds(5),
677+
keyLogCallback: nil,
678+
renegotiationSupport: .none,
679+
additionalTrustRoots: [],
680+
sendCANameList: false,
681+
pskClientProvider: nil,
682+
pskServerProvider: nil,
683+
pskHint: nil
684+
)
685+
}
645686
}
646687

647688
// MARK: Deprecated constructors.

Tests/NIOSSLTests/TLSConfigurationTest.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,67 @@ class TLSConfigurationTest: XCTestCase {
21872187

21882188
XCTAssertEqual(callbackCount.withLockedValue { $0 }, 5)
21892189
}
2190+
2191+
func testCorrectSetUpOfMTLSContext() throws {
2192+
var basicConfig = TLSConfiguration.makeServerConfiguration(
2193+
certificateChain: [.certificate(TLSConfigurationTest.cert2)],
2194+
privateKey: .privateKey(TLSConfigurationTest.key2)
2195+
)
2196+
let mtlsConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
2197+
certificateChain: [.certificate(TLSConfigurationTest.cert2)],
2198+
privateKey: .privateKey(TLSConfigurationTest.key2),
2199+
trustRoots: .default
2200+
)
2201+
XCTAssertFalse(basicConfig.bestEffortEquals(mtlsConfig))
2202+
2203+
basicConfig.trustRoots = .default
2204+
basicConfig.certificateVerification = .noHostnameVerification
2205+
2206+
XCTAssertTrue(basicConfig.bestEffortEquals(mtlsConfig))
2207+
}
2208+
2209+
func testMTLSContext_happyPath() throws {
2210+
var clientConfig = TLSConfiguration.makeClientConfiguration()
2211+
clientConfig.trustRoots = .certificates([TLSConfigurationTest.cert1])
2212+
clientConfig.certificateChain = [.certificate(TLSConfigurationTest.cert2)]
2213+
clientConfig.privateKey = .privateKey(TLSConfigurationTest.key2)
2214+
clientConfig.certificateVerification = .noHostnameVerification
2215+
2216+
let serverConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
2217+
certificateChain: [.certificate(TLSConfigurationTest.cert1)],
2218+
privateKey: .privateKey(TLSConfigurationTest.key1),
2219+
trustRoots: .certificates([TLSConfigurationTest.cert2])
2220+
)
2221+
2222+
let clientContext = try assertNoThrowWithValue(
2223+
NIOSSLContext(configuration: clientConfig)
2224+
)
2225+
let serverContext = try assertNoThrowWithValue(
2226+
NIOSSLContext(configuration: serverConfig)
2227+
)
2228+
2229+
try assertHandshakeSucceeded(withClientContext: clientContext, andServerContext: serverContext)
2230+
}
2231+
2232+
func testMTLSContext_clientPresentsWrongCert() throws {
2233+
var clientConfig = TLSConfiguration.makeClientConfiguration()
2234+
clientConfig.trustRoots = .certificates([TLSConfigurationTest.cert1])
2235+
clientConfig.certificateChain = [.certificate(TLSConfigurationTest.cert1)]
2236+
clientConfig.privateKey = .privateKey(TLSConfigurationTest.key1)
2237+
clientConfig.certificateVerification = .noHostnameVerification
2238+
2239+
let serverConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
2240+
certificateChain: [.certificate(TLSConfigurationTest.cert1)],
2241+
privateKey: .privateKey(TLSConfigurationTest.key1),
2242+
trustRoots: .certificates([TLSConfigurationTest.cert2])
2243+
)
2244+
2245+
try assertPostHandshakeError(
2246+
withClientConfig: clientConfig,
2247+
andServerConfig: serverConfig,
2248+
errorTextContainsAnyOf: ["ALERT_UNKNOWN_CA", "ALERT_CERTIFICATE_UNKNOWN"]
2249+
)
2250+
}
21902251
}
21912252

21922253
extension EmbeddedChannel {

0 commit comments

Comments
 (0)