Swift 3 UrlSession通过证书进行客户端身份验证 [英] Swift 3 UrlSession with client authentication by certificate

查看:476
本文介绍了Swift 3 UrlSession通过证书进行客户端身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用URLSession通过主捆绑包中包含的TLS 1.2协议和证书(都是自签名的)发出获取请求.我设法进行了固定,但是服务器还需要使用客户端证书进行身份验证,因此我尝试使用UrlCredential响应AuthenticationChallenge,但是它不起作用:我一直在获取

I'm using URLSession to make a get request with TLS 1.2 protocol and certificates (which are all self-signed) included in the main bundle. I managed to do the pinning but server also requires a client certificate for authentication so I'm trying to respond to the AuthenticationChallenge with UrlCredential but it's not working: i keep getting NSURLErrorDomain Code=-1206 which is "The server "my_server_domain.it" requires a client certificate."

这是我的要求:

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:

URLSessionDelegate, where I respond to the 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的地方:

And here is where i obtain the client URLCredential from the PKCS12 (.pfx) certificate:

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中提取的指向身份,证书链和信任的指针;我知道身份和证书应该存储在钥匙串中,但目前我只是将它们包括在捆绑包中,主要是因为钥匙串的文档非常糟糕.

Note that func 'extractIdentityAndTrust' -successfully- returns a struct with pointers to identity, certificate-chain and trust extracted from the PKCS12; I know that identity and certificates should be stored in the keychain but at the moment I'm just including them in the bundle mainly because the documentation for keychain is anything but good.

我还已将App Transport安全设置添加到我的Info.plist文件中,像这样

I've also added App Transport Security Settings to my Info.plist file like this

客户端似乎甚至没有尝试进行身份验证,所以我想丢失了一些东西...

It looks like client doesn't even try to authenticate, so I'm missing something, I guess...

推荐答案

如果正在调用getClientCredential()函数,则您的客户端正在尝试进行身份验证.如果不是,则服务器日志(例如/var/log/nginx/access.log)可能会指出原因.

If your getClientCredential() function is being called, then your client is trying to authenticate. If not, then the server logs (such as /var/log/nginx/access.log) might indicate why.

这个答案对我有用.

关于钥匙串,此Apple文档

要在自己的应用中使用数字身份,您将需要编写代码以导入它们.这通常意味着读取PKCS#12格式的Blob,然后使用证书,密钥和信任服务参考中记录的SecPKCS12Import函数将Blob的内容导入到应用程序的钥匙串中.

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.

这篇关于Swift 3 UrlSession通过证书进行客户端身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆