1. SSL简介
证书锁定(SSL/TLS Pinning)顾名思义,将服务器提供的SSL/TLS证书内置到移动端开发的APP客户端中,当客户端发起请求时,通过比对内置的证书和服务器端证书的内容,以确定这个连接的合法性。
2. 证书锁定原理
证书锁定(SSL/TLS Pinning)提供了两种锁定方式: Certificate Pinning 和 Public Key Pinning,文头和概述描述的实际上是Certificate Pinning(证书锁定)。
1.1 证书锁定
我们需要将APP代码内置仅接受指定域名的证书,而不接受操作系统或浏览器内置的CA根证书对应的任何证书,通过这种授权方式,保障了APP与服务端通信的唯一性和安全性,因此我们移动端APP与服务端(例如API网关)之间的通信是可以保证绝对安全。但是CA签发证书都存在有效期问题,所以缺点是在证书续期后需要将证书重新内置到APP中
1.2 公钥锁定
公钥锁定则是提取证书中的公钥并内置到移动端APP中,通过与服务器对比公钥值来验证连接的合法性,我们在制作证书密钥时,公钥在证书的续期前后都可以保持不变(即密钥对不变),所以可以避免证书有效期问题。
所以我们最终选择使用公钥锁定,为避免证书过期需要客户端更新的问题
1.3 客户端获取公钥
这里采用公钥锁定的方式,则需要获取证书公钥的摘要hash,该值由服务器端提供,提取证书的摘要hash并查看base64的格式,示例:bAExy9pPp0EnzjAlYn1bsSEGvqYi1shl1OOshfH3XDA=,这就是我们将要进行证书锁定的指纹(Hash)信息
1.4 客户端使用SSL锁定选择
1、网络框架是Alamofire,其实该SDK本身支持SSL证书锁定,但是只支持证书锁定,需要将证书放在项目,读取证书的内容进行锁定
2、常用的第三方SDK TrustKit,提供了使用公钥锁定,只需设置域名和对应的Hash值,然后处理网络系统回调方法:
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping @Sendable (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
}
选择使用TrustKit来进行公钥锁定
1.5 项目支持SSL证书锁定
1、podFile引入TrustKit框架,github源码
2、初始化TrustKit,在AppDelegate应用程序启动的方法中添加以下代码:
let trustKitConfig: [String: Any] = [
kTSKSwizzleNetworkDelegates: false,
kTSKPinnedDomains: [
"oversea-sit.yolanda.hk": [
kTSKIncludeSubdomains: true,
kTSKEnforcePinning: true,
kTSKPublicKeyHashes: [
"rseBZG6ykune+DEw3uysPJokdBusGMtrTeJlHd7bHLA=",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
]
]
]]
TrustKit.initSharedInstance(withConfiguration: trustKitConfig)
3、实现自定义代理方法:
class CustomSessionDelegate: SessionDelegate {
override func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("testLog: didReceive challenge")
if TrustKit.sharedInstance().pinningValidator.handle(challenge, completionHandler: completionHandler) == false {
// TrustKit did not handle this challenge: perhaps it was not for server trust
// or the domain was not pinned. Fall back to the default behavior
completionHandler(.performDefaultHandling, nil)
}
}
}
4、修改网络的初始化方法,设置自定义代码类
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 10
configuration.timeoutIntervalForResource = 10
self.session = Session(configuration: configuration, delegate: CustomSessionDelegate())
5、测试验证
使用Charles设置网络抓包,网络请求接口响应错误提示:
Charles上的表现截图:
修改前在Charles上的截图:
原文链接:https://blog.csdn.net/weixin_44836266/article/details/138607973