Skip to content

Commit 6f8a5f4

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 34486a9 commit 6f8a5f4

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
@@ -459,9 +459,9 @@ public struct TLSConfiguration {
459459
self.signingSignatureAlgorithms = signingSignatureAlgorithms
460460
self.minimumTLSVersion = minimumTLSVersion
461461
self.maximumTLSVersion = maximumTLSVersion
462-
self.certificateVerification = certificateVerification
463462
self.trustRoots = trustRoots
464463
self.additionalTrustRoots = additionalTrustRoots
464+
self.certificateVerification = certificateVerification
465465
self.certificateChain = certificateChain
466466
self.privateKey = privateKey
467467
self.encodedApplicationProtocols = []
@@ -652,6 +652,47 @@ extension TLSConfiguration {
652652
pskHint: nil
653653
)
654654
}
655+
656+
/// Create a TLS configuration for use with server-side contexts that expect to validate a client
657+
/// certificate (often called mTLS).
658+
///
659+
/// This provides sensible defaults while requiring that you provide any data that is necessary
660+
/// for server-side function. For servers that don't need mTLS, try
661+
/// ``TLSConfiguration/makeServerConfiguration()`` instead.
662+
///
663+
/// This configuration is very similar to ``TLSConfiguration/makeServerConfiguration()`` but
664+
/// adds a `trustRoots` requirement. These roots will be used to validate the certificate
665+
/// presented by the peer. It also sets the ``certificateVerification`` field to
666+
/// ``CertificateVerification/noHostnameVerification``, which enables verification but disables
667+
/// any hostname checking, which cannot succeed in a server context.
668+
///
669+
/// For customising fields, modify the returned TLSConfiguration object.
670+
public static func makeServerConfigurationWithMTLS(
671+
certificateChain: [NIOSSLCertificateSource],
672+
privateKey: NIOSSLPrivateKeySource,
673+
trustRoots: NIOSSLTrustRoots
674+
) -> TLSConfiguration {
675+
TLSConfiguration(
676+
cipherSuites: defaultCipherSuites,
677+
verifySignatureAlgorithms: nil,
678+
signingSignatureAlgorithms: nil,
679+
minimumTLSVersion: .tlsv1,
680+
maximumTLSVersion: nil,
681+
certificateVerification: .noHostnameVerification,
682+
trustRoots: trustRoots,
683+
certificateChain: certificateChain,
684+
privateKey: privateKey,
685+
applicationProtocols: [],
686+
shutdownTimeout: .seconds(5),
687+
keyLogCallback: nil,
688+
renegotiationSupport: .none,
689+
additionalTrustRoots: [],
690+
sendCANameList: false,
691+
pskClientProvider: nil,
692+
pskServerProvider: nil,
693+
pskHint: nil
694+
)
695+
}
655696
}
656697

657698
// MARK: Deprecated constructors.

Tests/NIOSSLTests/TLSConfigurationTest.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,6 +2255,67 @@ class TLSConfigurationTest: XCTestCase {
22552255
config.trustRoots = .none
22562256
XCTAssertEqual(config.certificateVerification, .fullVerification)
22572257
}
2258+
2259+
func testCorrectSetUpOfMTLSContext() throws {
2260+
var basicConfig = TLSConfiguration.makeServerConfiguration(
2261+
certificateChain: [.certificate(TLSConfigurationTest.cert2)],
2262+
privateKey: .privateKey(TLSConfigurationTest.key2)
2263+
)
2264+
let mtlsConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
2265+
certificateChain: [.certificate(TLSConfigurationTest.cert2)],
2266+
privateKey: .privateKey(TLSConfigurationTest.key2),
2267+
trustRoots: .default
2268+
)
2269+
XCTAssertFalse(basicConfig.bestEffortEquals(mtlsConfig))
2270+
2271+
basicConfig.trustRoots = .default
2272+
basicConfig.certificateVerification = .noHostnameVerification
2273+
2274+
XCTAssertTrue(basicConfig.bestEffortEquals(mtlsConfig))
2275+
}
2276+
2277+
func testMTLSContext_happyPath() throws {
2278+
var clientConfig = TLSConfiguration.makeClientConfiguration()
2279+
clientConfig.trustRoots = .certificates([TLSConfigurationTest.cert1])
2280+
clientConfig.certificateChain = [.certificate(TLSConfigurationTest.cert2)]
2281+
clientConfig.privateKey = .privateKey(TLSConfigurationTest.key2)
2282+
clientConfig.certificateVerification = .noHostnameVerification
2283+
2284+
let serverConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
2285+
certificateChain: [.certificate(TLSConfigurationTest.cert1)],
2286+
privateKey: .privateKey(TLSConfigurationTest.key1),
2287+
trustRoots: .certificates([TLSConfigurationTest.cert2])
2288+
)
2289+
2290+
let clientContext = try assertNoThrowWithValue(
2291+
NIOSSLContext(configuration: clientConfig)
2292+
)
2293+
let serverContext = try assertNoThrowWithValue(
2294+
NIOSSLContext(configuration: serverConfig)
2295+
)
2296+
2297+
try assertHandshakeSucceeded(withClientContext: clientContext, andServerContext: serverContext)
2298+
}
2299+
2300+
func testMTLSContext_clientPresentsWrongCert() throws {
2301+
var clientConfig = TLSConfiguration.makeClientConfiguration()
2302+
clientConfig.trustRoots = .certificates([TLSConfigurationTest.cert1])
2303+
clientConfig.certificateChain = [.certificate(TLSConfigurationTest.cert1)]
2304+
clientConfig.privateKey = .privateKey(TLSConfigurationTest.key1)
2305+
clientConfig.certificateVerification = .noHostnameVerification
2306+
2307+
let serverConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
2308+
certificateChain: [.certificate(TLSConfigurationTest.cert1)],
2309+
privateKey: .privateKey(TLSConfigurationTest.key1),
2310+
trustRoots: .certificates([TLSConfigurationTest.cert2])
2311+
)
2312+
2313+
try assertPostHandshakeError(
2314+
withClientConfig: clientConfig,
2315+
andServerConfig: serverConfig,
2316+
errorTextContainsAnyOf: ["ALERT_UNKNOWN_CA", "ALERT_CERTIFICATE_UNKNOWN"]
2317+
)
2318+
}
22582319
}
22592320

22602321
extension EmbeddedChannel {

0 commit comments

Comments
 (0)