【问题标题】:How do I accept a self-signed SSL certificate using iOS 7's NSURLSession如何使用 iOS 7 的 NSURLSession 接受自签名 SSL 证书
【发布时间】:2015-08-24 16:34:28
【问题描述】:

我有以下代码(快速实现):

func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool
{
    return protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
}

func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
{
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
    {

        if challenge.protectionSpace.host == "myDomain"
        {
            let credentials = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust)
            challenge.sender.useCredential(credentials, forAuthenticationChallenge: challenge)
        }
    }

    challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge)

}

它在 iOS 8.x 中完美运行,但在 iOS 7.x 中无法运行 在 iOS 7.x 中出现错误:

NSURLConnection/CFURLConnection HTTP 加载失败(kCFStreamErrorDomainSSL,-9813)

有什么想法吗? 谢谢!!!

【问题讨论】:

    标签: ios swift ssl https self-signed


    【解决方案1】:

    connection:canAuthenticateAgainstProtectionSpace:connection:didReceiveAuthenticationChallenge: 在 iOS 8 中无论如何都已弃用,因此您应该使用其他方法。

    我在项目中使用的是 NSURLSessionDelegate 的委托方法。遵守该协议,然后添加此方法:

    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void) {
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust))
    }
    

    然后,当您使用委托设置为 self 的初始化 NSURLSession 时。例如:

    var session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
    

    然后使用该会话实例调用 dataTaskWithRequest 方法:

    var task = session.dataTaskWithRequest(request){
        (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
        if error != nil {
            callback("", error.localizedDescription)
        } else {
            var result = NSString(data: data, encoding:
                NSASCIIStringEncoding)!
        }
    }
    task.resume()
    

    可以在here找到完整的工作示例。

    出于安全原因,如果您使用自签名证书,我建议您还实施公钥固定 (https://gist.github.com/edwardmp/df8517aa9f1752e73353)

    【讨论】:

    • 如果没有公钥固定,这是否安全?据我所见,您没有检查证书是否与应有的匹配
    • 不使用公钥固定时如何检查证书匹配?不需要密钥固定,但我推荐它,这就是为什么我在底部包含指向 sn-p 的链接..
    • 流量仍然在不使用公钥固定的情况下加密,但我猜中间人攻击 (MITM) 可以提供另一个证书以拦截流量。
    • 对我不起作用 - 我得到一些“错误域 = NSURLErrorDomain 代码 = -999”。但它适用于有效证书。
    • @coyer -999 错误表示请求被取消。也许您或您使用的其他库取消了请求(请参阅stackoverflow.com/questions/25390073/…)?或者服务器本身可能有问题(例如证书是自签名的,但主机名也不正确)
    【解决方案2】:

    使用 URLSessionDelegate 继承类

    创建会话对象

    let config = URLSessionConfiguration.default
    
    
    let session = Foundation.URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
    
    
     let task = session.dataTask(with: httpRequest as URLRequest, completionHandler: {requestData, response, errorData -> Void in
    
                if errorData == nil {
    
                    dataCallback(requestData! as NSData)
                }
                else {
    
                    let error = NSError(domain: "Err-1001", code: 11, userInfo:nil)
                    failureCallback(error)
                }
            });
    
            task.resume() 
    

    添加委托方法

    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive     challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
         completionHandler(
            .useCredential,
            URLCredential(trust: challenge.protectionSpace.serverTrust!))
     }
    

    将此添加到您的 info.plist 文件中

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>xyc.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <true/>
                <key>NSExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
                <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
                <false/>
                <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
                <true/>
                <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
                <key>NSRequiresCertificateTransparency</key>
                <false/>
            </dict>
        </dict>
    </dict>
    

    【讨论】:

      【解决方案3】:

      Swift 5 解决方案

      注意这些设置仅用于测试目的,请勿在生产应用中推送更改

      步骤

      1. 在 info.plist 的“App Transport Security Settings”下将“Allow Arbitrary Loads”键添加到“YES”

      2. 创建新的会话对象并委托给自己

        让 config = URLSessionConfiguration.default
        let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)

      3. 为当前类实现 URLSessionDelegate 协议

        扩展 YouClass: URLSessionDelegate{ func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 完成处理程序(URLSession.AuthChallengeDisposition.useCredential,URLCredential(信任:challenge.protectionSpace.serverTrust!)) } }

      修改版: https://stackoverflow.com/a/30820452/3754976

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-15
        • 2011-08-25
        • 2021-05-07
        • 2017-05-08
        相关资源
        最近更新 更多