我使用URLSession通过主包中包含的TLS 1.2协议和证书(都是自签名的)发出get请求。我设法进行固定,但服务器也需要客户端证书进行身份验证,因此我尝试使用UrlCredential响应AuthenticationChallenge,但它无法正常工作:我不断获取NSURLErrorDomain Code=-1206这是&# 34;服务器“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' -successfully-返回一个结构,其中包含从PKCS12中提取的标识,证书链和信任的指针;我知道身份和证书应该存储在钥匙串中,但目前我只是将它们包含在捆绑包中,主要是因为钥匙串的文档不是很好。
我还将应用传输安全设置添加到我的Info.plist文件like this
看起来客户端甚至没有尝试进行身份验证,所以我错过了一些东西,我猜......
答案 0 :(得分:0)
如果正在调用getClientCredential()函数,那么您的客户端正在尝试进行身份验证。如果没有,则服务器日志(例如/var/log/nginx/access.log)可能指示原因。
this answer中的PKCS12课程为我工作。
关于钥匙串,this Apple documentation说
要在自己的应用中使用数字身份,您需要编写代码来导入它们。这通常意味着读取PKCS#12格式的blob,然后使用证书,密钥和信任服务参考中记录的SecPKCS12Import函数将blob的内容导入应用程序的钥匙串。
这样,您的新钥匙串项目就会使用您应用的钥匙串访问组创建。