Alamofire源码分析(12)——网络挑战证书

2020-12-28  本文已影响0人  无悔zero

特点:1. 支持客户/服务器模式;2. 简单快速;3. 灵活;4. 无连接;5. 无状态。

问题:1. 通信使用明文;2. 不验证通信方身份,无论是客户端还是服务器;3. 无法证明报文的完整性,可能被篡改。

加密过程
  • 服务器把自己的公开密钥登录至数字证书认证机构。
  • 数字证书认证机构用自己的私有密钥向服务器的公开密码署数字签名并颁发公钥证书。
  • 客户端拿到服务器的公钥证书后,使用数字签名认证机构的公开密钥,向数字证书认证机构验证公钥证书上的数字签名,以确认服务器的公开密钥的真实性。
  • 使用服务器的公开密钥对报文加密后发送。
  • 服务器用私有密钥对报文解密。

工作流程基本分为三个阶段:1. 认证服务器;2. 协商会话密钥;3. 加密通讯。

{\large\text{作者:Cooci_和谐学习_不急不躁 链接:https://www.jianshu.com/p/970befc7a312 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。}}

今天就来简单了解一下使用证书的例子:

let serverTrustPlolicies:[String: ServerTrustPolicy] = [
    hostUrl: .pinCertificates(
        certificates: ServerTrustPolicy.certificates(), //遍历获取所有证书
        validateCertificateChain: true, //一般为true,验证证书链
        validateHost: true) //一般为true
]
let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPlolicies)
let sessionManger = SessionManager(serverTrustPolicyManager: serverTrustPolicyManager)
sessionManager.request(url).response { (response) in
            print(response)
        }
  1. 首先遍历工程获取证书:
public enum ServerTrustPolicy {
    ...
    public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {
        var certificates: [SecCertificate] = []

        let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
            bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)
        }.joined())

        for path in paths {
            if
                let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
                let certificate = SecCertificateCreateWithData(nil, certificateData)
            {
                certificates.append(certificate)
            }
        }

        return certificates
    }
    ...
}
  1. 验证证书会在TaskDelegate代理中回调:
open class TaskDelegate: NSObject {
    ...
    //验证证书回调
    @objc(URLSession:task:didReceiveChallenge:completionHandler:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?

        if let taskDidReceiveChallenge = taskDidReceiveChallenge {
            (disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
        } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            //Challenge 就是为了验证用户身份,向访问者发送一个质询,然后访问者需要提供一个正确的回答以示身份
            //URLProtectionSpace 这个表示服务器上的一块受保护的区域,访问这一块需要进行质询
            let host = challenge.protectionSpace.host
            
            if
                let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
                let serverTrust = challenge.protectionSpace.serverTrust
            {  //重点
                if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
                    disposition = .useCredential
                    credential = URLCredential(trust: serverTrust)
                } else { ... }
            }
        } else { ... }

        completionHandler(disposition, credential)
    }
    ...
}

质询验证方式有如下几种是常用的
NSURLAuthenticationMethodHTTPBasic: HTTP基本验证,服务器向客户端询问用户名,密码
NSURLAuthenticationMethodClientCertificate: 客户端证书验证,服务器向客户端询客户端身份证书
NSURLAuthenticationMethodServerTrust: 服务器端证书验证,客户端对服务器端的证书进行验证。HTTPS中的服务器端证书验证属于这一种。

UrlCredential是客户端对服务器端质询的响应。根据验证方式不一样,有如下几种:
基于用户名密码的;
基于客户端证书的;
基于服务器端证书的;(就是这里验证服务器端的证书要用到的)
它们分别对应于 UrlCredential 的三种构造方式。
{\large\text{作者:Cooci_和谐学习_不急不躁 链接:https://www.jianshu.com/p/970befc7a312 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。}}

  1. 其重点在于evaluate
public enum ServerTrustPolicy {
    ...
    public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool {
        var serverTrustIsValid = false
        //根据情况验证
        switch self {
        case let .performDefaultEvaluation(validateHost): ...
        case let .performRevokedEvaluation(validateHost, revocationFlags): ...
        case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
            if validateCertificateChain {//验证证书链
                let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
                SecTrustSetPolicies(serverTrust, policy)

                SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray)
                SecTrustSetAnchorCertificatesOnly(serverTrust, true)

                serverTrustIsValid = trustIsValid(serverTrust)
            } else {//验证证书数据
                let serverCertificatesDataArray = certificateData(for: serverTrust)
                let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates)

                outerLoop: for serverCertificateData in serverCertificatesDataArray {
                    for pinnedCertificateData in pinnedCertificatesDataArray {
                        if serverCertificateData == pinnedCertificateData {
                            serverTrustIsValid = true
                            break outerLoop
                        }
                    }
                }
            }
        case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): ...
        case .disableEvaluation: ...
        case let .customEvaluation(closure): ...
        }

        return serverTrustIsValid
    }
    ...
}

根据类型走向pinCertificates,如果validateCertificateChaintrue,就会验证证书链;否则验证证书数据。

Alamofire 安全认证策略的六种模式,其中最常用的有这三种:.pinCertificates 证书验证模式、.pinPublicKeys 公钥验证模式和 .disableEvaluation 不验证模式。
.performDefaultEvaluation 默认策略,只有合法证书才能通过验证
.performRevokedEvaluation 对注销证书做的一种额外设置
.pinCertificates 证书验证模式,代表客户端会将服务器返回的证书和本地保存的证书中的 所有内容 全部进行校验,如果正确,才继续执行。
.pinPublicKeys 公钥验证模式,代表客户端会将服务器返回的证书和本地保存的证书中的 PublicKey 部分 进行校验,如果正确,才继续执行。
.disableEvaluation 该选项下验证一直都是通过的,无条件信任。
.customEvaluation 自定义验证,需要返回一个布尔类型的结果。

{\large\text{作者:Cooci_和谐学习_不急不躁 链接:https://www.jianshu.com/p/970befc7a312 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。}}

上一篇下一篇

猜你喜欢

热点阅读