You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用NSURLSession处理HTTPS证书错误?

嘿,我明白你遇到的问题了——当请求使用未授权CA的HTTPS服务器时,因为是客户端侧的证书信任错误,response会返回nil,只能拿到信息有限的Error对象,确实头疼!我来给你梳理下解决方案:

首先先把你的代码贴出来方便参考:

let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in 
    if(data != nil){ 
        //... 
    } else if(error != nil) { 
        //... 
    } 
    //... 
}) 
task.resume()
1. 精准识别未授权CA类错误

系统抛出的证书相关错误都属于NSURLErrorDomain,我们可以通过错误码来精准判断是不是未授权CA/未知根证书的问题。常见的相关错误码有:

  • NSURLErrorServerCertificateUntrusted(错误码-1202):服务器证书未被信任
  • NSURLErrorServerCertificateHasUnknownRoot(错误码-1203):证书的根CA不被系统信任
  • NSURLErrorServerCertificateHasBadDate(错误码-1201):证书过期或日期无效

你可以在completionHandler里这样处理:

else if let error = error as NSError? {
    if error.domain == NSURLErrorDomain {
        switch error.code {
        case NSURLErrorServerCertificateUntrusted, NSURLErrorServerCertificateHasUnknownRoot:
            // 这里就是你要处理的未授权CA错误
            print("⚠️ 服务器使用未受信任的CA证书")
            // 可以在这里给用户提示或者执行自定义逻辑
        case NSURLErrorServerCertificateHasBadDate:
            print("⚠️ 服务器证书已过期或日期无效")
        default:
            print("其他网络错误:\(error.localizedDescription)")
        }
    }
}
2. 自定义信任策略(按需允许特定未授权CA)

如果你是在测试环境,需要临时允许这个未授权CA的服务器,或者生产环境要做证书固定(Certificate Pinning)来信任特定证书,可以通过自定义URLSessionDelegate来手动处理信任挑战。

示例代码(测试环境临时信任):

// 先创建一个遵守URLSessionDelegate的自定义代理类
class CustomSessionDelegate: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // 只处理服务器信任类型的挑战
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else {
            // 其他类型挑战交给系统默认处理
            completionHandler(.performDefaultHandling, nil)
            return
        }
        
        // 获取服务器的信任对象
        guard let serverTrust = challenge.protectionSpace.serverTrust else {
            // 无法获取信任对象,拒绝连接
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        // 测试环境下直接信任该服务器(⚠️生产环境绝对不能这么做!)
        let credential = URLCredential(trust: serverTrust)
        completionHandler(.useCredential, credential)
    }
}

// 使用自定义代理创建URLSession
let sessionDelegate = CustomSessionDelegate()
let customSession = URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: nil)

// 用这个自定义session发起请求
let task = customSession.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
    if let data = data {
        // 处理返回数据
    } else if let error = error as NSError? {
        // 这里可以继续处理其他错误
        if error.domain == NSURLErrorDomain {
            // 错误判断逻辑和之前一致
        }
    }
})
task.resume()

生产环境建议:证书固定(Certificate Pinning)

如果是生产环境,绝对不能直接信任所有未知CA,建议固定服务器证书的公钥或指纹,只信任特定的证书:

// 在CustomSessionDelegate的didReceive方法里替换成以下逻辑
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else {
        completionHandler(.performDefaultHandling, nil)
        return
    }
    
    guard let serverTrust = challenge.protectionSpace.serverTrust else {
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }
    
    // 获取服务器证书的公钥数据
    guard let serverPublicKey = SecTrustCopyPublicKey(serverTrust),
          let serverPublicKeyData = SecKeyCopyExternalRepresentation(serverPublicKey, nil) as Data? else {
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }
    
    // 这里替换成你预先存储的服务器公钥(base64格式)
    let expectedPublicKeyBase64 = "你的服务器公钥base64字符串"
    guard let expectedPublicKeyData = Data(base64Encoded: expectedPublicKeyBase64) else {
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }
    
    // 对比公钥,一致则信任
    if serverPublicKeyData == expectedPublicKeyData {
        let credential = URLCredential(trust: serverTrust)
        completionHandler(.useCredential, credential)
    } else {
        // 公钥不一致,拒绝连接
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}

这样既可以处理未授权CA的错误,又能保证生产环境的安全性。

内容的提问来源于stack exchange,提问作者Mateusz Wlodarczyk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:02:03