SSL Pinning in iOS (Swift): Certificate & Public Key Pinning Explained

When building iOS apps that communicate with backend servers, security is critical.
Even though HTTPS encrypts data, apps can still be vulnerable to Man-in-the-Middle (MITM) attacks if a malicious certificate is trusted.
SSL Pinning solves this by ensuring your app trusts only a specific certificate or public key, not just any certificate signed by a trusted CA.
In this blog, we’ll cover:
What SSL Pinning is
Certificate Pinning vs Public Key Pinning
How to implement both in Swift
What is SSL Pinning?
SSL Pinning is a technique where an app validates the server’s identity by comparing:
The server certificate, or
The server public key
against a trusted copy bundled inside the app.
If the validation fails → the connection is rejected.
Certificate Pinning vs Public Key Pinning
| Type | Description | Pros | Cons |
| Certificate Pinning | Matches entire SSL certificate | Strong security | Breaks when cert renews |
| Public Key Pinning | Matches only public key | Survives cert renewal | Slightly complex |
Prerequisites
Download your server’s SSL certificate (
.cer)Add it to your Xcode project
Enable Target → Signing & Capabilities → App Transport Security
Common Setup (URLSession Delegate)
class NetworkManager: NSObject, URLSessionDelegate {
lazy var session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
}
Certificate Pinning (Swift)
Step 1: Add Certificate to App Bundle
Add server.cer to your project and ensure Copy Bundle Resources is enabled.
Step 2: Validate Certificate
Connection succeeds only if certificates match exactly
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard
let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0),
let localCertPath = Bundle.main.path(forResource: "server", ofType: "cer"),
let localCertData = try? Data(contentsOf: URL(fileURLWithPath: localCertPath))
else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let serverCertData = SecCertificateCopyData(certificate) as Data
if localCertData == serverCertData {
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
Public Key Pinning (Swift)
Public Key Pinning extracts the public key from the certificate and compares it.
Step 1: Extract Public Key
func publicKey(from certificate: SecCertificate) -> SecKey? {
let policy = SecPolicyCreateBasicX509()
var trust: SecTrust?
SecTrustCreateWithCertificates(certificate, policy, &trust)
return trust.flatMap { SecTrustCopyKey($0) }
}
Step 2: Compare Public Keys
Works even if certificate renews (same key)
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard
let serverTrust = challenge.protectionSpace.serverTrust,
let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0),
let serverKey = publicKey(from: serverCertificate),
let localCertPath = Bundle.main.path(forResource: "server", ofType: "cer"),
let localCertData = try? Data(contentsOf: URL(fileURLWithPath: localCertPath)),
let localCertificate = SecCertificateCreateWithData(nil, localCertData as CFData),
let localKey = publicKey(from: localCertificate)
else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
if serverKey == localKey {
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
Common Mistakes
Forgetting to update cert after expiry (certificate pinning)
Using pinning in debug builds only
Pinning to third-party APIs (can break unexpectedly)
Best Practices
Use Public Key Pinning for production apps
Keep pinning logic modular
Always have a fallback strategy
Monitor certificate expiration dates
Summary
SSL Pinning significantly improves app security by protecting against MITM attacks.
If your app handles payments, authentication, or sensitive data, pinning is highly recommended.
Certificate Pinning → Maximum security
Public Key Pinning → Better long-term stability
About Me
Exploring iOS, Swift, and Kotlin through real-world challenges, clean architecture, and production-ready mobile solutions.




