Last active
October 3, 2018 10:51
-
-
Save 0xPr0xy/49f30d61737f8d4840463e350781ff6e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CertificatePinner.swift | |
// BenuApp | |
// | |
// Created by Peter IJlst | The Mobile Company on 02/10/2018. | |
// Copyright © 2018 The Mobile Company. All rights reserved. | |
// | |
import WebKit | |
import Alamofire | |
/// Class that provides certificate pinning logic for Alamofire and WKWebView requests | |
class CerPinsky: SessionDelegate { | |
/// Return a session manager configured for certificate pinning | |
lazy var sessionManager: SessionManager = { | |
sessionDidReceiveChallengeWithCompletion = { session, challenge, completionHandler in | |
self.handleChallenge(challenge: challenge, completionHandler: completionHandler) | |
} | |
return SessionManager(configuration: .default, delegate: self, serverTrustPolicyManager: nil) | |
}() | |
/// Execute certificate pinning | |
/// | |
/// - Parameters: | |
/// - challenge: the authentication challenge | |
/// - completionHandler: the completion handler | |
private func handleChallenge(challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { | |
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, | |
let serverTrust = challenge.protectionSpace.serverTrust else { | |
completionHandler(.cancelAuthenticationChallenge, nil) | |
return | |
} | |
var secResult: SecTrustResultType = .invalid | |
let status = SecTrustEvaluate(serverTrust, &secResult) | |
guard status == errSecSuccess, | |
let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 1) else { | |
completionHandler(.cancelAuthenticationChallenge, nil) | |
return | |
} | |
let certData = SecCertificateCopyData(serverCert) | |
let data = CFDataGetBytePtr(certData); | |
let size = CFDataGetLength(certData); | |
let domainCertificate = NSData(bytes: data, length: size) | |
let includedCertificatePaths = Bundle.main.paths(forResourcesOfType: "crt", inDirectory: "") | |
for includedCertificatePath in includedCertificatePaths { | |
if let includedCertificate = NSData(contentsOfFile: includedCertificatePath) { | |
if domainCertificate.isEqual(to: includedCertificate as Data) { | |
completionHandler(.useCredential, URLCredential(trust: serverTrust)) | |
return | |
} | |
} | |
} | |
completionHandler(.cancelAuthenticationChallenge, nil) | |
return | |
} | |
} | |
// MARK: - WKNavigationDelegate Conformance | |
extension CerPinsky: WKNavigationDelegate { | |
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, | |
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { | |
handleChallenge(challenge: challenge, completionHandler: completionHandler) | |
} | |
} | |
// MARK: - UIWebViewDelegate Conformance | |
extension CerPinsky: UIWebViewDelegate { | |
override func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, | |
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { | |
handleChallenge(challenge: challenge, completionHandler: completionHandler) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment