iOS certificate pinning with Swift and NSURLSession
Swift 3+ Update:
Just define a delegate class for NSURLSessionDelegate
and implement the didReceiveChallenge function (this code is adapted from the objective-c OWASP example):
class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
// Adapted from OWASP https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#iOS
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
if let serverTrust = challenge.protectionSpace.serverTrust {
let isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil)
if(isServerTrusted) {
if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
let serverCertificateData = SecCertificateCopyData(serverCertificate)
let data = CFDataGetBytePtr(serverCertificateData);
let size = CFDataGetLength(serverCertificateData);
let cert1 = NSData(bytes: data, length: size)
let file_der = Bundle.main.path(forResource: "certificateFile", ofType: "der")
if let file = file_der {
if let cert2 = NSData(contentsOfFile: file) {
if cert1.isEqual(to: cert2 as Data) {
completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:serverTrust))
return
}
}
}
}
}
}
}
// Pinning failed
completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}
}
(you can find a Gist for Swift 2 here - from the initial answer)
Then create the .der
file for your website using openssl
openssl s_client -connect my-https-website.com:443 -showcerts < /dev/null | openssl x509 -outform DER > my-https-website.der
and add it to the xcode project. Double check that it's present in the Build phases
tab, inside the Copy Bundle Resources
list. Otherwise drag and drop it inside this list.
Finally use it in your code to make URL requests:
if let url = NSURL(string: "https://my-https-website.com") {
let session = URLSession(
configuration: URLSessionConfiguration.ephemeral,
delegate: NSURLSessionPinningDelegate(),
delegateQueue: nil)
let task = session.dataTask(with: url as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print("error: \(error!.localizedDescription): \(error!)")
} else if data != nil {
if let str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) {
print("Received data:\n\(str)")
} else {
print("Unable to convert data to text")
}
}
})
task.resume()
} else {
print("Unable to create NSURL")
}
Public Key pinning with NSURLSession
You can have a look at https://github.com/datatheorem/TrustKit . It's an iOS library that makes it easy to do public key pinning, and it works with NSURLSession.
SSL pinning not working (Objective-C, using NSURLSession)
I figured out the issue .
- You must always download ssl certificate when you are out of your local office network or wifi . Because if you download your
certificate within vpn / office LAN / office wifi , your certificate
will be tempered / overwritten . Thus when you will pin the certificate which you downloaded in your secure private office network then , then it will never match
the remote certificate as remote certificate is always public.Therefore , download the certificate from open wifi or your own data plan :) and then pin this certificate in your code and i am sure
then it will match the remote certificate.
- You must always download ssl certificate when you are out of your local office network or wifi . Because if you download your
Public Key Pinning in Swift 2
The following code might be helpful: link
import UIKit
import Foundation
class ViewController: UIViewController, NSURLSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
httpGet(NSMutableURLRequest(URL: NSURL(string: "https://example.com")!))
}
func httpGet(request: NSMutableURLRequest!) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
}).resume()
}
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
}
Alamofire, ssl pinning on subdomain address
class WildcardServerTrustPolicyManager: ServerTrustManager {
override func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? {
if let policy = evaluators[host] {
return policy
}
var domainComponents = host.split(separator: ".")
if domainComponents.count > 2 {
domainComponents[0] = "*"
let wildcardHost = domainComponents.joined(separator: ".")
return evaluators[wildcardHost]
}
return nil
}
}
Implementation:
let evaluators: [String: ServerTrustEvaluating] = [
"*.airmacau.com.mo": evaluator
]
let manager = WildcardServerTrustPolicyManager(evaluators: evaluators)
Session Manager Config:
sessionManager = Session(configuration: URLSessionConfiguration.default, delegate: SessionDelegate(), serverTrustManager: manager)
How do I accept a self-signed SSL certificate using iOS 7's NSURLSession and its family of delegate methods for development purposes?
This works for me:
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:Nil];
...
...
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
if([challenge.protectionSpace.host isEqualToString:@"mydomain.example"]){
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
}
}
Related Topics
Invalid Signature - Code Object Is Not Signed at All
Phone Call Number with Hashtag on iOS
Missing Compliance Status in Testflight
Is There a Public API for Card View UI That Can Be Seen Across iOS 10
Xcode Unit Testing with Cocoapods
How to Create .Ipa File for Testing Using Runner.App
Draw iOS 7-Style Squircle Programmatically
Which Tasks Are More Suitable to Nsoperation Than Gcd
Change Table to Edit Mode and Delete Rows Inside a Normal Viewcontroller
How to Fetch User's Email Using Facebook
How to Use Git Properly with Xcode
How to Change Font of Uibutton with Swift
Uicollectionview's Cellforitematindexpath Is Not Being Called
Apply Gradient Color to Arc Created with Uibezierpath
How to Solve "Error Running Pod Install" in Flutter on MAC
Uisearchdisplaycontroller's Searchresultstableview's Contentsize Is Incorrect. Bug in iOS 7