SSL Pinning for preventing Man in Middle Attack

Communicating with a server gives an entirely new dimension to an application and can do a huge amount of value addition for both client and business. But communication with server also comes with its share of risks.

A insecure connection is really a bad idea but a poorly implemented https connection is even worse.

In order to make sure that even on ssl your app is communicating with the server it is intended to and not a imposter, you should implement SSL PINNING.

What is SSL Pinning in iOS APPS ?

SSL pinning simply means that the application has a pinned SSL certificate with it. Every update of the certificate requires each installed app to update its copy. This can be achieved through a new app version. SSL pining works in the following steps

  • A copy of certificate is kept with in the app bundle
  • Every time when the a secure connection is initiated app compares its copy of certificate with server certificate
  • If they don’t match the connection is terminated

How to acheive SSL Pinning in swift application?

It is really simple you just need to compare app’s local copy of the certificate with the server copy in URLSessionDelegate

class URLSessionPinningDelegate:NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let serverTrust = challenge.protectionSpace.serverTrust {
                let isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil)
                if(isServerTrusted) {
//Getting server certificate
                    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)
//Getting local certificate
                        let file_der = Bundle.main.path(forResource: "<YOU CERTIFICATE>", ofType: "der")
//Comaprign both
                        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
                                }
                            }
                        }
                    }
                }
            }
        }

        // Certificates dont  Match
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }

}
 

That was it!!! so simple.

A pat on the back !!