Article by Pushkar Deshmukh

Certificate Pinning in iOS (Part 1): A Practical Guide Using URLSession

Learn how to implement certificate pinning in iOS using URLSession with a simple, real-world approach. This guide also explains SSL certificates (root, intermediate, and leaf) in an easy-to-understand way and how they impact pinning decisions.

Pushkar Deshmukh

Pushkar Deshmukh

Senior iOS Engineer

March 29, 20264 min read18 views
iOSSwiftSecurityCertificate PinningURLSessionNetworkingSSLTLSApp Security

🚀 Introduction

When your iOS app talks to a backend server, it relies on HTTPS (SSL/TLS) to secure communication.

But here’s the catch:

👉 Even HTTPS can be compromised via Man-in-the-Middle (MITM) attacks if a malicious certificate is trusted by the system.

That’s where Certificate Pinning comes in.

Certificate pinning ensures your app only trusts specific certificates — not just any certificate trusted by the system.

In this blog, we’ll:

  • Understand how SSL certificates work (in simple terms)

  • Learn about Root, Intermediate, and Leaf certificates

  • Implement Certificate Pinning using URLSession


🔐 What is Certificate Pinning?

Normally, iOS trusts a server if:

  • Its certificate is valid

  • It is signed by a trusted Certificate Authority (CA)

👉 With certificate pinning, we add an extra rule:

“Only trust this specific certificate (or set of certificates), even if others are valid.”


🌳 Understanding the Certificate Chain (Very Important)

Before jumping into code, you need to understand how certificates actually work.

Think of it like a trust chain:

Root Certificate (Top Authority)
        ↓
Intermediate Certificate
        ↓
Leaf Certificate (Your Server)

🥇 1. Root Certificate

  • Issued by a trusted Certificate Authority (CA)

  • Pre-installed in iOS (Apple maintains this list)

  • Example: DigiCert, GlobalSign

👉 Key point:

  • You don’t control this

  • It rarely changes


🥈 2. Intermediate Certificate

  • Issued by the Root CA

  • Used to sign server (leaf) certificates

👉 Why it exists:

  • Security (root key stays offline)

  • Flexibility for certificate management


🥉 3. Leaf Certificate (Server Certificate)

  • This is your server’s certificate (e.g., api.yourapp.com)

  • Issued by an intermediate certificate

👉 Key point:

  • This is what your app connects to

  • This changes frequently (renewals, updates)


⚖️ How Each Certificate Affects Pinning

Certificate Type

Stability

Control

Pinning Suitability

Root

Very High

No

❌ Not recommended

Intermediate

Medium

No

⚠️ Risky

Leaf

Low

Yes

✅ Most common


🧠 Practical Insight

  • Leaf pinning → More secure, but breaks when cert renews

  • Intermediate pinning → Less maintenance, but slightly weaker

  • Root pinning → Too broad, defeats purpose

👉 In most real-world apps:

✅ Leaf certificate pinning is preferred


🛠️ Part 1: Certificate Pinning using URLSession

We’ll implement pinning using:

  • URLSession

  • URLSessionDelegate


📥 Step 1: Get the Certificate

  1. Open your API URL in browser (https://api.yourserver.com)

  2. Download the certificate

  3. Convert it to .cer format

  4. Add it to your Xcode project

👉 Make sure:

  • Target membership is enabled


🧩 Step 2: Create a Pinned Session Delegate

import Foundation

final class PinnedSessionDelegate: NSObject, URLSessionDelegate {
    
    private let pinnedCertificates: [Data]
    
    override init() {
        // Load local certificate
        guard let certPath = Bundle.main.path(forResource: "server", ofType: "cer"),
              let certData = try? Data(contentsOf: URL(fileURLWithPath: certPath)) else {
            fatalError("Certificate not found")
        }
        
        self.pinnedCertificates = [certData]
    }
    
    func urlSession(
        _ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
    ) {
        
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let serverTrust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        // Get server certificate
        let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0)
        
        guard let cert = serverCertificate else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        let serverCertData = SecCertificateCopyData(cert) as Data
        
        // Compare with pinned certificate
        if pinnedCertificates.contains(serverCertData) {
            let credential = URLCredential(trust: serverTrust)
            completionHandler(.useCredential, credential)
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }
}

🌐 Step 3: Use the Custom Session

let session = URLSession(
    configuration: .default,
    delegate: PinnedSessionDelegate(),
    delegateQueue: nil
)

let url = URL(string: "https://api.yourserver.com/data")!

let task = session.dataTask(with: url) { data, response, error in
    if let error = error {
        print("Error: \(error)")
        return
    }
    
    print("Success")
}

task.resume()

⚠️ Common Mistakes (Important)

❌ 1. Pinning wrong certificate

  • Always verify you’re using correct .cer

❌ 2. Not handling certificate renewal

  • Leaf certificates expire → app breaks

❌ 3. Using default session accidentally

  • Pinning only works with your custom delegate


🧪 How to Test Pinning

  • Use tools like:

    • Charles Proxy

    • Proxyman

👉 If pinning works:

  • Requests should fail when intercepted


📊 When Should You Use Certificate Pinning?

Use it when:

  • You handle sensitive data (banking, payments)

  • You want extra security beyond HTTPS

Avoid it when:

  • Backend certificates change frequently

  • You don’t control backend infra


🔮 What’s Next (Part 2)

In Part 2, we’ll cover:

👉 Public Key Pinning

  • More flexible than certificate pinning

  • Survives certificate renewals

  • Used by large-scale apps


🧠 Final Thoughts

Certificate pinning is powerful—but comes with tradeoffs.

Security vs Maintainability

Start simple:

  • Implement leaf certificate pinning

  • Monitor certificate expiry

  • Move to public key pinning for scalability

Pushkar Deshmukh

Written by

Pushkar Deshmukh

Senior iOS Engineer

11+ years of experience building mobile and web applications. Passionate about Swift, React, and sharing knowledge through technical writing.

18views0comments

Comments

Loading…

Loading comments…

Read next

View all
Back to All Articles