在2017年估计是 HTTPS 的热潮,从 Apple 在 WWDC 宣布 App Store 的中 App 强制打开 ATS (App Transport Security),这也就意味着我们的应用需要支持 HTTPS,至少是对应的网络接口需要支持。关于其他的,比如CDN、外链是否需要支持,Apple 也没有给出明确的要求。这篇文章介绍一下 HTTPS 的原理。

为什么需要 HTTPS

我们知道 HTTP 协议中数据传输都是明文的,也就是说在我们传输的数据被中间人监听或者截获,数据内容也就被完全的知道,甚至是修改或者冒充你的身份。所以在 HTTP 的通讯过程中有以下不安全的因素:

  • 窃听,获取传输信息
  • 篡改,修改传输信息
  • 冒充,假冒身份通讯

HTTPS 是如何做到安全的

HTTPS 是 HTTP + SSL/TLS。我们知道 HTTP 在 OSI 七层协议架构中属于应用层,了解 OSI 的同学就知道 SSL (Secure Socket Layer安全套接层) 就应该在表示层(处理一些数据加解密等)和会话层(处理通讯之间建立、协作等)工作。
从上面的描述来看,HTTPS 其实握手时进行身份验证和在数据传输时进行数据加密、校验来达到安全。

  • 窃听,获取传输信息 数据加密
  • 篡改,修改传输信息 数据校验
  • 冒充,假冒身份通讯 身份验证

HTTPS 通讯流程

以下流程以 web 访问一个 HTTPS 网站为例:

1
2
3
4
5
6
Client->Service: 1.发送支持的协议版本,发送一个‘随机数’
Service-->Client: 2.确定协议版本,发送一个‘随机数’,发送证书
Client->Service: 3.验证证书通过,发送一个公钥加密过后的‘随机数’
Service-->Client: 4.协商好的加密方法
Client->Service:5.发送公钥加密之后的密钥,之后使用密钥加密通讯
Service-->Client: ...
  1. 发送能够支持的 SSL/TLS 协议版本,加密方法,压缩方法等,生成一个 随机数 ,之后生成发密钥时用到,并送到服务端。
  2. 确定客户端能否支持的协议版本,若没有匹配,则取消此次连接。同时也确定加密方法和压缩方法。生成一个 随机数,同时发送 随机数 和证书。
  3. 客户端认证证书是否有效,若无效,页面弹出警告窗口。若有效,取出证书中的公钥。生成一个 随机数 ,并用公钥加密之后发送给服务端。
  4. 此时服务端保存有两个明文的 随机数 和一个加密之后的 随机数。服务器通过密钥解密出 随机数 ,三个 随机数 通过一个密钥生成器,生成之后通讯所需的对称密钥。
  5. 此时客户端有三个 随机数,通过一个密钥生成器,生成之后通讯所需的对称密钥,通过非对称的密钥(证书中公钥多对应的密钥)对对称密钥加密传输给服务端,服务端使用私钥解密出对称密钥。

还有哪些不安全

通过上面的 HTTPS 握手过程已经比较安全了。但是其中还存在一些问题:

  • 证书被篡改
  • 证书是伪造的

首先我们先了解下,证书的格式和证书是如何验证的。

证书的格式

一个数据的内容主要包含下面几项:

  1. 证书包含了颁发证书的机构的名字(CA)
  2. 证书内容本身的数字签名(用CA私钥加密)
  3. 证书持有者的公钥
  4. 证书签名用到的 hash 算法

CA 的名字是在验证时,获取根证书的公钥需要 CA 名字。数据签名保证了证书的内容完整正确无误。持有者的公钥用来在握手的时加密用。hash 算法在验证的时候需要用到。

mac 的用户可以去[实用工具-钥匙串访问-系统根证书]里面包含一些系统内置的权威机构颁发的证书,比如 GlobalSign、VeriSign 等等。

证书的验证

我们已经了解了这些证书格式,他的验证过程如下:

  1. 获取证书的 CA,根据 CA 得到根证书的公钥
  2. 用根证书的公钥对证书的数字签名解密,得到证书内容的摘要 AA
  3. 用证书所提供的 hash 算法对证书内容进行 hash,得到摘要 BB
  4. 此时比较摘要 AA 和摘要 BB 相同则验证如同通过;反之,证书有问题。

通过验证的了解。第一个问题如果在篡改证书的内容在验证的第四步中两个摘要不会相同,所以验证不通过。第二个问题伪造证书,伪造 CA 名字。如果根据 CA 名字找不到根证书认为是有问题证书。如果伪造数字签名,根证书解密不了认为是有问题证书 … 诸如此类问题其实都会在证书验证的过程中暴露出来。

HTTPS 的单向认证 & 双向认证

以上的握手过程是一个单向认证的过程,我们知道握手时只是验证了,服务端的身份。为什么叫单向认证?

  • 单向认证:握手过程中,验证了服务端的证书是有效的。更关心的服务端的身份,而不是客户端的身份。比如访问 HTTPS 网站,大多都是单向认证,除一些银行的证书登录、网上支付等
  • 双向认证:同时也关心客户端的身份有效性。比如:一些银行证书登录(插入 U 盾,里面内置了一些证书)。晚上支付的,一般在第一次访问的时候提示安装安全控件。

在 iOS 中的 HTTPS

  • NSURLSession
    我们熟知的 NSURLSession 对于默认支持 HTTPS 单向认证,这就是我们为什么在请求 HTTPS 接口的时候也是能得到数据的。
    但是对于双向认证,NSURLSession 也为我们提供了相应的代理方法。
1
2
3
4
5
6
7
8
9
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
NSLog(@"%@",challenge.protectionSpace);
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
NSURLCredential *cre = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 调用block
completionHandler(NSURLSessionAuthChallengeUseCredential,cre);
}
}

对于一些要求双向认证的服务,在这个代理方法中实现认证即可。注意的是在 SSL 认证的过程中会两次调用此方法。

  • WKWebView
    在 WebKit 中也有相应的代理方法。
1
2
3
4
	- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {

completionHandler(NSURLSessionAuthChallengeUseCredential,nil);
}

*以上两点,还未实践过,正确性还有待验证,仅作一个实现思路的参考ß

几个附加的概念

  • 对称加密:通讯双方持有相同的密钥,并且这个密钥可以同时实现加解密。
  • 非对称加密:通讯双方持有不同的密钥,我们分别称作为公钥和密钥,一般公钥是公开的,大家都知道的。密钥是有一方保存不公开的。但是一个密钥加密的密文可以用公钥解开,用公钥加密的密文也可以用公钥解开。
  • 电子证书的验证过程:用户的敏感个人数据并不会传输至索取数据者的电脑系统上。通常,当索取数据者获取用户的电子证书数据后,会即时递交至电子核证机关的系统中进行核证。当用户身份经确认后,核证机关会将经确认的消息转交至索取数据者,在此期间,除用户同意并主动给出的个人数据以外,其他数据(如出生日期、身份证号码等)均不会自动递交至索取数据者。通过这种数据交换模式,用户既可证实自己的身份,亦不用过度披露个人数据,对保障电脑服务访问双方皆有好处。

参考

HTTPS科普扫盲帖
SSL/TLS协议运行机制的概述
HTTPS wiki

写在后面

希望对你有帮助,写的不正确的地方,欢迎拍砖~