现如今的 Web,HTTPS 早已经成为标配,公开的 HTTP 网站已经和 Flash 一样,慢慢在消亡了。
启用 HTTPS 的核心是一个叫做 证书 的东西。不知道大家是否有留意,前几年上 12306 的时候,浏览器都会提示「您的链接不是私密链接」,这其实就是因为 12306 的证书有问题。如果点击「继续前往」,打开 12306 网站,它会提示你下载安装它提供的“根证书”。
那么,证书是什么?里面含有什么内容?浏览器为什么会不信任 12306 的证书?为什么下载 12306 提供的根证书就可以解决这个问题?根证书又是什么?
证书,公钥和私钥
我们先来简单回顾一下 TLS 和 HTTPS。关于这个话题,我在 从零开始搭建一个 HTTPS 网站 这篇博客中有详细的论述,对细节感兴趣的朋友可以先去读一下这篇博客。
HTTPS 全称是 HTTP Over TLS,也就是使用 TLS 进行 HTTP 通信。
TLS (Transport Layer Security) 是一个安全通信协议,用于建立一个安全通道来交换数据。
看名字可以知道这是一个传输层协议,因此应用层协议对它是没有感知的,HTTP, FTP, SMTP 都可以和 TLS 配合使用。
关于网络协议,阮一峰的这篇博客 互联网协议入门(一) 写的很好,值得一看。
TLS 创建安全链接的步骤如下:
- 双方协商使用的协议版本,加密算法等细节
- 服务器发送 证书 给客户端
- 客户端校验证书有效性
- 双方根据握手的一些参数生成一个对称秘钥,此后所有的内容使用这个秘钥来加密
我们先来看证书,证书是一个文件,里面含有目标网站的各种信息。
例如网站的域名,证书的有效时间,签发机构等,其中最重要的是这两个:
- 用于生成对称秘钥的公钥
- 由上级证书签发的签名
证书文件的格式叫做 X.509,由 RFC5280 规范详细定义。存储上分为两种,一种叫做 DER,是二进制的,还有一种叫做 PEM,是基于 Base64 的。
关于 RSA 的公钥和私钥记住一点就行:我们可以使用算法生成一对钥匙,他们满足一个性质:公钥加密的私钥可以解开,私钥加密的公钥可以解开。
RSA 算法的具体工作原理可以参考我这篇博客 RSA 的原理与实现。
证书,顾名思义,是用来证明自己身份的。因为发送证书的时候是明文的(这一步也没法加密),所以证书内容是可以被中间设备篡改的。
那么要怎样设计一套机制保证当我访问 github.com 的时候,收到的证书确实是 github.com 的证书,而不是某个中间设备随意发来的证书?
大家可以思考一下这个问题🤔。
解决办法是采用「信任链」。
首先,有一批证书颁发机构(Certificate Authority,简称为 CA),由他们生成秘钥对,其中私钥保存好,公钥以证书的格式安装在我们的操作系统中,这就是 根证书。
我们的手机、电脑、电视机的操作系统中都预装了 CA 的根证书,他们是所有信任构建的基石。当然,我们也可以自己下载任意的根证书进行安装。
接下来,只要设计一个体系,能够证明 A 证书签发了 B 证书即可。这样对于收到的任何一个证书,顺藤摸瓜,只要最上面的根证书在系统中存在,即可证明该证书有效。
比如说,我们收到了服务器发过来的 C 证书,我们验证了 C 是由 B 签发的,然后又验证了 B 是由 A 签发的,而 A 在我们的系统中存在,那也就证明了 C 这个证书的有效性。
这其中,A 是根证书,B 是中间证书,C 是叶证书(类似树中的叶节点)。中间证书可以有很多个,信任的链条可以任意长,只要最终能到根证书即可。
得益于 RSA 的非对称性质,验证 A 是否签发了 B 证书很简单:
- 计算 B 的 hash 值(算法随便,比如 SHA1)
- 使用 A 的 私钥 对该 hash 进行加密,加密以后的内容叫做「签名(Signature)」
- 将该「签名」附在 B 证书中
A 使用自己的私钥给 B 生成签名的过程也就是「签发证书」,其中 A 叫做 Issuer,B 叫做 Subject。
这样,当我们收到 B 证书时,首先使用 A 证书的公钥(公钥存储在证书中)解开签名获得 hash,然后计算 B 的 hash,如果两个 hash 匹配,说明 B 确实是由 A 签发的。
重复上面的过程,直到根证书,就可以验证某个证书的有效性。
接下来我们来问几个问题。
为什么需要中间证书?
为什么要设计中间证书这个环节?直接使用根证书进行签发不好吗?
这是因为根证书的私钥安全性至关重要,一旦被泄露,将引起巨大的安全问题。
所以,根证书的私钥都是被保存在离线的计算机中,有严格的操作规章,每次需要使用时,会有专人将数据通过 USB 拷贝过去,操作完了以后,再将数据带出来。
在这套流程下,直接将根证书用于签发普通证书是不现实的。想想这个世界上有多少网站,每天对证书的需求量都是巨大的,根证书的操作效率无法满足要求,因为不能批量和自动化。
同时,对根证书私钥的操作越多,泄露的风险也就越大,因此,人们就发明了中间证书。
使用根证书签发一些中间证书,这些中间证书就可以用来签发大量的叶证书,这个过程完全可以是自动化的,就像 Let’s Encrypt 那样。
同时,即便中间证书的私钥泄露了也不要紧,可以使用根证书把它们撤销掉,具体怎么撤销是另外一个话题了,这里不再展开。
通过使用中间证书,我们就可以做到既方便,又安全。
浏览器如何获取中间证书?
一般来说,服务器会将中间证书一并发送过来。也就是说,当我们访问某个网站时,收到的不是一个证书,而是一系列证书。
当然,这不是强制要求,在服务器不发送的情况下,浏览器也会使用一些方法去定位中间证书,比如
- 缓存之前下载过的