1. TLS 1.2 握手过程 (经典四次握手)
在握手完成前,所有的通信都是明文的;握手完成后,所有的通信都是加密的。
-
Client Hello: 客户端(iOS App)发送自己支持的 TLS 版本、加密算法列表(Cipher Suites)和一个随机数 。
-
Server Hello & Certificate:
- 服务器选择一套加密算法。
- 发送服务器的 数字证书(包含服务器公钥)。
- 发送另一个随机数 。
-
Certificate Verification & Key Exchange:
- 客户端验证证书合法性(颁发机构、有效期、域名)。
- 客户端生成第三个随机数(Pre-master Secret) ,并用证书里的服务器公钥加密发送给服务器。
-
Finish:
- 双方根据 和 Pre-master Secret 计算出最终的对称密钥(Session Key) 。
- 双方发送“Finished”消息,确认握手完成,后续数据全部用该对称密钥加密。
2. 在 iOS 中如何验证服务器证书?
在 iOS 开发中,验证过程通常分为两个层面:系统默认验证和自定义验证(如 SSL Pinning) 。
A. 系统默认验证 (ATS)
自 iOS 9 起,Apple 引入了 App Transport Security (ATS) 。只要你使用 URLSession 请求 https:// 开头的地址,系统会自动帮你完成以下工作:
- 检查证书链是否完整。
- 检查证书是否由受信任的根证书颁发机构(CA)签名。
- 检查域名是否与证书匹配。
B. 自定义验证(处理自签名证书或 SSL Pinning)
如果你使用的是公司内部的自签名证书,或者为了极高安全性防止中间人攻击而使用 SSL Pinning(证书固定) ,你需要实现 URLSessionDelegate。
代码示例:实现证书校验回调
Swift
extension NetworkManager: URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// 1. 判断是否是服务器信任挑战
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.performDefaultHandling, nil)
return
}
// 2. 如果是 SSL Pinning,这里需要对比本地预埋的证书
// let remoteCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0)
// let localCertificateData = ... 从 App Bundle 读取的 .cer 文件
// 3. 校验逻辑
if customVerify(serverTrust) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
// 校验失败,断开连接
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
3. 什么是 SSL Pinning (证书固定)?
虽然 HTTPS 很安全,但如果用户的手机被安装了恶意的“根证书”(例如抓包工具 Charles 的证书),中间人依然可以解密你的流量。
SSL Pinning 的逻辑是:
在 App 内部打包一份服务器的公钥或证书文件。握手时,App 不再仅仅信任系统信任库,而是直接对比服务器发来的证书和本地预埋的是否一致。
- 优点:极大提升安全性,防抓包。
- 缺点:维护成本高。如果服务器证书更换了,App 必须强制更新,否则网络请求会全部失败。