Unlike Android, where libraries like OkHttp abstract much of the complexity, iOS takes a more low-level approach to networking and security.
This means one thing:
You have more control — but also more responsibility.
In this second part, we’ll explore how SSL pinning is implemented in iOS using two different strategies:
Certificate Pinning (
.cer)Public Key Pinning (recommended for production)
Both approaches achieve the same goal — trusting only your backend — but they differ significantly in terms of stability, maintainability, and real-world viability.
We’ll also take a step back and look at the bigger picture:
When pinning makes sense
When it becomes a liability
And how it fits into a broader mobile security strategy
Let’s dive in.
🍎 iOS Implementation
iOS is more low-level. You’ll work with:
- URLSession
- URLSessionDelegate
- Security.framework
There are two approaches:
🟢 Approach 1: Certificate Pinning with .cer
This is typically the first approach developers encounter when implementing pinning on iOS.
🔧 Steps
- Export your backend certificate as .cer
- Add it to your Xcode project
- Compare it at runtime
🧪 Example
class PinningDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust,
let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let serverCertData = SecCertificateCopyData(serverCertificate) as Data
guard let localCertPath = Bundle.main.path(forResource: "server", ofType: "cer"),
let localCertData = try? Data(contentsOf: URL(fileURLWithPath: localCertPath)) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
if serverCertData == localCertData {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
⚠️ Downside (critical)
This approach is:
💣 Fragile
- Certificates expire
- Renewal changes .cer
- Your app breaks
👉 You must release a new version of the app
🟡 Approach 2: Public Key Pinning (Recommended)
Instead of comparing full certificates:
👉 Compare public keys
✔️ Advantages
- Survives certificate renewal
- More stable in production
- Equivalent to Android approach
🧪 Conceptual Example
let serverPublicKey = SecCertificateCopyKey(serverCertificate)
let localPublicKey = SecCertificateCopyKey(localCertificate)
// Compare keys or their hashes
// ⚠️ Apple APIs here are verbose and require careful handling.
🧭 Better Option: Use a Library
Instead of manual implementation, use:
- Alamofire (widely used networking library)
Example
let evaluators: [String: ServerTrustEvaluating] = [
"api.yourservice.com": PinnedCertificatesTrustEvaluator()
]
let manager = ServerTrustManager(evaluators: evaluators)
let session = Session(serverTrustManager: manager)
🔍 What SSL Pinning DOES NOT Do
Let’s be clear:
| Feature | Covered by Pinning |
|---|---|
| Encrypt traffic | ✔ (via TLS) |
| Prevent MITM | ✔ |
| Authenticate user | ❌ |
| Protect API access | ❌ |
| Replace VPN | ❌ |
👉 You still need:
- JWT / OAuth
- API Gateway
- Rate limiting
- Backend security
⚠️ Real-World Trade-offs
Before adding pinning, ask yourself:
❗ Operational cost
- Certificate rotation becomes risky
- You need fallback pins
- You need monitoring
❗ Release dependency
- A backend change can break clients instantly
❗ Debugging complexity
- Harder to inspect traffic (Charles Proxy, etc.)
🧠 When Should You Use It?
Use pinning if:
- You build fintech / healthcare apps
- You operate in hostile network environments
- You have strong DevOps practices
Avoid (or delay) if:
- You’re building a typical consumer app
- You don’t control backend infrastructure
- Your team lacks experience with cert rotation
🧩 Recommended Architecture (No VPN)
Mobile App
↓
HTTPS (TLS)
↓
API Gateway
↓
Authentication (JWT / OAuth)
↓
Microservices
Optional hardening:
- Certificate Pinning 🔐
- WAF 🛡️
- Rate limiting 🚦
🧠 Final Thoughts
“SSL pinning” is often mentioned casually, but:
👉 It’s not a silver bullet
👉 It’s not a replacement for authentication
👉 It’s not trivial to maintain
Used correctly, it adds a strong extra layer of defense.
Used blindly, it becomes a production risk.
👋 Closing
If you’re working with Kotlin Multiplatform or shared logic, keep in mind:
- Pinning is platform-specific
- You’ll need separate implementations for Android and iOS

Top comments (0)