iOS: Certificate pinning Part - 1 (Alamofire)

lalit kant
4 min readMar 21, 2022

--

What is Certificate Pinning?

Certificate pinning is one of the basic security mechanisms of network communication.This technique validates the server certificates again, even after SSL handshaking. The developer embeds a list of trustful certificates inside the client application and compare them with the server certificates during runtime. In case of a mismatch between the server’s certificates and the local copy, the connection is simply aborted.

Why Certificate Pinning?

Imagine you are using a public Wi-Fi network on the train. That network might be created by someone who wants to read the data sent by you. Since that person is providing your internet connection, they can create their own certificate and tell you to encrypt your data with it. Because this person created the certificate, they can read all the data inside it and then send it further to the right receiver. You may not see that you’re using the wrong certificate. This technique is called a man-in-the-middle attack. This illustration shows how it works.

How to prevent invalid certificate?

This is where certificate pinning and public key pinning come in. These approaches consist of defining a list of valid certificates/public keys. Every time the app sends data, it checks if that certificate is on the list of trusted certificates/public keys. This list of certificates/public keys may be hard-coded or defined in a file. Both solutions prevent man-in-the-middle attacks and should pass a penetration test. If you want to be even more secure, you may also encrypt the certificate to make it harder to get.

More formal definition:

“Server/Host is associated with a certificate or public key. You configure the app to reject all certificates other than the predefined certificates or public keys. Whenever the app connects to a server, it compares the server certificate with the pinned certificate(s) or public key(s). If and only if they match, the app trusts the server and establishes the connection.”

Implementation

iOS:

  1. For Restful api’s we used public kay pinning(Preferred) as we are using Alamofire for rest api’s.
  2. For GraphQL api’s we used Certificate Pinning Fingerprint as we are using Apollo library for GraphQl.

Certificate setup:

We initially downloaded Intermediate certificate from a secured website URL, I am taking an example of https://stackoverflow.com

Drag drop certificate icon to your desktop by dragging and drop picking icon in screenshot.

Kept in app bundle like below image.

Alamofire:

In Alamofire we need to set Evaluator for hosts means we need to pass a dictionary of host and its ServerTrustEvaluating.

Public Key Pinning:

private var afSession: Session!func certificatePinningWithAlamofirePublicKey() {// Build serverTrust manager for public key, Alamofire 5 +  don't support explicit public key pinning, It extract public key from certificate and compare it with api response public keylet manager = ServerTrustManager(evaluators: NetworkAdapterUtility().buildEvaluators(evaluator_hosts: ["stackoverflow.com"]))afSession = Session(serverTrustManager: manager)certificatePinning()}

Pinning with Pinned certificate:

private var afSession: Session!func certificatePinningWithPinnedCertificatesTrustEvaluator() {let certificates = ["stackoverflow.com":PinnedCertificatesTrustEvaluator(certificates: [Certificates.certificate],acceptSelfSignedCertificates: false,performDefaultValidation: true,validateHost: true)]let serverTrustPolicy = ServerTrustManager(allHostsMustBeEvaluated: true,evaluators: certificates)afSession = Session(serverTrustManager: serverTrustPolicy)certificatePinning()}

Common Method

func certificatePinning() {afSession.request("https://stackoverflow.com/questions/34611112/certificate-pinning-in-alamofire", method: .get).validate().response(completionHandler: { response inswitch response.result {case .success:print(response.data ?? Data())case .failure(let error):switch error {case .serverTrustEvaluationFailed(let reason):/* Try Removing stackoverflow.cer file from project folder compiler will execute serverTrustEvaluationFailed caseerror: Alamofire.AFError.ServerTrustFailureReason.noPublicKeysFoundGoto ServerTrustFailureReason class implementation you will find all SSL pinning failure cases*/// The reason here is a place where you might fine-tune your// error handling and possibly deduce if it's an actualy MITM// or just another error, like certificate issue.//// In this case, this will show `noRequiredEvaluator` if you try// testing against a domain not in the evaluators list which is// the closest I'm willing to setting up a MITM. In production,// it will most likely be one of the other evaluation errors.debugPrint(reason)default:debugPrint(error)}}})}struct Certificates {static let certificate: SecCertificate = Certificates.certificate(filename: "stackoverflow")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}}

Try Removing stackoverflow.cer file from project folder here compiler will execute serverTrustEvaluationFailed case

error: Alamofire.AFError.ServerTrustFailureReason.noPublicKeysFound

Goto ServerTrustFailureReason class implementation you will find all SSL pinning failure cases.

Conclusion: Certificate Pinning with Alamofire is pretty streight-forward and easy. Nevertheless Alamofire is third party framwork, always keep an eye on Alamofire framework updates. There can be some threat to security, so keep your project updated with latest security guidelines.

Download complete code from here. Happy Coding..

https://github.com/LalitKY/SSLPinningAlamofire

--

--

lalit kant

With around 11 years of experience I have worked in all Mobile platforms like iOS, Android, React Native and Flutter. Mainly into iOS.