使用Swift和NSURLSession固定的​​iOS证书

问题描述:

如何在Swift中将证书固定添加到NSURLSession?

Howto add certificate pinning to a NSURLSession in Swift?

OWASP网站仅包含Objective-C和NSURLConnection的示例。

The OWASP website contains only an example for Objective-C and NSURLConnection.

Swift 3 更新:

只需为 NSURLSessionDelegate 定义一个委托类,然后执行didReceiveChallenge function(此代码改编自objective-c OWASP示例):

Just define a delegate class for NSURLSessionDelegate and implement the didReceiveChallenge function (this code is adapted from the objective-c OWASP example):

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        // Adapted from OWASP https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#iOS

        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let serverTrust = challenge.protectionSpace.serverTrust {
                var secresult = SecTrustResultType.invalid
                let status = SecTrustEvaluate(serverTrust, &secresult)

                if(errSecSuccess == status) {
                    if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
                        let serverCertificateData = SecCertificateCopyData(serverCertificate)
                        let data = CFDataGetBytePtr(serverCertificateData);
                        let size = CFDataGetLength(serverCertificateData);
                        let cert1 = NSData(bytes: data, length: size)
                        let file_der = Bundle.main.path(forResource: "certificateFile", ofType: "der")

                        if let file = file_der {
                            if let cert2 = NSData(contentsOfFile: file) {
                                if cert1.isEqual(to: cert2 as Data) {
                                    completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:serverTrust))
                                    return
                                }
                            }
                        }
                    }
                }
            }
        }

        // Pinning failed
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }

}

(你可以找到这里的Swift 2要点 - 从最初的答案

然后使用 openssl

openssl s_client -connect my-https-website.com:443 -showcerts < /dev/null | openssl x509 -outform DER > my-https-website.der

并将其添加到xcode项目中。仔细检查它是否存在于构建阶段选项卡中,位于复制捆绑资源列表中。否则将其拖放到此列表中。

and add it to the xcode project. Double check that it's present in the Build phases tab, inside the Copy Bundle Resources list. Otherwise drag and drop it inside this list.

最后在您的代码中使用它来发出URL请求:

Finally use it in your code to make URL requests:

if let url = NSURL(string: "https://my-https-website.com") {

    let session = URLSession(
            configuration: URLSessionConfiguration.ephemeral,
            delegate: NSURLSessionPinningDelegate(),
            delegateQueue: nil)


    let task = session.dataTask(with: url as URL, completionHandler: { (data, response, error) -> Void in
        if error != nil {
            print("error: \(error!.localizedDescription): \(error!)")
        } else if data != nil {
            if let str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) {
                print("Received data:\n\(str)")
            } else {
                print("Unable to convert data to text")
            }
        }
    })

    task.resume()
} else {
    print("Unable to create NSURL")
}