Client Certificates

I'm trying to authenticate to my webserver with a p12 certificate. When the connection tries to run I end up getting back an error saying that the server requires a certificate...meaning my code isn't working. The server is running TLS1.2 and I know the certificate I'm using is good as I exported the working one from the Mac Keychain tool.



CFNetwork SSLHandshake failed (-9824 -> -9829)

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

The server “my.site.com” requires a client certificate.



    lazy private var credential: NSURLCredential = {
        let url = NSBundle.mainBundle().URLForResource("Certificates", withExtension: "p12")!
        let data = NSData(contentsOfURL: url)!
      
        var importResult: CFArray? = nil
        SecPKCS12Import(data, [kSecImportExportPassphrase as String : "qwer1234ASDF"], &importResult)

        let result = (importResult! as NSArray as! [[String : AnyObject]]).first!
        let identity = result[kSecImportItemIdentity as String] as! SecIdentity
        let certificate = result[kSecImportItemCertChain as String] as! SecCertificate

        return NSURLCredential(identity: identity, certificates: [certificate], persistence: .ForSession)
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        let mydomain = "my.site.com"
        let port = 1443

        let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
        let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)

        let url = NSURL(string: "https://\(mydomain):\(port)/startup/byts?ts=0")!
        let task = session.dataTaskWithURL(url) {
            data, response, error in
            if data == nil {
                print(error!.localizedDescription)
            }
        }
        task.resume()
    }

    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        completionHandler(.UseCredential, credential)
    }

Replies

First things first, you need to fix your implementation of

URLSession(_:didReceiveChallenge:completionHandler:)
. That can be called with many different type of authentication challenges, and you only want to provide a client identity credential for the
NSURLAuthenticationMethodClientCertificate
challenge. So, your code needs to look like this:
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
        completionHandler(.UseCredential, credential)
    } else {
        completionHandler(.PerformDefaultHandling, nil)
    }
}

That might fix the problem (the first challenge you typically get in the HTTPS case is

NSURLAuthenticationMethodServerTrust
, and responding to that with a client identity credential is not good) but, if not, please post back describing the latest behaviour.

ps I presume you’ve hard wired

Certificates.p12
just for the sake of doing a quick test. Baking credentials into your app is less than ideal from a security perspective.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"