Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS certificate pinning with Swift and NSURLSession

Howto add certificate pinning to a NSURLSession in Swift?

The OWASP website contains only an example for Objective-C and NSURLConnection.

like image 717
lifeisfoo Avatar asked Dec 11 '15 12:12

lifeisfoo


People also ask

Is SSL pinning required for iOS?

SSL Pinning is a technique that we use on the client-side to avoid a man-in-the-middle attack by validating the server certificates. The developers embed (or pin) a list of trustful certificates to the client application during development, and use them to compare against the server certificates during runtime.

What is certificate pinning in Swift?

SSL Pinning is a technique used in swift to prevent man-in-middle attacks. In this process, the app validates the Server's certificate again after the SSL handshaking. There is a local copy of trustful certificates maintained at the client's end and compare them with the Server's certificates at runtime.

What is Dynamic SSL pinning?

The SSL pinning (or public key, or certificate pinning) is a technique mitigating Man-in-the-middle attacks against the secure HTTPS communication. The typical Android solution is to bundle the hash of the certificate, or the exact data of the certificate into the application.

What is CERT pinning iOS?

SSL pinning allows you to verify the server's identity on top of the SSL chain of trust verification. With SSL pinning, you can refuse all connections except the ones with the designated server whose SSL certificate we've saved into our local bundle.


1 Answers

Swift 3+ Update:

Just define a delegate class for NSURLSessionDelegate and implement the didReceiveChallenge function (this code is adapted from the objective-c OWASP example):

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        // Adapted from OWASP https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#iOS

        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let serverTrust = challenge.protectionSpace.serverTrust {
                let isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil)

                if(isServerTrusted) {
                    if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
                        let serverCertificateData = SecCertificateCopyData(serverCertificate)
                        let data = CFDataGetBytePtr(serverCertificateData);
                        let size = CFDataGetLength(serverCertificateData);
                        let cert1 = NSData(bytes: data, length: size)
                        let file_der = Bundle.main.path(forResource: "certificateFile", ofType: "der")

                        if let file = file_der {
                            if let cert2 = NSData(contentsOfFile: file) {
                                if cert1.isEqual(to: cert2 as Data) {
                                    completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:serverTrust))
                                    return
                                }
                            }
                        }
                    }
                }
            }
        }

        // Pinning failed
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }

}

(you can find a Gist for Swift 2 here - from the initial answer)

Then create the .der file for your website using openssl

openssl s_client -connect my-https-website.com:443 -showcerts < /dev/null | openssl x509 -outform DER > my-https-website.der

and add it to the xcode project. Double check that it's present in the Build phases tab, inside the Copy Bundle Resources list. Otherwise drag and drop it inside this list.

Finally use it in your code to make URL requests:

if let url = NSURL(string: "https://my-https-website.com") {

    let session = URLSession(
            configuration: URLSessionConfiguration.ephemeral,
            delegate: NSURLSessionPinningDelegate(),
            delegateQueue: nil)


    let task = session.dataTask(with: url as URL, completionHandler: { (data, response, error) -> Void in
        if error != nil {
            print("error: \(error!.localizedDescription): \(error!)")
        } else if data != nil {
            if let str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) {
                print("Received data:\n\(str)")
            } else {
                print("Unable to convert data to text")
            }
        }
    })

    task.resume()
} else {
    print("Unable to create NSURL")
}
like image 109
lifeisfoo Avatar answered Oct 17 '22 20:10

lifeisfoo