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.
// Struct to save values of the Cert.
// Method to extract the identity, certificate chain and trust
// Delegate method of URLSession
// Struct to save values of the Cert.
Code Block struct IdentityAndTrust { var identityRef: SecIdentity var trust: SecTrust var certificates: [SecCertificate] }
// Method to extract the identity, certificate chain and trust
Code Block 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
Code Block 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) }
Code Block } 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}