网络通信是怎么加密的
0.引言
这是Google的官方网站,在这里我们可以看到浏览器地址栏有一个“tune”图标表示这个连接是安全的。同时,我们可以点击这个图标,会跳出一个卡片(也就是图中的那个),显示一些扩展信息,比如“证书”之类。
那么……为什么浏览器会显示这个图标呢?它是怎么判断连接是否“安全”的?
浏览器怎么定义“安全”?为什么要有这个图标?
答案是HTTPS(Hypertext Transfer Protocol Secure,超文本传输安全协议),它通过一系列手段将网络通信的内容加密避免被攻击者截获。在互联网建立初期(1990年代-2000年代初期),绝大多数互联网通信都是明文的,很容易被截获,当HTTPS出现后浏览器为将HTTPS网站和HTTP网站区分就在HTTPS网站上加了这个图标。
0.1 HTTPS是怎么加密的?
HTTPS的加密依赖的是TLS(Transport Layer Security,传输层安全性协议)加密;而2010年之前主要是其前身SSL(Secure Sockets Layer,安全套接层)。所以HTTPS相较明文传输的 HTTP(Hypertext Transfer Protocol) 多了一层加密保护数据的安全。
1.密钥是怎么共享的?
1.1 TLS做了什么?
TLS的本质是非对称加密(Asymmetric cryptography)和对称加密(Symmetric-key algorithm)的结合,其中,非对称算法主要用于身份验证和协商密钥,因为非对称加密性能开销相当大且低效(相对对称加密而言);而实际的数据传输则通过协商出的 会话密钥(Session Key) 使用高效的对称加密算法来加密。
简单来说,TLS(较早期的版本,例如TLS1.0/1.1/1.2)做了这些来保护你的密钥:
生成一个对称加密密钥
使用非对称加密算法传输共享密钥
然后快乐加密
但是,在新版的TLS1.3(RFC8446定义)中,非对称加密对称密钥(通常使用RSA)的方法已被弃用,改为直接协商共同生成一个相同的密钥(注意:这个密钥从未被传输)。在实际使用中,通常使用 ECDHE(椭圆曲线 Diffie-Hellman 临时密钥交换) 交换方法。这种方式不仅提升了效率和安全性,还实现了所谓的 前向安全性(Forward Secrecy) —— 即使服务器的长期密钥将来被泄露,也无法解密过去的通信内容。
所以TLS(更新的版本,如TLS1.3)做了这些来保护你的密钥:
客户端和服务端同时协商密钥
各自协商出一个共同密钥
然后快乐加密
但是,在密钥协商过程中,服务器不会直接发送裸露的公钥,而是将SSL/TLS证书(SSL/TLS Certificate)发送给客户端以证明其身份。
1.2 为什么要有证书?
即使我们使用了最强的加密方法,但我们现在依旧有个问题:怎么证明服务端是不是真正的服务端?
举个例子:客户端A要和服务端B建立连接,此时有攻击者C处在AB中间,C截获了A发送给B的报文,自己生成了一对公钥/私钥,将自己的公钥伪装为服务端的公钥发送给A,自己伪装一个客户端向B发送报文,B将真实的公钥返回给攻击者C,于是客户端A、服务端B的连接在毫不知情的情况下被攻击者C截获,攻击者C将有能力完全解密客户端A和服务端B通信的所有内容并篡改,此时所谓加密也就形同虚设。这就是著名的MITM攻击(Man In The Middle,中间人攻击)。所以,为了确保密钥未被篡改和连接的安全,SSL/TLS证书出现了。
简单来说:密钥本身没有任何问题,加密算法也没有,问题是你不知道密钥到底是谁的。
所以,为了确保密钥未被篡改和连接的安全,我们需要一种验证服务端身份的机制——这就是证书的用途。
2.怎么信任一个证书?
2.1 证书里有什么?
SSL/TLS证书内部通常会包含这些部分(可能因版本或具体实现略有出入):
- 服务端的公钥
- 公用名、国家、组织
- 签发机构等
- SAN(Subject Alternative Name,主题备用名称),用以覆盖多个主机名(不是所有证书都有)
- “签名”(Digital Signature,又名“公钥数码签名”)。
签名不是由服务端自主生成的,而是来自CA(Certificate Authority,证书颁发机构)。只有由可信的CA签名颁发的证书才会被客户端所信任,客户端会在本地系统存有这些CA的根证书,即“受信任的根证书列表”,其中包含了这些CA的公钥,用于验证签名。
简单来说:证书的意义不在于加密通信,而是**“证明这个公钥是服务端的”。**
2.2 怎么验证签名?
SSL/TLS证书内有CA的签名,那么如何验证?答案是签名算法。签名算法本质是摘要算法和非对称加密算法的结合,最主流的签名搭配是SHA-256和RSA-2048/ECDSA或SM2和SM3(主要在中国大陆的部分服务使用,较少见)
要验证签名,我们需要先了解签名。数字签名的操作流程如下:

我们可以看到,CA首先计算了证书数据hash并用私钥对其签名,随后将其写入签名域,所以CA的签名在技术上仅仅是一个被CA认证的哈希值,仅此而已。真正有价值的是“这段签名数据是CA用自己的私钥签名的”对这段数据真实性的保证作用,而不是这段签名数据本身。签名的作用是保证数据未被篡改,而不是存储证书信息(毕竟哈希算法不可逆对吧23333)。
所以,通过对CA签名过程的理解,我们自然不难推理出用户验签的过程。如图所示,用户在收到CA签名的证书后,会先将签名域的数据提取出来,使用CA的公钥还原CA签名时证书明文部分的哈希值,并同时计算证书明文部分的哈希值,将二者进行比对,如果相同则证明证书及服务端的密钥未被篡改,如果不同则表明证书被篡改而不可信。

3.我们怎么信任CA?
3.1 CA的证书哪里来?
上文提到,我们需要用CA的公钥还原哈希值来验被签名的证书,那么我们哪来的CA的公钥?怎么确保CA的公钥是真的?答案就藏在你的操作系统的证书目录里,“受信任的根证书列表”就默默躺在那,提供CA的公钥供你比对(尽管这个过程都是软件自己完成就是)。
那我问你,CA的证书谁签名的?——一位不愿透露姓名的网友
那我回答你,答案很简单:CA自己签的(也有一部分的CA是中级CA,由上游的CA签名)。事实就是这样,你别不信,有图有真相:

所以你可以这么理解:
根CA就像“我们敬爱的***同志”,ta自己给自己签发证书,我们默认就信任;
其他服务器的证书,只要是ta本人或ta的下属们签发的,我们也就信了。
这就是“信任链”,又名“证书链”。当然,这种建立在信任之上的安全似乎不怎么稳固……
那我问你,CA一滥权这个模型不就崩了?——一位网友
那我回答你:啊对,就是这么个事!CA一滥权直接玩完,草(一种植物)!
3.2 怎么防止CA滥权和密钥泄露?
2015年发生了一起CA滥权事件,由CNNIC发行的一个中级CA未经授权签发了一张Google域名的假证书,在当时引起巨大争议,此事件之后对于CA透明度的问题引起了行业重视,并对CA增加了监管。(资料出处:维基百科)
既然这个模型下如果CA滥权则信任体系即刻土崩瓦解,那我们怎么防止CA滥权?答案是引入一个新的监管机制,即CT(Certificate Transparency,证书透明度)。2018年起,任何CA签发的证书都需要登记在CT日志上,否则将不被信任。
但是,只有CT还不够。如果网站保管不当发生密钥泄漏事故或者CA滥权乱发证书,该怎么办?我们将引入一个新的机制:证书吊销列表(Certificate revocation list,CRL)。在这个列表上的证书都是因各种原因而被吊销的证书,它们将不再被任何的设备所信任,浏览器在接收服务端的证书时也会查询证书吊销列表确认该证书是否已被吊销以保证连接安全性。当然,CRL也有其问题,例如其实时性和可靠性较差,部分浏览器为速度快甚至根本不核查证书有效性。
当然,为了连接的安全性,在实际操作中还会引入新的机制,例如OCSP(Online Certificate Status Protocol,在线证书状态协议),它定义浏览器在收到证书后向CA实时查询证书的有效性,但缺点也一样:实时性差,且对部分用户会造成隐私问题,故又有推出OCSP装订(OCSP Stapling),服务器会在发送证书时发送预先缓存的OCSP相应而无需用户再次向CA发出请求查询证书有效性。但是OCSP装订同一时间只能发送一个OCSP响应,对于有中级证书的证书链并不够。该问题在RFC 6961中被解决。
参考资料:
传输层安全性协议 - Wikipedia
参见:
