手机和操作系统

NSURLErrorDomain -1200:iOS App 提示“发生了 SSL 错误”

作者: · 最后核实

报错信息NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."

NSURLErrorDomain Code=-1200 的意思是:iOS 或 macOS 上的 App 无法建立安全连接,TLS 握手失败了。 苹果的 URL 加载系统会执行 App Transport Security(ATS),所以某些浏览器能打开的服务器,在 App 里照样可能失败。修法几乎都在服务器端,偶尔在 App 的 ATS 设置里。

先判断是网站的问题还是你设备的问题

所有用户都失败是服务器的问题,只有部分用户失败多半是网络或设备的问题。用 SSL 检测 查 App 访问的接口域名或网站:

curl ssl.sh/example.com

服务器不支持 TLS 1.2、套件太弱或证书链不完整,所有用户都会失败。服务器检测通过、只有部分用户失败,就看他们的网络和已安装的描述文件。在 Mac 上,用苹果的 ATS 诊断工具测试 App 实际访问的地址:

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

原因

5 种原因里,前 3 种出在服务器的协议、证书或证书链上,后两种出在网络拦截和 App 的证书固定。

1. 服务器不满足 ATS 要求

ATS 要求 TLS 1.2 及以上和前向保密(ECDHE)套件。只提供 TLS 1.0/1.1 或 RSA 密钥交换的服务器会报 -1200。国内一些老旧的接口服务器、前面挂着老负载均衡的系统经常是这种情况。

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

SHA-1 签名、RSA 密钥小于 2048 位、没有 SAN、有效期过长,都会让 ATS 或苹果的信任评估失败。看失败发生在哪一步,会报 -1200 或 -1202。

3. 证书链不完整或私有 CA

缺中间证书、或者私有 CA 的根证书没装,通常报 -1202,但有些握手路径会报 -1200。用 证书链工具 修好证书链。

4. 网络拦截

公司代理、需要认证的公共 Wi-Fi、VPN 或过滤类描述文件都可能破坏 TLS。用蜂窝数据测试。

5. 证书固定(Pinning)不匹配

App 固定了某张证书或公钥,服务器换了证书,App 自己的固定逻辑就会拒绝连接,常常表现为 -1200 或请求被取消(-999)。

在各浏览器/系统上的样子

同一个握手问题,App 日志里是 -1200,Safari 里是“无法建立安全连接”;证书不受信任时则改报 -1202。

场景 显示内容
iOS / macOS App 日志 NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."
同一服务器在 Safari 里 “Safari 无法建立安全连接”
App 里证书不受信任 NSURLErrorDomain Code=-1202
安卓 App Trust anchor for certification path not found 或 SSLHandshakeException

站长怎么修

修法以服务器为主:开启 TLS 1.2 以上和 ECDHE 套件,使用公开证书和完整证书链;ATS 例外只作最后手段。

1. 让服务器达到 ATS 标准。

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_certificate /etc/nginx/ssl/api.example.com.fullchain.pem;

接口放在阿里云/腾讯云负载均衡或 API 网关后面的,在控制台把 TLS 安全策略调到最低 TLS 1.2。

2. 使用公开证书和完整证书链。 用 curl ssl.sh/example.com 检查。

3. 服务器实在改不了时,只针对那一个域名在 App 的 Info.plist 里加例外:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>legacy.example.com</key>
    <dict>
      <key>NSExceptionRequiresForwardSecrecy</key>
      <false/>
    </dict>
  </dict>
</dict>

4. 提前规划证书固定的轮换:固定多个公钥,并在服务器换证书之前发布新版 App。

常见问题

-1200 和 -1202 有什么区别?

-1200 是安全连接本身失败(协议、套件,或握手中收到致命告警);-1202 是握手已经走到收到证书这一步,但证书不受信任。-1202 指向证书或证书链,-1200 指向 TLS 设置或网络干扰。

直接把 NSAllowsArbitraryLoads 设成 YES 行不行?

不行。它会对所有连接关闭 ATS,降低 App 安全性,App 审核时还要说明理由。应该修服务器;服务器不归你管时,只给那一个域名加一条有针对性的例外。

只有部分用户的手机报错,为什么?

这些用户可能连着会拦截 TLS 的网络(公司 Wi-Fi、需要认证的公共 Wi-Fi),装了 VPN 或过滤类描述文件,或者 iOS 版本较老。从日志里取出底层的 _kCFStreamErrorCodeKey:比如 -9802 表示收到致命 TLS 告警,-9806 表示连接被关闭。

资料来源

  1. Apple Developer: NSURLErrorSecureConnectionFailed
  2. Apple Developer: Preventing insecure network connections
  3. Apple Developer: NSAppTransportSecurity