SSL
-
SSL(Secure Sockets Layer,安全套接层)是一种安全协议,用于在互联网上建立加密链接,确保数据在客户端和服务器之间安全传输。SSL 通过使用加密算法来保护数据不被未授权访问、篡改或拦截。
SSL链接步骤:
-
Client hello:浏览器会将"支持的加密组件"/"尝试连接到Host头"等信息发送给服务器, 并会附上一份随机生成的 session ticket1。
-
Sever hello:服务器收到浏览器发送来的 TLS 握手请求后, 存储浏览器发送的session ticket1, 然后根据发送来的 host 寻找对应的的服务器证书, 然后会将服务器证书, 服务器与浏览器妥协(均支持)的加密套件方法, 和一份随机生成的 session ticket2返回给浏览器.
-
Cipher spec:浏览器收到服务器返回的证书后, 会验证证书有效性。验证步骤大概如下:
-
验证证书有效期
-
验证证书域名
-
验证证书吊销状态
-
验证证书颁发机构, 如果颁发机构是中间证书, 在验证中间证书的有效期/颁发机构/吊销状态。一直验证到最后一层证书, 如果最后一层证书是在操作系统或浏览器内置, 那么就是可信的, 否则就是自签名
-
-
若检查通过, 随机生成一份 session ticket 3 (这是浏览器生成的第二份 ticket), 通过返回证书中的公钥, 用协商的"秘钥交换算法"加密, 返回给服务器。
-
浏览器和服务器用交换得到的密钥进行传输,对称加密算法。
-
session ticket3是无法窃取的
-
攻击者在客户端和服务器建立中间代理,将两头的证书替换为自己的证书,但攻击者的证书不在信任列表中,因此两头都会警告。
-
SSL-Pinning
-
证书锁定(SSL/TLS Pinning)顾名思义,将服务器提供的SSL/TLS证书内置到移动端开发的APP客户端中,当客户端发起请求时,通过比对内置的证书和服务器端证书的内容,以确定这个连接的合法性
-
简单来说就是,在app代码指定证书/公钥,保证客户端和服务端的通信安全性和唯一性。但需要考虑有证书效期的问题。证书和公钥都是用摘要生成hash值
-
证书锁定
import Foundation // 1. 准备证书文件,确保服务器证书在项目中可用 guard let serverCertificateURL = Bundle.main.url(forResource: "serverCertificate", withExtension: "der") else { fatalError("缺少服务器证书文件") } do { // 2. 从项目中读取证书文件 let serverCertificateData = try Data(contentsOf: serverCertificateURL) let serverCertificate = SecCertificateCreateWithData(nil, serverCertificateData as CFData) // 3. 创建服务器信任策略 let serverTrustPolicy = ServerTrustPolicy.pinCertificates(certificates: [serverCertificate], validateCertificateChain: true, validateHost: true) // 4. 配置 URLSession let configuration = URLSessionConfiguration.default configuration.serverTrustPolicy = serverTrustPolicy // 5. 创建 URLSession 对象 let session = URLSession(configuration: configuration) // 6. 发起网络请求 if let url = URL(string: "https://example.com") { let task = session.dataTask(with: url) { data, response, error in // 处理响应 } task.resume() } } catch { print("证书读取失败: \(error)") }
-
公钥锁定:避免证书过期
-
-
公钥锁定:
/// 对比服务器的公钥是不是官方公钥(青藤长连域名认为是官方链接) for serverPublicKey in trust.publicKeys { for pinnedPublicKey in keys where pinnedPublicKey == serverPublicKey { security.evaluateTrust(trust: trust, domain: domain, completion: completion) return } } /// 证书不匹配直接中断连接,同时上报错误日志给服务器 let error = CFErrorCreate(kCFAllocatorDefault, "证书单向校验失败" as NSString?, ResponseErrorCode.unknown.rawValue, nil) QTLogError(.webSocket, "\(String(describing: error))") completion(.failed(error))