自签名服务器证书验证遇SecTrustResultType.recoverableTrustFailure错误排查
问题分析与修复方案
你的代码存在几个关键问题,导致证书验证始终返回recoverableTrustFailure,以下是具体问题和修复后的代码:
核心错误点
- 错误重建Trust对象:你直接提取服务器证书重新创建了Trust实例,但系统传入的
trust已经包含完整的服务器证书链和默认的TLS策略,重新创建会丢失链的层级信息,无法正确验证证书链的完整性。 - 未触发证书评估:
SecTrustGetTrustResult只是获取已评估的结果,你没有主动调用评估方法(如SecTrustEvaluate)来完成验证流程。 - 错误的结果处理:无论验证结果如何,你最后都调用
completionHandler(false),直接拒绝连接。 - 强制解包风险:
caCertPath!这类强制解包会在证书文件缺失时导致崩溃,应该增加安全判断。
修复后的代码
func mqtt(_ mqtt: CocoaMQTT, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void) { // 安全获取CA证书路径与数据 guard let caCertPath = Bundle.main.path(forResource: "ca", ofType: "der"), let caCertData = try? Data(contentsOf: URL(fileURLWithPath: caCertPath)), let caCert = SecCertificateCreateWithData(nil, caCertData as CFData) else { dprint("CA证书加载失败") completionHandler(false) return } // 使用系统传入的trust对象,而非重建 var trustResult: SecTrustResultType = .invalid // 设置自定义锚点证书 guard SecTrustSetAnchorCertificates(trust, [caCert] as CFArray) == errSecSuccess, SecTrustSetAnchorCertificatesOnly(trust, true) == errSecSuccess else { dprint("锚点证书设置失败") completionHandler(false) return } // 触发证书评估 let evaluateStatus = SecTrustEvaluate(trust, &trustResult) // 处理评估结果 var shouldProceed = false switch trustResult { case .proceed, .unspecified: dprint("证书验证通过") shouldProceed = true case .recoverableTrustFailure: dprint("可恢复的信任失败,通常是证书链问题或日期无效") case .deny, .fatalTrustFailure: dprint("证书验证被拒绝") case .invalid, .otherError: dprint("证书验证出错") @unknown default: dprint("未知验证结果") } // 根据评估结果决定是否允许连接 completionHandler(shouldProceed) }
额外说明
SecTrustSetAnchorCertificatesOnly(trust, true):设置只信任你指定的CA证书,避免系统默认信任锚干扰自签名证书的验证。- 优先使用系统传入的
trust对象:它已经包含了服务器发送的完整证书链和适配当前连接的TLS策略,不需要手动重建。 - 必须调用
SecTrustEvaluate:这是触发证书验证流程的核心步骤,没有它SecTrustGetTrustResult只会返回初始的无效状态或旧结果。
内容的提问来源于stack exchange,提问作者Fab
相关产品推荐
相关产品推荐

