func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodClientCertificate:
let query: [String:Any] = [kSecAttrAccessGroup as String: kSecAttrAccessGroupToken,
kSecAttrKeyClass as String : kSecAttrKeyClassPrivate,
kSecClass as String : kSecClassIdentity,
kSecReturnAttributes as String : kCFBooleanTrue as Any,
kSecReturnRef as String: kCFBooleanTrue as Any,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnPersistentRef as String: kCFBooleanTrue as Any
]
var result : AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let allItems = result as? [[String: Any]] else {
let errorDescription = SecCopyErrorMessageString(status, nil)
print(errorDescription as Any)
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let items = allItems.filter { item in
return (item["tkid"] as? String)?.starts(with: "Bundle ID") ?? false
// return true
}
// **items are the certificates which is already added in device**
// Let user select which cert to use, handle pin entry and call the completion handler.
let alert = UIAlertController.selectCertAndPinEntryAlert(certs: items, completionHandler: completionHandler)
alert.show()
case NSURLAuthenticationMethodServerTrust:
let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential);
case NSURLAuthenticationMethodHTTPBasic:
print("Basic auth")
default:
completionHandler(.useCredential, nil)
}
}
selectCertAndPinEntryAlert : this method allows user to choose the certificate which is already added in Keychain.
static func selectCertAndPinEntryAlert(certs: [[String : Any]], completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> UIAlertController {
// Select cert UIAlertController.
let alert = UIAlertController(title: "Select certificate", message: nil, preferredStyle: .actionSheet)
certs.forEach { item in
guard let certData = item["certdata"] as? Data else { return }
guard let certificate = SecCertificateCreateWithData(nil, certData as CFData) else { return }
alert.addAction(UIAlertAction(title: certificate.commonName,
style: .default,
handler: { action in
// Pin entry UIAlertController created after the user selected one of the certs.
let passwordAlert = UIAlertController(title: "Enter pin", message: nil, preferredStyle: .alert)
passwordAlert.addTextField { textField in
textField.isSecureTextEntry = true
}
passwordAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
completionHandler(.cancelAuthenticationChallenge, nil)
})
passwordAlert.addAction(UIAlertAction(title: "Ok",
style: .default,
handler: { action in
guard let pin = passwordAlert.textFields?.first?.text else { completionHandler(.cancelAuthenticationChallenge, nil); return }
** let secIdentity = item["v_Ref"] as! SecIdentity
let urlCredential = URLCredential(identity: secIdentity, certificates: nil, persistence: .forSession)**
// If this doesn't return a UserDefaults something is broken in the project and we might as well crash.
UserDefaults(suiteName: "App Group which used by app")!.writePin(pin)
**completionHandler(.useCredential, urlCredential)**
}))
passwordAlert.show()
}))
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
completionHandler(.cancelAuthenticationChallenge, nil)
})
return alert
}
Please provide the suggestion what i am doing wrong. What code i need to change?
Note : I am using Yubikey for fetching certificates and storing it into the keychain. Later All the added certificate popup i ll get to choose one.
I have tried this code but when i am entring the key to access the certificate, WKWebView is not taking this certificate as further process and i am getting URL Credentials as null in response.