iOS Workaround for Public Key Pinning
When your mobile application is communicating with SSL servers, you can heighten the security by implementing SSL pinning, which prevents man-in-the-middle attacks and ensures that third parties cannot read and intercept private data about your user.
The most common approach for implementing SSL pinning on iOS is pinning the certificate as a whole (see the sample code provided by OWASP). The problem with this approach is that every time the certificate needs to be updated (which can happen for various reasons, such as when the encryption algorithm changes or the certificate is compromised), your app will need an update. This poses serious issues because you could potentially abandon all users who did not or will not update the app.
Another alternative to pinning the certificate as a whole is to pin the public key of the root certificate in the certificate’s chain; this keeps the security intact while solving the problem of handling new certificates. Unfortunately, Apple’s iOS APIs within the Security Framework don’t play well with this approach.
It is possible to pin the public key of the whole certificate using
This is how popular frameworks such as AFNetworking and AlamoFire are implemented, but this does not solve our problem. We are looking to pin only the public key of the root certificate (which is in the certificate’s chain), not the whole certificate. The public key of the whole certificate is more likely to chain between certificate changes/updates. You can retrieve the root certificate, but not its public key.
– (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef rootCertificate = SecTrustGetCertificateAtIndex(trust, 0);
A solution, though not the cleanest, is to extract the public key of the root certificate using a library on GitHub called ios-openssl. By extracting the public key, and comparing it with the public key from the SSL certificate that you are using to pin, we have found the solution to our problems.