Skip to content

Instantly share code, notes, and snippets.

@tempire
Last active September 1, 2022 09:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tempire/1e6191e810636a638d0f203c8240d2b8 to your computer and use it in GitHub Desktop.
Save tempire/1e6191e810636a638d0f203c8240d2b8 to your computer and use it in GitHub Desktop.
WKWebView with client certificate
//
// ViewController.swift
// CertificateRequest
//
// Created by Glen Hinkle on 3/4/19.
// Copyright © 2019 Zombie Dolphin LLC. All rights reserved.
//
// Works with iOS 12
//
import UIKit
import WebKit
class ViewController: UIViewController {
@IBOutlet weak var contentView: UIView!
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
contentView.addSubview(webView)
contentView.pinToInside(webView)
webView.load(
url: URL(
string: "https://some-web-server-that-requires-client-certificate-authentication")!)
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print(navigation)
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let method = challenge.protectionSpace.authenticationMethod
switch method {
case NSURLAuthenticationMethodClientCertificate:
sendClientCertificate(for: challenge, via: completionHandler)
default:
completionHandler(.performDefaultHandling, .none)
}
}
func sendClientCertificate(for challenge: URLAuthenticationChallenge, via completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let path = Bundle.main.path(forResource: "some-certificate-file", ofType: "p12"),
let data = try? Data(contentsOf: URL(fileURLWithPath: path)),
let credential = credential(from: data, withPassword: "some-certificate-password") else {
challenge.sender?.cancel(challenge)
return completionHandler(.rejectProtectionSpace, .none)
}
return completionHandler(.useCredential, credential);
}
func credential(from data: Data, withPassword password: String) -> URLCredential? {
guard let security = security(from: data, withPassword: password) else {
return .none
}
return URLCredential(
identity: security.identity,
certificates: security.certificates,
persistence: .permanent
)
}
func security(from data: Data, withPassword password: String) -> (identity: SecIdentity, trust: SecTrust, certificates: [SecCertificate])? {
var _items: CFArray?
let securityError = SecPKCS12Import(data as NSData,
[ kSecImportExportPassphrase as String : password ] as CFDictionary,
&_items);
guard let items = _items as? [Any],
let dict = items.first as? [String:Any],
securityError == errSecSuccess else {
return .none
}
let identity = dict["identity"] as! SecIdentity
let trust = dict["trust"] as! SecTrust;
// Certificate chain
var certificate: SecCertificate!
SecIdentityCopyCertificate(identity, &certificate);
return (identity, trust, [certificate]);
}
}
@ealloradai
Copy link

Hi,
in this code you are providing a p12 to perform an SSL client authentication.
If private key is on external token (accessible in some way) is it still possible do to client auth?
Thanks

@dariocast
Copy link

I am interested in performing the same. I achieved doing this only using a custom nsurlprotocol, forcing wkwebview using it. It is not the right way and I am looking for something similar

Hi,
in this code you are providing a p12 to perform an SSL client authentication.
If private key is on external token (accessible in some way) is it still possible do to client auth?
Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment