Mastering SSL Pinning in Swift: No Third-Party Libraries Required

Eugene Gordenstein
LunaSolutions
Published in
4 min readJul 12, 2023

TL;DR: If you want to implement SSL Pinning in your project, copy the contents of this file into your project. Initialize SSLPinningManager with a list of your public key hashes, and add this method to your URLSessionTaskDelegate.

extension SessionDelegate: URLSessionTaskDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
sslPinningManager.validate(challenge: challenge, completionHandler: completionHandler)
}
}

Take a look at the demo implementation here.

Introduction

SSL Pinning is an important security measure to prevent Man-in-The-Middle attacks, where an attacker positions himself between a client and a server. This article will guide you through implementing SSL Pinning in iOS using public key hashes without relying on third-party libraries like TrustKit. Let’s get started!

A Few Words About SSL Pinning

SSL Pinning is a technique that involves storing SSL certificates or public keys within your app, enabling it to verify the authenticity of a server’s certificate. By allowing the use of specific certificates, SSL Pinning makes sure that your app communicates only with trusted servers. If you want to understand SSL Pinning more deeply, take a look at this article.

How To Get SSL Certificate Public Key Hashes

To get the SHA256 hash of the server public key, we are going to use SSL Server Test by Qualys. It’s an easy-to-use, user-friendly tool that clearly illustrates certificate chains, making it easy to understand.

Step 1: Enter your server’s URL in the Hostname bar.

Step 2: Select any of the available servers.

Step 3: Copy the SHA256 of the Leaf Certificate.

Step 4&5: Copy the SHA256 of the Intermediate and Root Certificates.

Choosing the Right Certificate for Pinning

Now that you have all three certificates, you need to choose which one you will use for SSL Pinning.

  1. Leaf Certificate. The most secure option but comes at a cost, since the expiration time is very limited. Thus, it requires frequent updates.
  2. Intermediate Certificate. A recommended option that balances security and manageability by trusting the Certificate Authority (CA) that issues certificates to your server.
  3. Root Certificate. The least secure option, as it trusts the root CA and every intermediate CA that the root CA issued a certificate to. This option might still be a good one in some cases.

Implementing SSL Pinning In Your Project

You’ve completed the first part of SSL Pinning implementation — finding the public key hash that you are going to compare against during the SSL Pinning process. The next step is to implement a way to get a public key hash in Swift.

First, we need to become a URLSessionTaskDelegate and receive URLAuthenticationChallenge. We pass the challenge to the SSLPinningManager as well as the completion handler to notify URLSession about the results. The complete demo file can be found here.

extension SessionDelegate: URLSessionTaskDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
sslPinningManager.validate(challenge: challenge, completionHandler: completionHandler)
}
}

Now, let’s take a look at SSLPinningManager implementation. We will cover step by step implementation of the manager below.

Step 1: We receive the challenge and pass it to the private function.

Step 2: Next, we get a copy of the certificate chain and cast it as an array of SecCertificate where the first certificate represents a leaf certificate and the last one the root certificate. We are going to loop through the array.

Step 3: Now, we get the public key from a certificate.

Step 4: In this step we’ll create a hash for our public key. Initially, we have the public key in its raw format from the server. To make it standard-compliant, we need to prepend ASN.1 metadata, represented by rsa2048ASN1Header, to the raw public key data. By doing so, we create a complete public key ready for hashing.

Step 5: The last part will be to compare the server’s public key hash with the hash that we saved in our code.

Step 6: In case of success, we call the completion handler with SecTrust.

Conclusion

In this tutorial, we’ve gone through the implementation of SSLPinningManager for SSL pinning in iOS. While there are other approaches, this method offers several advantages. Notably, it doesn’t rely on third-party libraries and provides a straightforward solution for storing pinning data, such as public key hashes.

I hope you find this article and implementation helpful. Feel free to reach out in the comments section if you have any questions or need further clarification. Happy coding!

--

--