SSL Pinning in iOS

Abhishek Ravi 🇮🇳
6 min readDec 29, 2022

--

What is the ‘Man in The Middle’ Attack ?

Let’s start with some boring definition — A man-in-the-middle (MITM) is a type of cyber attack in which the attackers secretly intercepts the message between client and server.
So, this is the definition which you’ll get on the internet about MITM.

If I have to tell you in a lay-man words, then it will be like —
You’re having a phone conversation with your friend and you have a belief that, only your friend can listen what you’re saying i.e. One to One Conversation.
When some attacker comes in between you two & tape your phone conversation (This is MITM, when there is a third person comes in between your one-to-one connection).

As a consumer, you always wants your tele-communication partner to tell you when your phone conversation is monitored — Yes, this is SSL Pinning for you.

🙋🏻‍♂️ SSL Pinning will tell you when your connection is compromised or monitored.

How SSL Pinning Can Fix

Before we start, let me state what is SSL — Secure Socket Layer.
It’s a cryptographic protocol designed to provide communication security over computer network.

// TODO: I’ll talk about SSL/TLS in a separate article.

So, SSL Pinning is a process of introducing SSL Certificate between the Client App and Server (or, API Gateway) so that, each connection is encrypted & secure. It’s more like, a private key is kept at Server and Public key is distributed to the clients such that each conversation can encrypted by the respective key(s).

Choose between SSL Public Key and SSL Certificate

There are two ways of SSL Pinning in your app —
1. SSL Pinning via Certificate (In this article, we’ll explore this)
2. SSL Pinning via Public Key

AFAIK, there is only one advantage of Public Key over Certificate is Expiration of Key.
Public Key will never expire, where SSL Certificate has some definite time of validity.

Demo Project

I have created a demo project which has Alamofire & URL Session’s data request to a GET API without any SSL Pinning.
You must have little understanding of API Request with Alamofire and URLSession.
Further, we’re making request with SSL Pinned in this article.
Before we move ahead, please clone this project from ‘feature/without-ssl-pinning’ branch.

git clone -b feature/pre-ssl-pinning git@github.com:greenSyntax/ssl-pinning-demo.git

Branch ‘main’ will have the final code after SSL Pinning, which you can checkout later.

Prerequisite for SSL Pinning

You need a free API which you can use for demonstration. Here, I will be using run.mocky.io and it’s SSL Public Certificate.
Later, in this article I will be helping you how to get SSL Certificate.

Here, we have Mocky’s GET URL —

https://run.mocky.io/v3/77e82362-7222-46fa-a6ab-c2203c9df461
{
"id": 1,
"userId": 123,
"title": "Hello World",
"body": "iOS SSL Pinning"
}

Since, we are using run.mocky.io, so we’ll need a Public SSL Certificate which is bound with this domain-name that we will use in our demo app.

Let’s open the URL in your browser and get the SSL Certificate —
1. Click on the ‘Lock’ icon,

2. Next, you will be exporting SSL Certificate which is bundled with it,

3. Export the Certificate in *.cer formate with DER encoded binary.

Just add this certificate file in your demo project and make sure, you add this in project target.

What is Proxy Application ?

Proxy application will show the HTTP network traffic of your phone or device. Charles is one of the most popular option for this.
Here, I am considering you’ve Charles on your machine (Please bear this with me, later I will write an article on Charles Setup).

Without SSL Pinning

If there is No SSL Pinning in your app, then you can easily monitor the network traffic by any Proxy App like Charles.
You can monitor the response or request, adulterate as per your need. This will behave like MITM.

Meantime, If I run my demo app, this is a screenshot of Charles how it looks like when Charles monitor my Wifi Traffic—

Here, I can see the JSON response what I am getting for my demo app. Yes, this is very threatening as an app user ‘coz, whatever data that is passed to app can be seen here on Charles. Adding to it, you can edit the response and request.

With SSL Pinning

And, when, you enable SSL Pinning for my demo app then app’s network traffic will look some thing like this —

You won’t see the JSON response body if SSL Pinning is enabled for the app which seems secure. Now, you can see what SSL Pinning can do & yes, you must have SSL Pinning in your app if you are interacting with backend server.

SSL Pinning Using Alamofire

If you are using Alamofire in your project then, it will be pretty easy to add SSL Pinning. There will be a session object through which you are creating any data request (or, any request), that session object needs to have a SeverTrustManager instance. That’s all, you are done with SSL Pinning.

Earlier, your code will be like —

// Here, 'AF' is the default session object
AF.request(url).response { response in
//TODO: Handle Response
print(response)
}

Next, you need to write a method which will load the Certificate from the bundle, I am assuming you have kept your *.cer file in main bundle.

struct Certificates {

static let certificate: SecCertificate = Certificates.certificate(filename: "run.mocky.io")

private static func certificate(filename: String) -> SecCertificate {
let filePath = Bundle.main.path(forResource: filename, ofType: "cer")!
let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
let certificate = SecCertificateCreateWithData(nil, data as CFData)!

return certificate
}
}

Next, you need to create a new Session object which will have the Server Trust Manager instance —


let certificates = [
"run.mocky.io":
PinnedCertificatesTrustEvaluator(certificates: [Certificates.certificate],
acceptSelfSignedCertificates: false,
performDefaultValidation: true,
validateHost: true)]

let serverTrustPolicy = ServerTrustManager(
allHostsMustBeEvaluated: true,
evaluators: certificates)

self.session = Session(serverTrustManager: serverTrustPolicy)

Finally, your data request method will look something like this —

self.session.request(url).response { response in
if response.error?.isServerTrustEvaluationError {
//TODO: handle when SSL Validation Failed
}

print(response)
}

Now, when you run your demo with Charles Active on your Wifi Network then it will throw error ‘ServerTrustEvaluationError’ which means, your network is compromised by any proxy app or MITM.

SSL Pinning Using URLSession

In URLSession, you need to enable the URLSessionDelegate and implement the following method —

private lazy var session = URLSession(configuration: .default, delegate: self,
delegateQueue: nil)
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

and, Validate the SSL Certificate data —

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

if let trust = challenge.protectionSpace.serverTrust,
SecTrustGetCertificateCount(trust) > 0 {
if let certificate = SecTrustGetCertificateAtIndex(trust, 0) {
let data = SecCertificateCopyData(certificate) as Data

if certificates.contains(data) {
completionHandler(.useCredential, URLCredential(trust: trust))
return
} else {
//TODO: Throw SSL Certificate Mismatch
}
}

}
completionHandler(.cancelAuthenticationChallenge, nil)
}
private let certificates: [Data] = {
let url = Bundle.main.url(forResource: "run.mocky.io", withExtension: "cer")!
let data = try! Data(contentsOf: url)
return [data]
}()

This is it, now you request will be pinned with the SSL Certificate and it case it got monitored, then you‘ll get SSL Certificate Mismatch error.

Github

Clone the project from `main` branch for the SSL Pinning code.
https://github.com/greenSyntax/ssl-pinning-demo 🌎

Conclusion

After all the pain, if we have to recall why we really need SSL Pinng in any app —
1. To safeguard app from MITM Attack.
2. Secure the communication between client & server.

Yes, that all for now. Stay Safe, be it from Covid or MITM attack 😄

--

--