0

I am working on an test application which connects to a service using HTTPS. The certificate of this connection is using a custom root certificate. So I implemented the delegate for `NSURLSessionDelegate' and implemented it like this:

func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
        let trust: SecTrustRef = challenge.protectionSpace.serverTrust!
        var secresult: SecTrustResultType = SecTrustResultType(kSecTrustResultInvalid)
        if SecTrustEvaluate(trust, &secresult) == errSecSuccess {
            switch (Int(secresult)) {
            case kSecTrustResultUnspecified:
                break
            case kSecTrustResultProceed:
                let credential = NSURLCredential(forTrust: trust)
                completionHandler(.UseCredential, credential)
                return
            default:
                print("default")
            }
        }
    }
    completionHandler(.CancelAuthenticationChallenge, nil)
}

I get into the case kSecTrustResultProceed, but the code shown here results in a endless loop. I always run into this error:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

I suspect it is because the certificate is not trusted by the user, so I read about SFCertificateTrustPanel where the user can accept the trust for a certificate. Yet this method seems not available from Swift.

How do I use SFCertificateTrustPanel in swift?

Is there another way how I can trust a certificate, e.g. based on a fingerprint?

Flovdis
  • 2,945
  • 26
  • 49

1 Answers1

1

I don't know any reason you couldn't use SFCertificateTrustPanel from Swift, assuming you're on OS X. With that said, it doesn't do much that you can't do from within the app.

What you need to do is modify the protection space to allow your specific TLS certificate. I recommend reading Apple's article "Overriding TLS Chain Validation Correctly". You'll need to load the TLS certificate into an NSData object, convert it to a SecCertificateRef, and add it to the SecTrustRef object. Most (but not all) of those steps are covered in the document linked above.

If you're going to be using different certificates on different servers, the right way to handle that is to make the connection fail when you see a new cert, then show a dialog asking the user whether to trust the cert (e.g. giving its fingerprint). If the user says yes, export that cert to an NSData object (e.g. in DER form) and store it in an array of trusted certs in your NSUserDefaults or something, and add every item in that array of certs to your trust object in your custom trust evaluation function.

One final note: the iOS versions of the various TLS libraries have a bunch of extra methods that make it easier to read keys and certs from files on disk, IIRC, and easier to convert between NSData representations and actual certs or RSA keys. These methods are all available on OS X, despite what the documentation says, as they are necessary for the iOS Simulator to function. :-)

dgatwood
  • 10,129
  • 1
  • 28
  • 49
  • What do I exactly need to include in my Swift project to use `SFCertificateTrustPanel`? The problem I have is, that I do not find the right libraries/headers to include to use this panel. – Flovdis Aug 02 '16 at 21:23
  • It's in the SecurityInterface framework, so I would assume that you'd import SecurityInterface/SecurityInterface.h or whatever the Swift equivalent is, but... I'm not a Swift programmer, so I can't say for certain. It is possible that Apple never got around to building Swift headers for it, in which case you might have to create them. – dgatwood Aug 03 '16 at 02:28
  • In this case, your answer does not answer my question at all. I know how to validate a certificate. What I am asking for is, how to use the `SFCertificateTrustPanel` to ask the user to trust a shown certificate. – Flovdis Aug 03 '16 at 10:36
  • The problem with using SFCertificateTrustPanel—and the reason that it isn't really recommended for new development—is that IIRC it adds trust for the certificate *globally*. As a result, from that point on, that self-signed certificate is potentially an anchor cert that can sign arbitrary certs for arbitrary domains. It's a much better approach to create your own "trust pane" that shows some info about the cert and lets the user agree to trust it, then add the trust specifically for your app's connections, as I described in my answer. – dgatwood Aug 03 '16 at 16:05
  • 1
    With that said, to import SFCertificateTrustPanel, first add the SecurityInterface framework to your project, then add an Objective-C file to your project. Xcode should offer to create bridging headers. Delete the Objective-C file. Import the bridging header. For details, see http://stackoverflow.com/questions/24272184/connect-objective-c-framework-to-swift-ios-8-app-parse-framework – dgatwood Aug 03 '16 at 16:13
  • It took some time, but now I could implement the handler with the information you gave me. – Flovdis Aug 22 '16 at 16:24