We are trying to make an app which will communicate with our multiple servers over HTTPS using NSURLSession, in that we are using our own Root CA which is bundled in our app and ATS fully enabled (NSAllowsArbitraryLoads set to False).
On call of NSURLSession Authentication delegate i.e “URLAuthenticationChallenge” we are setting anchor cert through “SecTrustSetAnchorCertificates” which validates a certificate by verifying its signature plus the signatures of the certificates in its certificate chain, up to the anchor certificate. Also used SecTrustSetAnchorCertificatesOnly to exclude other anchors.
After successful execution of SecTrustEvaluate, received an error in SessionDataTask completionHandler which is mentioned below:
[4704:1445043] ATS failed system trust
[4704:1445043] System Trust failed for [1:0x1c41678c0]
[4704:1445043] TIC SSL Trust Error [1:0x1c41678c0]: 3:0
[4704:1445043] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."
Note:
When Root CA Certificate is installed and trusted on the device then HTTPS communication with ATS enabled works without any error. But I don't want user to manually install and trust the Root CA on the device.
Code Snippet:
func getRequest(){
let requestUrl = “https://server.test.com/hi.php” //url modified
let configuration = URLSessionConfiguration.default
var request = try! URLRequest(url: requestUrl, method: .get)
let session = URLSession(configuration: configuration,
delegate: self,
delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
print("Data = \(data)")
print("Response = \(response)")
print("Error = \(error)")
})
task.resume()
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping(URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void){
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let trust = challenge.protectionSpace.serverTrust
let rootCa = “root"
if let rootCaPath = NSBundle.mainBundle().pathForResource(rootCa, ofType: "der") {
if let rootCaData = NSData(contentsOfFile: rootCaPath) {
let rootCert = SecCertificateCreateWithData(nil, rootCaData).takeRetainedValue()
SecTrustSetAnchorCertificates(trust, [rootCert])
SecTrustSetAnchorCertificatesOnly(trust, true)
}
var trustResult: SecTrustResultType = 0
SecTrustEvaluate(trust, &trustResult)
if (Int(trustResult) == kSecTrustResultUnspecified ||
Int(trustResult) == kSecTrustResultProceed) {
// Trust certificate.
} else {
NSLog("Invalid server certificate.")
}
} else {
challenge.sender.cancelAuthenticationChallenge(challenge)
}
}