我正在使用 URLSession 发出包含在主包中的 TLS 1.2 协议(protocol)和证书(都是自签名的)的获取请求。我设法进行了固定,但服务器还需要客户端证书进行身份验证,因此我尝试使用 UrlCredential 响应 AuthenticationChallenge 但它不起作用:我不断收到 NSURLErrorDomain Code=-1206这是“服务器“my_server_domain.it”需要客户端证书。”
这是我的要求:
func makeGetRequest(){
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()
}
URLSessionDelegate,我在其中响应 AuthenticationChallenge:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let authenticationMethod = challenge.protectionSpace.authenticationMethod
print("authenticationMethod=\(authenticationMethod)")
if authenticationMethod == NSURLAuthenticationMethodClientCertificate {
completionHandler(.useCredential, getClientUrlCredential())
} else if authenticationMethod == NSURLAuthenticationMethodServerTrust {
let serverCredential = getServerUrlCredential(protectionSpace: challenge.protectionSpace)
guard serverCredential != nil else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
completionHandler(.useCredential, serverCredential)
}
}
服务器证书固定:
func getServerUrlCredential(protectionSpace:URLProtectionSpace)->URLCredential?{
if let serverTrust = protectionSpace.serverTrust {
//Check if is valid
var result = SecTrustResultType.invalid
let status = SecTrustEvaluate(serverTrust, &result)
print("SecTrustEvaluate res = \(result.rawValue)")
if(status == errSecSuccess),
let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
//Get Server Certificate Data
let serverCertificateData = SecCertificateCopyData(serverCertificate)
//Get Local Certificate NSData
let localServerCertNSData = certificateHelper.getCertificateNSData(withName: "localServerCertName", andExtension: "cer")
//Check if certificates are equals, otherwhise pinning failed and return nil
guard serverCertificateData == localServerCertNSData else{
print("Certificates doesn't match.")
return nil
}
//Certificates does match, so we can trust the server
return URLCredential(trust: serverTrust)
}
}
return nil
}
这里是我从 PKCS12 (.pfx) 证书获取客户端 URLCredential 的地方:
func getClientUrlCredential()->URLCredential {
let userCertificate = certificateHelper.getCertificateNSData(withName: "certificate",
andExtension: "pfx")
let userIdentityAndTrust = certificateHelper.extractIdentityAndTrust(fromCertificateData: userCertificate, certPassword: "cert_psw")
//Create URLCredential
let urlCredential = URLCredential(identity: userIdentityAndTrust.identityRef,
certificates: userIdentityAndTrust.certArray as [AnyObject],
persistence: URLCredential.Persistence.permanent)
return urlCredential
}
请注意,func 'extractIdentityAndTrust' - 成功 - 返回一个结构,其中包含指向从 PKCS12 中提取的身份、证书链和信任的指针;我知道身份和证书应该存储在钥匙串(keychain)中,但目前我只是将它们包含在 bundle 中,主要是因为钥匙串(keychain)的文档一点也不好。
我还在我的 Info.plist 文件中添加了 App Transport Security Settings like this
看起来客户端甚至没有尝试进行身份验证,所以我想我漏掉了一些东西......
最佳答案
如果您的 getClientCredential() 函数被调用,那么您的客户端正在尝试进行身份验证。如果不是,则服务器日志(例如/var/log/nginx/access.log)可能会指出原因。
this answer 中的 PKCS12 类为我工作。
关于钥匙串(keychain),this Apple documentation说
To use digital identities in your own apps, you will need to write code to import them. This typically means reading in a PKCS#12-formatted blob and then importing the contents of the blob into the app's keychain using the function SecPKCS12Import documented in Certificate, Key, and Trust Services Reference.
This way, your new keychain items are created with your app's keychain access group.
关于ios - 通过证书进行客户端身份验证的 Swift 3 UrlSession,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44023540/