手机和操作系统

NSURLErrorDomain Code=-1202:iOS 提示“此服务器的证书无效”

作者: · 最后核实

报错信息NSURLErrorDomain Code=-1202

NSURLErrorDomain Code=-1202 的意思是:苹果的网络库不信任服务器出示的证书。 对应常量是 NSURLErrorServerCertificateUntrusted(Swift 里是 URLError.Code.serverCertificateUntrusted)。苹果文档只提到“根证书不受信任”,但大多数信任失败都报这个码:内部 CA 或自签名证书、域名不匹配、证书链不完整、证书不符合苹果的规则。

iOS 信任一台服务器之前查什么

苹果文档列出了默认服务器信任校验的内容:证书签名完好、没有过期、名称和服务器域名一致、能通过有效的证书一级级链到受信任的锚证书;锚证书要么随系统自带,要么由用户或管理员安装到设备上。好几种不同的失败最后都表现为同一个 -1202,所以光看这个码分不出是哪一项没过,需要按下面的步骤缩小范围。

-1202 和其他 TLS 错误码的区别

-1202 专指证书信任校验失败;握手本身失败报 -1200,证书日期问题报 -1201。中文系统里,-1202 的日志通常以“此服务器的证书无效。您可能正在连接到一个伪装成……的服务器”开头。

错误码 Swift 名称 含义
-1200 secureConnectionFailed 握手失败,见 NSURLErrorDomain -1200
-1201 serverCertificateHasBadDate 证书过期或尚未生效
-1202 serverCertificateUntrusted 信任校验失败
-1203 serverCertificateHasUnknownRoot 没有由任何根证书签发
-1204 serverCertificateNotYetValid 证书尚未生效
-1206 clientCertificateRequired 服务器要求客户端证书

Safari 里同样的情况显示为 此连接非私人连接。

看日志里的底层 SSL 错误码

日志里的 kCFStreamErrorDomainSSL 错误码比 -1202 具体,能直接指出是缺中间证书、根证书不受信任还是主机名不匹配。URLSession 的日志常常还有一行 NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)。后面这个数字来自苹果的 Security 框架,比 -1202 具体得多:

错误码 常量 含义
-9807 errSSLXCertChainInvalid 证书链无效
-9812 errSSLUnknownRootCert 链是完整的,但根证书不受信任
-9813 errSSLNoRootCert 链没有被根证书验证,常见于缺中间证书或内部 CA
-9814 errSSLCertExpired 链里有证书过期
-9843 errSSLHostNameMismatch 主机名和证书不匹配

常见原因(按出现频率排序)

最常见的原因是开发环境或内部服务器用了内部 CA,设备上没有它的根证书。

1. 开发环境或内部服务器用了内部 CA

测试环境 API、私有化部署的服务器、本机调试服务,经常用自签名证书或公司内部 CA,iOS 的信任库里都没有。看签发者:

openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer

2. 域名或 IP 不在证书里

应用请求的是 https://203.0.113.10,或者某个没写进 SAN 的别名。从 iOS 13 起,写在 CN 里的域名完全不被信任,只写在 CN 里的证书同样会失败。国内很多应用接入了 HTTPDNS,把域名换成 IP 直连,日志里就会出现 -9843 和“伪装成 IP 地址的服务器”这样的提示;阿里云的文档给出的处理办法是在证书校验时改用原始域名。查看证书里的域名:

openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName

3. 证书链不完整

服务器只发了自己的证书。有些客户端能自己补上,不要指望。用 -showcerts 数证书数量,或者用 证书链工具 检查,详见 缺少中间证书。从阿里云、腾讯云下载证书时,Nginx 格式的文件里通常已经含有中间证书,部署时不要只保留第一段。

4. 证书不符合苹果的规则

苹果要求 iOS 13、macOS 10.15 及以上系统中的所有 TLS 服务器证书:RSA 密钥不少于 2048 位,签名使用 SHA-2,域名必须写在 SAN 里。2019 年 7 月 1 日之后签发的证书还必须带 id-kp-serverAuth 扩展用途,有效期不超过 825 天。2020 年 9 月 1 日及以后由系统预装根签发的公共证书,有效期不得超过 398 天。很多内部 CA 习惯签五年期、不带扩展用途的证书,就会在这里栽跟头。用 证书解析工具 逐项检查。

5. 抓包代理或网络拦截

Charles、Proxyman、mitmproxy、公司代理、需要网页认证的公共 Wi-Fi,都会出示自己的证书。它们的根证书没有装到设备上并打开完全信任,应用就会报 -1202。

在 Mac 上排查

nscurl --ats-diagnostics --verbose https://api.example.com/

nscurl 用的是苹果自己的网络库,它报信任失败,基本就复现了应用遇到的情况。再用 SSL 检测 从外部对比一下。

怎么修

公网服务器换成合规的公共证书并发送完整链;内部服务器按苹果规则签发证书,再把根证书下发到设备。应用代码保持默认的信任校验。

公网服务器: 用公共 CA 的证书,把应用会访问的每个域名都写进 SAN,并发送完整证书链(Nginx 用 fullchain.pem)。

内部或开发服务器(测试设备上): 1. 按苹果规则签发证书:SAN 写域名、带 serverAuth 扩展用途、SHA-256、有效期不超过 825 天。 2. 通过描述文件或 MDM 把根证书装到设备上。 3. 手动安装的描述文件,到“设置 > 通用 > 关于本机 > 证书信任设置”里给这个根证书打开完全信任。通过 MDM 或 Apple Configurator 部署的会自动信任。

应用代码: 保持默认的信任校验。苹果关于手动校验服务器信任的文档演示了如何在 urlSession(_:didReceive:completionHandler:) 里检查服务器信任,但关于不安全连接的文档也说明,ATS 启用时只能收紧校验(比如锁定公钥),不能放松。生产环境要用内部 CA,就把根证书下发到受管设备,而不是在代码里降低校验。无论如何不要在发布版本里“接受所有证书”,临时的也不行。

怎么确认已经修好

修好的标准是 openssl 验证返回 0、nscurl 诊断全部 PASS,并且真机和模拟器都能正常请求。

  1. 公共证书:openssl s_client -connect api.example.com:443 -servername api.example.com -verify_return_error 最后显示 Verify return code: 0 (ok)。
  2. nscurl --ats-diagnostics https://api.example.com/ 在默认配置下显示 PASS。
  3. 真机和模拟器都跑一遍请求,它们的信任库不一样。

常见问题

在 Info.plist 里加 ATS 例外能解决吗?

不能当作修复手段。默认的服务器信任校验仍然要求证书链到一个系统自带、或者由用户/管理员安装的信任锚。苹果文档还特别说明:ATS 启用时,应用只能收紧信任校验(比如做证书锁定),不能放松。要么修好证书,要么把内部根证书装到设备上。

模拟器能用、真机不行(或者反过来),为什么?

模拟器和真机的证书信任库是分开的。装在 Mac 上或某个模拟器里的开发根证书,真机上没有;真机通过描述文件装的根证书,还要去“证书信任设置”里打开完全信任。

用 IP 直连(比如接了 HTTPDNS)为什么报 -1202?

证书里写的是域名,不是这个 IP,域名校验失败。阿里云 HTTPDNS 的文档就专门讲了这个报错:改为按原始域名做证书校验即可。公共 CA 只给公网 IP 签 IP 证书,通常还是应该用证书覆盖的域名来连接。

-1202 和 -1200 有什么区别?

-1200(NSURLErrorSecureConnectionFailed)是 TLS 握手失败,常见于 ATS 的协议或套件要求;-1202 是握手已经走到证书这一步,信任校验没有通过。

资料来源

  1. Apple Developer: URLError.Code.serverCertificateUntrusted
  2. Apple 支持:iOS 13 和 macOS 10.15 中对受信任证书的要求
  3. Apple 支持:信任手动安装的证书描述文件
  4. Apple Developer: Preventing insecure network connections
  5. Apple Developer: errSSLHostNameMismatch
  6. 阿里云:使用 HttpDns iOS SDK 时处理“Code=-1202 此服务器的证书无效”报错