Alamofire之安全认证

1,331 阅读6分钟

在网络如此发达的今天,网络安全已经和每个人息息相关了。今天我们就一起来了解一下网络安全方面的相关知识。

一、HTTPS简介

我们知道,在HTTP协议中,发送的内容是以明文的形式传递的。这就导致了以下几个缺陷:

  • 明文内容,敏感数据容易被黑客窃听
  • 不对双方的身份作验证,容易被伪装
  • 无法验证数据的完整性,数据容易被篡改

为了解决上面👆几个缺陷,苹果官方推荐使用HTTPS传输协议。

HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议。重要的是比HTTP更安全。

二、密码学基础

在了解HTTPS协议之前,我们需要先有一点密码学基础。

明文:是指未被加密过的原始数据
密文:明文被某个加密方式(算法)加密后,就变成了密文。密文可以被解密得到明文
密钥:是在明文转换为密文的算法中所使用的参数。
密钥分为对称密钥和非对称密钥。分别对应对称加密和非对称加密。
对称加密:对称加密又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。

对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密。

对称加密加密过程:明文 + 加密算法 + 私钥 => 密文 对称加密解密过程:密文 + 解密算法 + 私钥 => 明文

非对称加密:非对称加密也叫做公钥加密。非对称加密使用一对密钥,即公钥和私钥,且二者成对出现。用公钥或私钥中的任何一个进行加密,用另一个进行解密。

非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

被公钥加密过的密文只能被私钥解密。 加密过程:明文 + 加密算法 + 公钥 => 密文 解密过程:密文 + 解密算法 + 私钥 => 明文

被私钥加密过的密文只能被公钥解密。 加密过程:明文 + 加密算法 + 私钥 => 密文 解密过程:密文 + 解密算法 + 公钥 => 明文

三、HTTPS加密

非对称加密比对称加密更加安全,那么HTTPS中使用的是非对称加密吗?不是的。https使用的是混合加密的方式。那什么是混合加密呢?

如上图所示,HTTPS协议在连接时使用的是非对称加密。在数据传输过程中,使用的是对称加密。

因为非对称加密速度慢,不合适在数据传输过程加密。所以在传输过程中使用对称加密。而为了网络请求的安全性,所以在连接过程中使用了非对称加密。

四、HTTPS通讯过程

那么HTTPS的通讯过程是怎样的呢?

  • 客户端使用HTTPS的URL访问服务器,与Web服务器建立SSL连接。
  • Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
  • 客户端浏览器与Web服务器开始协商SSL/TLS连接的安全等级,也就是信息加密的等级。
  • 客户端浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
  • Web服务器利用自己的私钥解密出会话密钥。
  • Web服务器利用会话密钥加密与客户端之间的通信。
  • 在当前未断开之前,客户端向服务端发送的消息都使用会话密钥加密,服务端再使用会话密钥解密。

五、网络挑战

举个栗子🌰:

let urlBD = "https://47.105.168.156:20199/"

Alamofire.request(urlBD, method: .get, parameters: ["users": "bar"])
    .response { (response) in
        print(response)
}

在上面👆的栗子中,我们向一个https地址发起了网络请求。运行之后会发现报了一些错误。

TIC SSL Trust Error [1:0x600000a592c0]: 3:0
...
Task <C47E510C-D9F5-4C62-9EB0-D58FFF71A638>.<1> finished with error - code: -1202
Task <C47E510C-D9F5-4C62-9EB0-D58FFF71A638>.<1> load failed with error Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “47.105.168.156” which could put your confidential information at risk."

根据错误信息,大概是说服务器的证书失效。那么这个错误应该如何修复呢?想要修复这个错误,我们应该知道,这个错误是何时打印的。

我们从错误信息中看到有 finished with error,那么我们在 didCompleteWithError 函数中断点调试一下。

断点后发现,在进入 didCompleteWithError 回调函数之前,已经打印了错误信息。所以,我们的猜测不成立。

我们知道,所有的网络请求都会走 SessionDelegate 的回调。既然不在 didCompleteWithError 回调函数,那会不会在其他回调函数呢?然后我们就会找到这样一个回调函数:

从注释文档来看,这是和证书相关的回调。我们断点调试一下,发现确实会来到这里。并且会调用 delegate.urlSession 回调函数。

就是这个回调函数,而且会因为红框中的判断没有成功而直接跳出执行。所以,这里就是错误的关键,这其实就是常说的 网络挑战

session.serverTrustPolicyManagerURLSession 的一个关联属性。那它是何时设置的呢? 还记得在【Alamofire之SessionManager】分析 SessionManager 初始化的时候见过这个属性,但是当时没有分析。

所以我们应该在 SessionManager 的初始化时应该多一个参数 serverTrustPolicyManager

我们自定义一个 SessionManager

func boManager() -> SessionManager {
    
    // 第一个参数是要信任的主机host
    let policies: [String: ServerTrustPolicy] = [
        "47.105.168.156": .pinCertificates(certificates: ServerTrustPolicy.certificates(), validateCertificateChain: true, validateHost: true)
    ]
    
    // 其余参数使用默认值
    let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies))
    
    return sessionManager
}

使用 ServerTrustPolicyManager 初始化 SessionManager

ServerTrustPolicyManager 需要一个字典参数 policies。 字典的key是你认证的主机地址。value是一个枚举 ServerTrustPolicy

枚举 ServerTrustPolicy 有很多个case,小伙伴可以根据文档选择自己的方式。这里使用 pinCertificates 固定证书认证举例。

需要服务器给你一个证书,放入工程中。然后再使用自定义的 SessionManager 请求网络。

let urlBD = "https://47.105.168.156:20199/"

// 使用自定义SessionManager网络请求
self.sessionManager = boManager()
self.sessionManager?.request(urlBD)
    .response { (response) in
        print(response)
}

重新运行,就会发现不会报之前的错误了。

如果小伙伴只是测试,或者自己写个demo,还可以使用 .disableEvaluation 不验证证书。

// 不验证证书
let policies: [String: ServerTrustPolicy] = [
    "47.105.168.156": .disableEvaluation
]

但是这样会很危险,小伙伴们慎用。

看到这里,很多小伙伴可能会有疑问了。因为平时我们开发中没有这样设置过,都是直接网络请求,也没有报错。这是因为,上面适用的情况是自签证书。而我们平时更多使用的是CA证书。

那么CA证书又是如何工作的呢?请看下图:

以上就是本篇Alamofire安全认证的全部内容。若有不足之处,请评论指正。