ios - 通过证书进行客户端身份验证的 Swift 3 UrlSession

标签 ios swift authentication ssl client-certificates

我正在使用 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/

相关文章:

ios - 应用内购买重定向到另一个 View Controller

ios - 删除每个 UICollectionViewCell 之间的间距并将其移动到 UICollectionView 的外部单元格

C# 登录并上传到 OneDrive 无需用户交互

reactjs - react SPA中的魔术链接身份验证

ios - UITextView 使用自动布局自动调整大小

ios - EXC_BAD_ACCESS变量存在但不存在?

ios - PFUsers 共享所有相同的 PFObjects?

ios - 下载图像数据时显示进度的最简单方法是什么? [ swift ]

ios - 我如何创建一个带有静态单元格的 UItableViewContoller 并将带有原型(prototype)单元格的 UITableView 插入其中一个静态单元格?

Python - 如何知道您已使用 urllib2 登录