Alamofire(7)— 安全认证

1,457 阅读12分钟

😊😊😊Alamofire专题目录,欢迎及时反馈交流 😊😊😊


Alamofire 目录直通车 --- 和谐学习,不急不躁!


这个篇章会介绍 http 安全认证相关知识!http 提供了一系列的技术和机器,可用来跟踪身份,进行安全性检测,控制对内容的访问。通过这一篇文章你会对知道为什么https如此重要。同时也会介绍 Alamofire 关于安全认证的处理

HTTP特点

  • 1️⃣.支持客户/服务器模式。

  • 2️⃣.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

  • 3️⃣.灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由Content-Type(Content-TypeHTTP包 中用来表示内容类型的标识)加以标记。

  • 4️⃣.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

  • 5️⃣.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

正是因为这些特点,也造就了灵活的http 存在很多问题

  • 通信使用明文【没有加密过内容的】
  • 不验证通信方身份,无论是客户端和服务器,都是随意通信的
  • 无法证明报文的完整性【别人监听后,可以篡改】

对称加密&非对称加密

  • 1️⃣:对称加密:在对称加密当中加密使用的秘钥和解密使用的秘钥是相同的。也就是加密和解密都是同一个秘钥。这样秘钥的安全性就非常重要,秘钥是一定不能公开的

例子:假如有 ClientServer 之间要进行通讯,他们商定了一种秘钥Client 用秘钥加密传输信息。Server 收到信息用秘钥解密信息。这样的一个通信过程就是对称加密的过程。

缺点:对称加密的缺点就在于如果秘钥要是泄露,这样Client与Server之间的信息传递就不安全了

  • 2️⃣非对称加密:有一对秘钥叫做公钥与私钥,公钥是对外公开的,所有人都能拥有,但是私钥有且只有一个。公钥和私钥都能进行加密,但是公钥加密的密文只有私钥能够解密,私钥加密的所有公钥都能解密,这就是非对称加密。

例子:假如有ClientA、ClientB、ClientC与Server进行通讯,Server拥有一对公钥和私钥,它自己保留唯一的私钥,对外公开自己的公钥,这样 ClientA、ClientB、ClientC 都能拿到公钥。ClientA 用公钥加密的密文只有 Server 的私钥才能解密,这样 ClientA 传递信息就是安全的了,因为即使有中间黑客获取了公钥加密的密文,因为黑客没有私钥也没有办法解密。

缺点:非对称加密只是保证了Client向Server发送的消息是安全的,因为私钥有且只有一把在Server手中,但是反过来Server向Client发送的消息就不是安全的,因为公钥是公开的大家都能下载,也就都能解密信息。

HTTPS安全传输

超文本传输安全协议(英语:Hypertext Transfer Protocol Secure,缩写:HTTPS,常称为 HTTP over TLSHTTP over SSLHTTP Secure)是一种通过计算机网络进行安全通信的传输协议。HTTPS 经由 HTTP 进行通信,但利用 SSL/TLS 来加密数据包。HTTPS 开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。

加密过程

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

可以看到工作流程,基本分为三个阶段:

  • 1️⃣:认证服务器。浏览器内置一个受信任的CA机构 列表,并保存了这些CA机构的证书。第一阶段服务器会提供经CA机构认证颁发的服务器证书,如果认证该服务器证书的CA机构,存在于浏览器的受信任CA机构列表中,并且服务器证书中的信息与当前正在访问的网站(域名等)一致,那么浏览器就认为服务端是可信的,并从服务器证书中取得服务器公钥,用于后续流程。否则,浏览器将提示用户,根据用户的选择,决定是否继续。当然,我们可以管理这个受信任CA机构列表,添加我们想要信任的CA机构,或者移除我们不信任的CA机构
  • 2️⃣协商会话密钥。客户端在认证完服务器,获得服务器的公钥之后,利用该公钥与服务器进行加密通信,协商出两个会话密钥,分别是用于加密客户端往服务端发送数据的客户端会话密钥,用于加密服务端往客户端发送数据的服务端会话密钥。在已有服务器公钥,可以加密通讯的前提下,还要协商两个对称密钥的原因,是因为非对称加密相对复杂度更高,在数据传输过程中,使用对称加密,可以节省计算资源。另外,会话密钥是随机生成,每次协商都会有不一样的结果,所以安全性也比较高。
  • 3️⃣加密通讯。此时客户端服务器双方都有了本次通讯的会话密钥,之后传输的所有Http数据,都通过会话密钥加密。这样网路上的其它用户,将很难窃取和篡改客户端和服务端之间传输的数据,从而保证了数据的私密性和完整性。

Alamofire安全传输

下面我们开始自签证书验证处理

1️⃣: 配置自签证书信息

fileprivate func lgtrustSession() -> SessionManager{
    
    let policies: [String:ServerTrustPolicy] = [
        hostUrl1: .pinCertificates(
            certificates: ServerTrustPolicy.certificates(),
            validateCertificateChain: false,
            validateHost: true),
        hostUrl2: .disableEvaluation,
        hostUrl3: .pinPublicKeys(
            publicKeys: ServerTrustPolicy.publicKeys(),
            validateCertificateChain: false,
            validateHost: true)
    ]
    
    let sesionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies))
    return sesionManager
}
  • 参数1:certificates代表的是证书
  • 参数2:validateCertificateChain 代表是否验证证书链
  • 参数3:validateHost 代表是否验证子地址

Alamofire 安全认证策略的六种模式,其中最常用的有这三种:.pinCertificates 证书验证模式、.pinPublicKeys 公钥验证模式和 .disableEvaluation 不验证模式。

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

2️⃣:证书与公钥信息获取

其中这里笔者采用的遍历整个工程 bundle 获取证书

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
}
  • 遍历bundle 获取整个项目".cer", ".CER", ".crt", ".CRT", ".der", ".DER",然后映射拼接
  • 获取文件中data数据 获取信息
  • 公钥key也是同样的道理
public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
    var publicKeys: [SecKey] = []

    for certificate in certificates(in: bundle) {
        if let publicKey = publicKey(for: certificate) {
            publicKeys.append(publicKey)
        }
    }
    return publicKeys
}

3️⃣:接受质询

urlSession(
        _ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
  • Challenge 就是为了验证用户身份,向访问者发送一个质询,然后访问者需要提供一个正确的回答以示身份
  • URLProtectionSpace 这个表示服务器上的一块受保护的区域,访问这一块需要进行质询。他有如下常用属性
// realm是ProtectionSpace的标示符,
//服务器上的一组资源通过realm来标示成一组采用相同验证方式的资源(ProtectionSpace)。
open var realm: String? { get }
// 确定此保护空间的密码是否可以安全地发送
open var receivesCredentialSecurely: Bool { get }
// 资源所在的服务器
open var host: String { get }
// 资源所在服务器端口
open var port: Int { get }
// 如果是代理,则获取此保护空间的类型
open var proxyType: String? { get }
//获取资源的协议资源
open var `protocol`: String? { get }
// 质询所采用验证方式
open var authenticationMethod: String { get }

质询验证方式有如下几种是常用的

  • NSURLAuthenticationMethodHTTPBasic: HTTP基本验证,服务器向客户端询问用户名,密码
  • NSURLAuthenticationMethodClientCertificate: 客户端证书验证,服务器向客户端询客户端身份证书
  • NSURLAuthenticationMethodServerTrust: 服务器端证书验证,客户端对服务器端的证书进行验证。HTTPS中的服务器端证书验证属于这一种。

UrlCredential

他是客户端对服务器端质询的响应。根据验证方式不一样,有如下几种UrlCredential:

  • 基于用户名密码的 UrlCredential
  • 基于客户端证书的 UrlCredential
  • 基于服务器端证书的 UrlCredential //就是我们这里验证服务器端的证书要用到的
  • 它们分别对应于 UrlCredential 的三种构造方式。详情参考Apple开发文档

SecTrust

  • iOS中对证书和Accept Policy 的包装。系统对后台证书验证实际上是对该对象的验证。详情建Apple开发文档
  • 用于执行X.509证书信任评估的CFType

下面具体代码分析

if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
    let host = challenge.protectionSpace.host
    // 返回与给定主机完全匹配的策略
    if
        let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
        // 返回一个SecTrustRef,它表示服务器SSL事务状态的状态
        let serverTrust = challenge.protectionSpace.serverTrust
    {
        // 评估服务器信任是否对给定主机有效。
        if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
            disposition = .useCredential
            credential = URLCredential(trust: serverTrust)
        } else {
            disposition = .cancelAuthenticationChallenge
        }
    }
}
  • 返回与给定主机完全匹配的策略
  • 返回一个SecTrustRef,它表示服务器SSL事务状态的状态
  • 评估服务器信任是否对给定主机有效
  • 通过回调函数告诉系统对于该质询的 UrlCredential

验证方法:public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool

  • .performDefaultEvaluation 策略模式下

    • 创建评估SSL证书链的策略对象
    • 设置应该验证信任的策略到serverTrust
    • 调用trustIsValid开始验证匹配
  • .performRevokedEvaluation 策略模式下

    • 创建评估SSL证书链的策略对象
    • 返回用于检查证书撤销的策略对象。
    • 设置应该验证信任的策略到serverTrust,这里是一组
    • 调用trustIsValid开始验证匹配
  • .pinCertificates策略模式下

    • 如果验证证书链
      • 创建评估SSL证书链的策略对象
      • 设置应该验证信任的策略到serverTrust
      • 为给定的信任设置锚证书
      • 此外,重新启用信任锚证书,通过 SecTrustSetAnchorCertificates API
      • 调用trustIsValid开始验证匹配
    • 如果不验证证书链
      • 获取受信任的证书信息data
      • 获取当前传入的所有证书信息组
      • 匹配如果传入的证书组信息有包含受信任的证书,就返回true ,并利用outerLoop一次性直接跳出
  • .pinPublicKeys策略模式下

    • 如果验证证书链
      • 创建评估SSL证书链的策略对象
      • 设置应该验证信任的策略到serverTrust
      • 调用trustIsValid开始验证匹配
    • 如果不验证证书链
      • 匹配传入的公钥是否包含受信任公钥信息,如果包含就返回true ,并利用outerLoop一次性直接跳出
  • .disableEvaluation策略模式下

    • serverTrustIsValid = true 表示不需要验证,直接通过
  • .customEvaluation策略模式下

    • serverTrustIsValid = closure(serverTrust, host) 对外提供验证处理闭包,由用户自行提供验证策略方案

这里同时也给大家提供一篇专业介绍 证书链的文章

4️⃣:证书完毕,就可以直接正常通讯了!

总结

这一篇关于 安全认证 的文章涉及到了 http以及https 各自的特点,还有加密手段,最后分析了 Alamofire安全认证,过程看是复杂,其实就是一个证书或者公钥匹配问题,但是这些也是很多iOS开发人员经常不愿去涉猎的地方,知识点枯燥难懂!但是:而世之奇伟,瑰鬼,非常之观,常在于险远,而人之所罕至焉,故非有志着不能至也。iOS中高级进阶没有其他,就是沉下心来,认真打磨自己!💪💪💪

下一篇开始介绍面向协议编程板块,最后非常感谢大家一路的关注!

就问此时此刻还有谁?45度仰望天空,该死!我这无处安放的魅力!