通过证书进行客户端身份验证的 Swift 3 UrlSession
Posted
技术标签:
【中文标题】通过证书进行客户端身份验证的 Swift 3 UrlSession【英文标题】:Swift 3 UrlSession with client authentication by certificate 【发布时间】:2017-05-17 11:26:57 【问题描述】:我正在使用 URLSession 使用主包中包含的 TLS 1.2 协议和证书(都是自签名的)发出 get 请求。我设法进行了固定,但服务器还需要客户端证书进行身份验证,因此我尝试使用 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' -successfully- 返回一个结构体,其中包含从 PKCS12 提取的身份、证书链和信任的指针;我知道身份和证书应该存储在钥匙串中,但目前我只是将它们包含在捆绑包中,主要是因为钥匙串的文档并不好。
我还在我的 Info.plist 文件like this中添加了应用传输安全设置@
看起来客户端甚至没有尝试进行身份验证,所以我想我错过了一些东西......
【问题讨论】:
嗨,Kashish,您设法得到答案了吗?我也对在 NSURLAuthenticationMethodServerTrust 停止的类似情况感到震惊。 【参考方案1】:如果您的 getClientCredential() 函数被调用,那么您的客户端正在尝试进行身份验证。如果不是,那么服务器日志(例如 /var/log/nginx/access.log)可能会指出原因。
this answer 中的 PKCS12 类对我有用。
关于钥匙串,this Apple documentation 说
要在您自己的应用中使用数字身份,您需要编写代码来导入它们。这通常意味着读取 PKCS#12 格式的 blob,然后使用证书、密钥和信任服务参考中记录的函数 SecPKCS12Import 将 blob 的内容导入应用的钥匙串。
这样,您的新钥匙串项将使用您应用的钥匙串访问组创建。
【讨论】:
以上是关于通过证书进行客户端身份验证的 Swift 3 UrlSession的主要内容,如果未能解决你的问题,请参考以下文章
Apache HttpClient 4.3 和 x509 客户端证书进行身份验证
通过 HTTPS 上的客户端证书对 WCF 请求进行身份验证