通过证书

时间:2017-05-17 11:26:58

标签: ios swift authentication ssl client-certificates

我使用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

看起来客户端甚至没有尝试进行身份验证,所以我错过了一些东西,我猜......

1 个答案:

答案 0 :(得分:0)

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

this answer中的PKCS12课程为我工作。

关于钥匙串,this Apple documentation

  

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

     

这样,您的新钥匙串项目就会使用您应用的钥匙串访问组创建。