In one of my application i'm trying to implement certificate Mutual authentication between client and server for my iOS app using URLSession. I was able to extract the identityRef and trust and certificate chain and in didReceivechallenge method i'm checking for the authenticationMethod and creating URLCredential for challenge for URLSession.
Below is my code
// Struct to save values of the Cert.
struct IdentityAndTrust {
var identityRef: SecIdentity
var trust: SecTrust
var certificates: [SecCertificate]
}
// Method to extract the identity, certificate chain and trust
func extractIdentity(certData: NSData, certPassword: String) -> IdentityAndTrust? {
var identityAndTrust: IdentityAndTrust?
var securityStatus: OSStatus = errSecSuccess
var items: CFArray?
let certOptions: Dictionary = [kSecImportExportPassphrase as String : certPassword]
securityStatus = SecPKCS12Import(certData, certOptions as CFDictionary, &items)
if securityStatus == errSecSuccess {
let certificateItems: CFArray = items! as CFArray
let certItemsArray: Array = certificateItems as Array
let dict: AnyObject? = certItemsArray.first
if let certificateDict: Dictionary = dict as? Dictionary<String, AnyObject> {
// get the identity
let identityPointer: AnyObject? = certificateDict["identity"]
let secIdentityRef: SecIdentity = identityPointer as! SecIdentity
// get the trust
let trustPointer: AnyObject? = certificateDict["trust"]
let trustRef: SecTrust = trustPointer as! SecTrust
// get the certificate chain
var certRef: SecCertificate? // <- write on
SecIdentityCopyCertificate(secIdentityRef, &certRef)
var certificateArray = [SecCertificate]()
certificateArray.append(certRef! as SecCertificate)
let count = SecTrustGetCertificateCount(trustRef)
if count > 1 {
for i in 1..<count {
if let cert = SecTrustGetCertificateAtIndex(trustRef, i) {
certificateArray.append(cert)
}
}
}
identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certificates: certificateArray)
}
}
return identityAndTrust
}
// Delegate method of URLSession
public class SessionDelegate : NSObject, URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if let localCertPath = Bundle.main.url(forResource: "my_client", withExtension: "p12"),
let localCertData = try? Data(contentsOf: localCertPath)
{
let identityAndTrust:IdentityAndTrust = extractIdentity(certData: localCertData as NSData, certPassword: "eIwj5Lurs92xtC9B4CZ0")!
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
let urlCredential:URLCredential = URLCredential(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certificates as [AnyObject],
persistence: URLCredential.Persistence.permanent);
completionHandler(URLSession.AuthChallengeDisposition.useCredential, urlCredential);
return
}
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let urlCredential:URLCredential = URLCredential(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certificates as [AnyObject],
persistence: URLCredential.Persistence.forSession);
completionHandler(URLSession.AuthChallengeDisposition.useCredential, urlCredential);
// completionHandler (URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!));
return
}
completionHandler (URLSession.AuthChallengeDisposition.performDefaultHandling, Optional.none);
return
}
challenge.sender?.cancel(challenge)
completionHandler(URLSession.AuthChallengeDisposition.rejectProtectionSpace, nil)
}
}
Below is the response i'm getting
`Project XXXX[1115:755397] [tcp] tcp_output [C22.1:3] flags=[R.] seq=2988084600,
ack=2995213448, w`in=2047 state=CLOSED rcv_nxt=2995213448, snd_una=2988084600
Project XXXX[1115:755397] Connection 22: received failure notification
2021-05-18 12:39:08.000356+0530 Project XXXX[1115:755397] Connection 22: failed to connect
3:-9816, reason -1
2021-05-18 12:39:08.000429+0530 Project XXXX[1115:755397] Connection 22: encountered
error(3:-9816)
finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=https://dev.xxxx.net/oauth/xxx/login, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <75A97E53-2AE1-4CE2-9C0D-64AA5965BCBC>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <75A97E53-2AE1-4CE2-9C0D-64AA5965BCBC>.<1>"
), NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://dev.projectzebra.net/oauth/zebra/login, NSUnderlyingError=0x282d26910 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=1, _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816}}, _kCFStreamErrorCodeKey=-9816}
I'm not sure about the response i have recieved and unable to proceed further, please help if anyone faced similar issue.
Any help is appreciated. Thanks.