0x00 前言

由于谷歌的推动和大家对网络安全的需求,HTTPS 协议开始广泛的使用了。作为开发者也应该去积极的了解,所以写篇博文在「如何提供加密通信」的角度,谈一谈 HTTPS 协议。

0X01 加密通信在 Web 领域的难点

我们知道 HTTP 协议是明文传输的协议,所以容易被第三方窃听、篡改、冒充,给用户隐私和安全带来危害。所以使用加密传输便是理所应当解决方式。下面是一些加密领域的常见概念:

  1. 对称加密:有流式、分组两种,加密和解密都是使用的同一个密钥。

    例如:DES、AES-GCM、ChaCha20-Poly1305 等。

  2. 非对称加密:加密使用的密钥和解密使用的密钥是不相同的,分别称为:公钥、私钥,公钥和算法都是公开的,私钥是保密的。非对称加密算法性能较低,但是安全性超强,由于其加密特性,非对称加密算法能加密的数据长度也是有限的。

    例如:RSA、DSA、ECDSA、 DH、ECDHE 等。

  3. 哈希算法:将任意长度的信息转换为较短的固定长度的值,通常其长度要比信息小得多,且算法不可逆。

    例如:MD5、SHA-1、SHA-2、SHA-256 等。

  4. 数字签名:签名就是在信息的后面再加上一段内容(信息经过 hash 后的值),可以证明信息没有被修改过。hash 值一般都会加密后(也就是签名)再和信息一起发送,以保证这个 hash 值不被修改。

一说起加密我们通常会首先想到的是「对称加密」,我们假设客户端和服务端都保存了同一串秘钥,它们将请求和响应都进行加密后再通过网络发送,然后在接收到网络数据后再进行解密,这样窃听者收到的都是乱码而无法知道通信内容。但是这个简单的方案却无法「直接应用」在 Web 领域。因为这个世界上的网站有无数多,我们的电脑上不可能存储所有网站的秘钥,网站服务器也无法做到向每个客户都提供秘钥并储存。既然无法存储那我们能不能在通信的时候让客户端和服务端都获得相同的秘钥呢?这就是我们的难点:「实现秘钥传输」。

0x02 尝试非对称加密

因为 HTTP 协议是明文协议,所以我们直接传输秘钥会被第三方窃听到,所以直接传输秘钥并不安全。我们尝试把目光放在「非对称加密」,非对称加密产生的是一对密钥对,其中有一串秘钥是用来加密数据的(通常作为公钥,就是可以对外公开),另外一串秘钥用于解密数据(通常作为私钥,不能公开要秘密保存)。我们现在的想法是这样:

在开始通信前服务端将公钥提供给客户端(浏览器),然后客户端生成对称加密的秘钥并将其通过服务端提供的公钥加密后发送服务端。服务端通过私钥解密后即得到客户端刚才生成的秘钥,然后在之后的通信过程中我们的服务端和客户端就可以通过这串秘钥进行加密通信了。

这个方案似乎可行,但是稍微思考一下就会发现和传输秘钥一样,我们无法保证服务端返回的「公钥」真的是由服务端返回的而没有经过掉包,因为如果存在「中间人」将服务端返回的公钥截获并掉包成自己的公钥那么整个通信过程对中间人来说还是透明的。不过现在我们把问题稍微简化了一点,变成了「如何验证公钥的真实性」。

0x03 引入第三方机构

通过非对称加密我们虽然只要在客户端拥有真正的公钥就可以实现加密通信,但实际上我们也无法实现保存所有网站的公钥。所以我们能不能通过信任部分第三方机构,实现「验证公钥的真实性」呢?实际上我们的浏览器和操作系统保存了许多信任的「证书发布机构」,通过证书发布机构CA我们可以验证公钥的真实性。在上面的例子里我们的服务端是直接返回公钥,但是引入第三方机构后,服务端返回的应该是证书。

什么是证书?

证书是网站所有者向证书发布机构CA申请的证明,证书包含的内容如下:

  1. 证书的发布机构CA
  2. 证书的有效期
  3. 公钥
  4. 证书所有者
  5. 签名

如何鉴别证书真伪?

首先我们还是看一下「非对称加密」,如果我们将「解密秘钥」公开而「加密秘钥」秘密保存,这时候的「公钥」就是「解密秘钥」,「私钥」就是「加密秘钥」,我们操作系统中保存的就是「证书发布机构CA」的「解密秘钥(公钥)」。证书中有一个重要的成分是「签名」,先将证书进行哈希计算然后把哈希结果通过「证书发布机构CA」的「加密密钥(私钥)」加密后所得到的密文就是「签名」。鉴别过程如下:

(1)首先浏览器读取证书中的证书所有者、有效期等信息进行一一校验

(2)浏览器开始查找操作系统中已内置的受信任的证书发布机构CA,与服务器发来的证书中的颁发者CA比对,用于校验证书是否为合法机构颁发

(3)如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的。

(4)如果找到,那么浏览器就会从操作系统中取出 颁发者CA 的公钥,然后对服务器发来的证书里面的签名进行解密

(5)浏览器使用相同的hash算法计算出服务器发来的证书的hash值,将这个计算的hash值与证书中签名做对比

(6)对比结果一致,则证明服务器发来的证书合法,没有被冒充

(7)此时浏览器就可以读取证书中的公钥,用于后续加密了

0x04 总结

上面所介绍的就是 HTTPS 的思想,对于细节部分并未详细解释。有兴趣细节请查看参考链接:

从HTTP到HTTPS再到HSTS

HTTPS系列干货(一):HTTPS 原理详解