我们正在尝试制作一个应用程序,该应用程序将使用 NSURLSession 通过 HTTPS 与我们的多个服务器进行通信,因为我们使用的是我们自己的根 CA,它捆绑在我们的应用程序中,并且 ATS 完全启用(NSAllowsArbitraryLoads 设置为 False)。
在调用 NSURLSession 身份验证委托即“URLAuthenticationChallenge”时,我们通过“SecTrustSetAnchorCertificates”设置锚证书,该证书通过验证证书的签名以及证书链中证书的签名来验证证书,直到锚证书。还使用 SecTrustSetAnchorCertificatesOnly 排除其他锚点。
成功执行 SecTrustEvaluate 后,在 SessionDataTask completionHandler 中收到错误,如下所述:
[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."
笔记:
在设备上安装并信任根 CA 证书后,启用 ATS 的 HTTPS 通信可以正常工作,不会出现任何错误。但我不希望用户手动安装和信任设备上的根 CA。
代码片段:
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)
}
}