Certificate pinning technique on iOS

Using swift

Anderson Santos Gusmão
7 min readOct 10, 2017

Hi, folks! Today we are going to talk about security. To completely comprehend these concepts presented here I need to provide some background knowledge about how SSL/TLS works and other stuff. Of course, it is not a deep dive, it is just a simple explanation before starting the main subject, if you already know it, it is ok, just step out this part and go to next section. At the final we are going to see a practical example of how to do certificate pinning on a iOS project, let’s go!

Part 1 — SSL/TLS at a glance

What is it?

SSL/TLS ensures transparently encryption on client-server communication over HTTPS, i.e (HTTP over SSL/TLS). The encryption is based on Public Key Infrastructure (PKI for short). It is transparent because the application layer knows nothing about this encryption process, the network infrastructure takes care of everything, as programmers we just compose our application requests putting our headers and bodies with plain-text inside requisitions and the network infrastructure does all the job for us.

Handshake

Before sending any application data, the server and client negotiate about how to encrypt/decrypt data, this process is called handshake, in the image below you can see a simplified view about this process:

SSL Handshake process (Simplified view from Google images)

After handshaking

Encrypted data (Image from Google images)

At this moment, if anyone tries to inspect the messages exchanged between client and server on data transportation, nothing will be human-readable, all traffic is encrypted on transit, i.e, it is just deciphered on the client and server perspective.

Certificate validation process

Certificate warning (Image from Google images)

During the handshake process, the server presents a digital certificate and the client needs to trust in the certificate issuer, based on a predefined trust certification authorities list, a. k. a. trusted root CA store. On a secure connection, if the presented certificate was issued by an unknown issuer, generally browsers shows a warning ⚠️ for the user to decide if he or she must proceed with the connection or not. This behavior on apps is different by default, where the connection is interrupted. It is possible you set as trusted a self-signed certificate on your device, in this case, the client will trust in this certificate in the subsequent connections.

Man in the middle attack

Man in the middle attack (Image from Imperva, Inc)

What would happen if instead of connecting to the original server, your connection passes through another server faking as an original server? See a brief description of what is a man in the middle attack from Imperva Incapsula web site.

A man in the middle (MITM) attack is a general term for when a perpetrator positions himself in a conversation between a user and an application — either to eavesdrop or to impersonate one of the parties, making it appear as if a normal exchange of information is underway.

In this situation someone can see all traffic and all information can be captured by an attacker.

Part 2 — Certificate pinning

What is it?

With the above background presented we can better understand what certificate pinning is. As we know, during the handshake 🤝 process, the server will present a certificate that must be trusted or not by the client. The certificate pinning technique consists in to save the target’s server certificate within the app bundle and trust just and only in it.

Our example

Let’s see a practical source code, for this purpose I created a stub authenticate rest service published on Heroku and a client iOS project.

At the moment of this publication, the generated certificate is not expired and the service is up. Of course, I cannot guarantee this over time, but if you understand the concept correctly, you can change the certificate and hostname to reach your own service successfully.

In our example we are going to reach the following URL https://floating-stream-25740.herokuapp.com/authentication/login using a post method to submit the request shown below:

POST /authentication/login HTTP/1.1
Host: floating-stream-25740.herokuapp.com
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache

id=user&password=pass

The id or password informed does not matter, it was created only for test. The output always will be:

{
“Id”: “user”,
“name”: “Sergio Mallandro”,
“preferences”: [“XPTO”,“XPTA”,“XPTZ” ]
}

Necessary tools

For testing purposes, we are going to need some web debugger to act as a proxy to simulate the man in the middle attack. For Mac users like us, I suggest two options, Charles Web Debugging proxy application or Burp. Both are commercial solutions, but you can get a trial or free edition. In case you are running Windows on another machine you can get Fiddler which is an excellent free option.

Client Sample project

Sample project

Our client application is published on GitHub at:

Where the things happen

The project is very simple, it is just one view controller class calling another API class to make a request in order to put its result on a TextView control. To simplify I put here the part of the code where is the pinning logic, basically, we have a URLSession making our request and the API class implement URLSessionDelegate to treat urlSession method. Inside urlSession method we have the logic to compare the remote certificate with the local certificate, if the code gets some divergence, the request operation will be canceled.

How to test it (Using Charles Web Debugging Proxy)

The first thing to do is run the Charles Proxy, when you open it for the first time, it will ask you some administrative rights in order to set up your networking configuration when it is running. So when you open it, automatically your proxy settings will be changed to drive your network traffic via Charles, you can check it on your setting as shown in the image below:

Proxy settings

Now if you open your iOS simulator and try to connect on any site via safari or if you run the sample project presented here, you will see the request and response as shown in the image below:

If it is a secure connection, nothing can be read because SSL/TLS protection.

Now try to enable the SSL Proxying:

Oops! The connection has failed! Why? The Charles presented a self-signed certificate instead of the original certificate, in this case when Charles application receives a connection request, it tries to act as the original server from the client perspective and make a new request to the original server. When the original server replies the response, it is replied to the client as if it was the original request, but this certificate presented by Charles is not trustable, we need to make the client trust in it to simulate our man in the middle attack.
It can be done as shown in the image below:

After that, you need to enable full trust for this certificate, if you need to test it on a real device, you can export it and install manually. After that, you need to reset the simulator.

Enabling Charles Certificate

Now, if you try to connect you will see everything on Charles as if it was a simple HTTP connection:

But, if you enable certification pinning, our logic will compare the embedded certificate with the presented Charles certificate, so the connection will be canceled as shown in the image below:

Conclusion

The advantage of this approach restricts the number of signing authorities trusted by your client application, CAs have had several prominent security breaches where signing keys were compromised or where overly permissive certificates were issued. These breaches allow anyone in possession of the signing key to impersonate any SSL/TLS server, meaning they can successfully and transparently read or modify requests to the server and their responses. Another advantage is that your app will not expose your API contract through proxies applications such as Charles. As a disadvantage, your app will stop to work when the server certificate changes, in this case, you need to update your app.

I hope it helps you understand certificate pinning technique, if something is not clear, please comment here, perhaps I can help you and improve this article. I see you again in the future, bye! 👋

--

--

Anderson Santos Gusmão

In my house I’m the boss, my wife is just the decision maker.