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
43 changes: 42 additions & 1 deletion Sources/NIOSSL/TLSConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,9 @@ public struct TLSConfiguration {
self.signingSignatureAlgorithms = signingSignatureAlgorithms
self.minimumTLSVersion = minimumTLSVersion
self.maximumTLSVersion = maximumTLSVersion
self.certificateVerification = certificateVerification
self.trustRoots = trustRoots
self.additionalTrustRoots = additionalTrustRoots
self.certificateVerification = certificateVerification
self.certificateChain = certificateChain
self.privateKey = privateKey
self.encodedApplicationProtocols = []
Expand Down Expand Up @@ -642,6 +642,47 @@ extension TLSConfiguration {
pskHint: nil
)
}

/// Create a TLS configuration for use with server-side contexts that expect to validate a client
/// certificate (often called mTLS).
///
/// This provides sensible defaults while requiring that you provide any data that is necessary
/// for server-side function. For servers that don't need mTLS, try
/// ``TLSConfiguration/makeServerConfiguration(certificateChain:privateKey:)`` instead.
///
/// This configuration is very similar to ``TLSConfiguration/makeServerConfiguration(certificateChain:privateKey:)`` but
/// adds a `trustRoots` requirement. These roots will be used to validate the certificate
/// presented by the peer. It also sets the ``certificateVerification`` field to
/// ``CertificateVerification/noHostnameVerification``, which enables verification but disables
/// any hostname checking, which cannot succeed in a server context.
///
/// For customising fields, modify the returned TLSConfiguration object.
public static func makeServerConfigurationWithMTLS(
certificateChain: [NIOSSLCertificateSource],
privateKey: NIOSSLPrivateKeySource,
trustRoots: NIOSSLTrustRoots
) -> TLSConfiguration {
TLSConfiguration(
cipherSuites: defaultCipherSuites,
verifySignatureAlgorithms: nil,
signingSignatureAlgorithms: nil,
minimumTLSVersion: .tlsv1,
maximumTLSVersion: nil,
certificateVerification: .noHostnameVerification,
trustRoots: trustRoots,
certificateChain: certificateChain,
privateKey: privateKey,
applicationProtocols: [],
shutdownTimeout: .seconds(5),
keyLogCallback: nil,
renegotiationSupport: .none,
additionalTrustRoots: [],
sendCANameList: false,
pskClientProvider: nil,
pskServerProvider: nil,
pskHint: nil
)
}
}

// MARK: Deprecated constructors.
Expand Down
61 changes: 61 additions & 0 deletions Tests/NIOSSLTests/TLSConfigurationTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2187,6 +2187,67 @@ class TLSConfigurationTest: XCTestCase {

XCTAssertEqual(callbackCount.withLockedValue { $0 }, 5)
}

func testCorrectSetUpOfMTLSContext() throws {
var basicConfig = TLSConfiguration.makeServerConfiguration(
certificateChain: [.certificate(TLSConfigurationTest.cert2)],
privateKey: .privateKey(TLSConfigurationTest.key2)
)
let mtlsConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
certificateChain: [.certificate(TLSConfigurationTest.cert2)],
privateKey: .privateKey(TLSConfigurationTest.key2),
trustRoots: .default
)
XCTAssertFalse(basicConfig.bestEffortEquals(mtlsConfig))

basicConfig.trustRoots = .default
basicConfig.certificateVerification = .noHostnameVerification

XCTAssertTrue(basicConfig.bestEffortEquals(mtlsConfig))
}

func testMTLSContext_happyPath() throws {
var clientConfig = TLSConfiguration.makeClientConfiguration()
clientConfig.trustRoots = .certificates([TLSConfigurationTest.cert1])
clientConfig.certificateChain = [.certificate(TLSConfigurationTest.cert2)]
clientConfig.privateKey = .privateKey(TLSConfigurationTest.key2)
clientConfig.certificateVerification = .noHostnameVerification

let serverConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
certificateChain: [.certificate(TLSConfigurationTest.cert1)],
privateKey: .privateKey(TLSConfigurationTest.key1),
trustRoots: .certificates([TLSConfigurationTest.cert2])
)

let clientContext = try assertNoThrowWithValue(
NIOSSLContext(configuration: clientConfig)
)
let serverContext = try assertNoThrowWithValue(
NIOSSLContext(configuration: serverConfig)
)

try assertHandshakeSucceeded(withClientContext: clientContext, andServerContext: serverContext)
}

func testMTLSContext_clientPresentsWrongCert() throws {
var clientConfig = TLSConfiguration.makeClientConfiguration()
clientConfig.trustRoots = .certificates([TLSConfigurationTest.cert1])
clientConfig.certificateChain = [.certificate(TLSConfigurationTest.cert1)]
clientConfig.privateKey = .privateKey(TLSConfigurationTest.key1)
clientConfig.certificateVerification = .noHostnameVerification

let serverConfig = TLSConfiguration.makeServerConfigurationWithMTLS(
certificateChain: [.certificate(TLSConfigurationTest.cert1)],
privateKey: .privateKey(TLSConfigurationTest.key1),
trustRoots: .certificates([TLSConfigurationTest.cert2])
)

try assertPostHandshakeError(
withClientConfig: clientConfig,
andServerConfig: serverConfig,
errorTextContainsAnyOf: ["ALERT_UNKNOWN_CA", "ALERT_CERTIFICATE_UNKNOWN"]
)
}
}

extension EmbeddedChannel {
Expand Down