Skip to content

Commit

Permalink
Merge pull request #81 from kkieffer/embeddedServerCert
Browse files Browse the repository at this point in the history
Feature request: let client compare self-signed server cert against local embedded version
  • Loading branch information
billabt authored Nov 23, 2019
2 parents d6616de + 5370e73 commit eea45c3
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 13 deletions.
50 changes: 47 additions & 3 deletions Sources/SSLService/SSLService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,29 +173,38 @@ public class SSLService: SSLServiceDelegate {

/// True if no backing certificates provided (Readonly).
public private(set) var noBackingCertificates = false

/// For clients that allow `self-signed` server certificates, verify against ones provided locally
public private(set) var embeddedServerCertPaths : [URL]? = nil


// MARK: Lifecycle

///
/// Initialize a configuration with no backing certificates.
///
/// - Warning: **This API is not supported when running on Apple platforms**.
///
/// - Parameters:
/// - cipherSuite: Optional String containing the cipher suite to use.
/// - clientAllowsSelfSignedCertificates:
/// `true` to accept self-signed certificates from a server. `false` otherwise.
/// **Note:** This parameter is only used when `SSLService` is used with a client socket.
/// - embeddedServerCertPath: when client allows self-signed certificates from a server, verify to server certificate
/// against one of the locally embedded certificates. Pass `nil` to skip the check.
/// **Note:** This parameter is only used when `SSLService` is used with a client socket.
/// **Note:** This parameter is only available on Apple platforms
/// **Note:** This feature unavailable (parameter ignored) on MacOS versions less than 10.14, iOS < 12.0
///
/// - Returns: New Configuration instance.
///
public init(withCipherSuite cipherSuite: String? = nil, clientAllowsSelfSignedCertificates: Bool = true) {
public init(withCipherSuite cipherSuite: String? = nil, clientAllowsSelfSignedCertificates: Bool = true, embeddedServerCertPaths : [URL]? = nil) {

self.noBackingCertificates = true
self.clientAllowsSelfSignedCertificates = clientAllowsSelfSignedCertificates
if cipherSuite != nil {
self.cipherSuite = cipherSuite!
}
self.embeddedServerCertPaths = embeddedServerCertPaths
}

///
Expand Down Expand Up @@ -1232,7 +1241,42 @@ public class SSLService: SSLServiceDelegate {

// Copy the trust into the context...
SSLCopyPeerTrust(sslContext, &self.trust)


#if swift(>=4.2)
if #available(macOS 10.14, iOS 12.0, *) {
// Verify against a local embedded certificate
if let trust = self.trust, let paths = configuration.embeddedServerCertPaths {

//Load all the certs from files
var certs : [SecCertificate] = []
for path in paths {
let data = try Data(contentsOf: path)
if let cert = SecCertificateCreateWithData(nil, data as CFData) { //load the cert and append it
certs.append(cert)
}
else {
throw SSLError.fail(Int(errSSLBadCert), "Bunded certificate is not a valid DER encoded X.509 certificate")
}
}

//Now add all certs as Anchor Certificates
status = SecTrustSetAnchorCertificates(trust, certs as CFArray)
if status != errSecSuccess {
try self.throwLastError(source: "SecTrustSetAnchorCertificates", err: status)
}

//Evaluate the embedded certs against the trust
if SecTrustEvaluateWithError(trust, nil) == false {
throw SSLError.fail(Int(errSSLXCertChainInvalid), "No bunded certificate matches server certificate")

}

}
}
#endif



// Continue the handshake...
status = errSSLWouldBlock
}
Expand Down
60 changes: 50 additions & 10 deletions Tests/SSLServiceTests/SSLServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,17 @@ class SSLServiceTests: XCTestCase {

}

func createSecureHelper(family: Socket.ProtocolFamily = .inet) throws -> Socket {

func createEmbeddedCertConfiguration() {
let certPath = SSLServiceTests.getFilePath(for: "cert", ofType: "der")
XCTAssertNotNil(self.certPath)
print("Using embedded cert: \(certPath!)")

self.configuration = SSLService.Configuration(clientAllowsSelfSignedCertificates: true, embeddedServerCertPaths: [certPath!])
}


func createSecureHelper(family: Socket.ProtocolFamily = .inet, useEmbeddedCert: Bool) throws -> Socket {

let socket = try Socket.create(family: family)
XCTAssertNotNil(socket)
Expand All @@ -127,8 +137,17 @@ class SSLServiceTests: XCTestCase {

if enableSSL {

self.createConfiguration()

if useEmbeddedCert {
self.createEmbeddedCertConfiguration()
}
else {
#if os(Linux)
self.createConfiguration() //cannot use this function as a client on Apple platforms
#else
self.configuration = SSLService.Configuration(clientAllowsSelfSignedCertificates: true) //simple client connection accepting self signed certs
#endif
}

let service = try SSLService(usingConfiguration: self.configuration!)
XCTAssertNotNil(service)

Expand Down Expand Up @@ -293,12 +312,12 @@ class SSLServiceTests: XCTestCase {
return nil
}

func testSSLConfiguration() {
func test_SSLConfiguration() {

self.createConfiguration()
}

func testSecureReadWrite() {
func testSecureReadWrite(useEmbeddedCert : Bool) {

let hostname = "127.0.0.1"
let port: Int32 = 1337
Expand All @@ -322,15 +341,17 @@ class SSLServiceTests: XCTestCase {
let signature = try Socket.Signature(protocolFamily: .inet, socketType: .stream, proto: .tcp, hostname: hostname, port: port)!

// Create the socket...
let socket = try createSecureHelper()
let socket = try createSecureHelper(useEmbeddedCert: useEmbeddedCert)

// Defer cleanup...
defer {
// Close the socket...
socket.close()
XCTAssertFalse(socket.isActive)
}


print("\nAttempting to connect to host: \(hostname):\(port)...")

// Connect to the server helper...
try socket.connect(using: signature)
if !socket.isConnected {
Expand Down Expand Up @@ -379,9 +400,28 @@ class SSLServiceTests: XCTestCase {
}

}


func testSecureReadWrite_A_SelfSigned() {
testSecureReadWrite(useEmbeddedCert: false)
}

func testSecureReadWrite_B_Embedded() {
testSecureReadWrite(useEmbeddedCert: true)
}

#if os(Linux)
static var allTests = [
("testSSLConfiguration", testSSLConfiguration),
("testSecureReadWrite", testSecureReadWrite),
("testSSLConfiguration", test_SSLConfiguration),
("testSecureReadWrite", testSecureReadWrite_A_SelfSigned),
]

#else
static var allTests = [
("testSSLConfiguration", test_SSLConfiguration),
("testSecureReadWrite", testSecureReadWrite_A_SelfSigned),
("testSecureReadWriteEmbedded", testSecureReadWrite_B_Embedded),
]

#endif

}
Binary file added Tests/SSLServiceTests/certs/cert.der
Binary file not shown.

0 comments on commit eea45c3

Please sign in to comment.