种一棵树最好的时间是十年前,其次是现在。

TLS-安全的信息通道

在上世纪80年代末,互联网还处于萌芽阶段,不仅简单而且脆弱。1989年,Tim Berners-Lee 在CERN(欧洲核子研究中心)提出了一个基于超文本的文档系统,这就是万维网的起源。到1991年,HTTP(HyperText Transfer Protocol,超文本传输协议)协议被引入,用在客户端和服务端之间传输超文本文档。这个新颖的协议使信息共享变得前所未有的简单和快捷。

但是,HTTP 协议有一个致命的缺陷:它是不加密的,所有的数据都以明文形式在网络上传输。随着互联网的飞速发展,这种不安全性带来了严重的安全隐患,比如数据窃听和中间人攻击。

1994年,网景公司(Netscape)开发了SSL(Secure Sockets Layer,安全套接层)协议,旨在为互联网通信提供加密和认证。但是,SSL 1.0在诞生之初并不完美,它存在许多安全漏洞,从未公开发布。1995年2月,网景发布了SSL 2.0,但依然存在一些安全问题。直到1996年11月,SSL 3.0的发布才解决了大部分安全漏洞,成为早期互联网安全通信的标准。

随着SSL的发展,网景进一步推出了一个新概念:HTTPS(HyperText Transfer Protocol Secure,超文本传输安全协议)。它是 HTTP 的一个扩展,在早期,它通过 SSL 协议提供安全的通信。1995年随着 SSL 2.0的发布,Netscape Navigator 浏览器开始支持 HTTPS,用户可以在浏览器中看到一个小锁图标,表明浏览器与网站之间的通信是加密的、安全的。这是一个革命性的时刻,因为它标志着互联网从此可以有一种保护用户数据安全的机制。

SSL 的成功为互联网安全通信奠定了基础,但故事并没有到此结束。1999年,IETF(互联网工程任务组)将 SSL 3.0 标准化,发布了 TLS 1.0(Transport Layer Security,传输层安全性协议)标准。虽然 TLS 1.0 相对 SSL 3.0 差异不大,但避免了不安全的 SSL 1.0和 SSL 3.0之间互操作。

随后 TLS 不断演进,2006年4月:TLS 1.1发布,解决了一些上一版本已知的安全漏洞。2008年8月,TLS 1.2发布,进一步增强了加密算法的灵活性和安全性。2018年8月,TLS 1.3发布,简化了握手过程,减少了延迟,并剔除了许多旧的、不安全的功能。如今,SSL 和 TLS 1.1 以及之前的版本已被弃用。自2017年2月起,Firefox 已默认启用 TLS 1.3,Chrome 曾在2017年将 TLS 1.3短暂设为默认,不过由于部分组件存在兼容性问题而取消。

随着移动互联网的普及,互联网的规模和复杂性急剧增加。为了鼓励网站采用 HTTPS,2014年,Google 宣布将 HTTPS 作为衡量搜索排名的一个因素。2015年,Let's Encrypt 推出了免费和自动化的 SSL/TLS 证书服务,此举极大降低了网站部署 HTTPS 的门槛。2017年,Chrome 和 Firefox 等浏览器开始将未使用 HTTPS 的网站标记为“不安全”。随着更多 CA 参与提供免费数字证书,以及浏览器和搜索引擎的推动,HTTPS 逐渐普及。如今大部分网站都提供 HTTPS 加密协议,明文的 HTTP 已经很少见了。

HTTP 与 HTTPS 网络层

建立在 TCP 之上的协议需要完成三次握手才能建立一个 TCP 连接。首先客户端会向服务端发送SYN(同步)包,表示请求建立连接。服务端收到后,发送SYN-ACK(同步-确认)包,表示同意建立连接。客户端发送ACK(确认)包,表示连接建立完成。TCP 握手数据是明文传输的。

在完成 TCP 三次握手,客户端和服务端建立 TCP 连接之后,往上的一层就开始工作了。HTTP 建立在 TCP 之上的应用层协议,在 TCP 连接建立之后,客户端开始向服务端发送 HTTP 请求,服务端收到后做出响应返回数据。这个过程中的所有数据是以明文传输的,不经过密码学方面的处理。

所谓 HTTPS,其实就是构建在 TLS 之上的 HTTP,在 TCP 之上加了一层 TLS 协议,因此 HTTPS 也称为 HTTP over TLS。HTTPS 的关键在于 TLS 协议,TLS 综合运用了对称密码 、公钥密码、消息认证码 、数字签名 、伪随机数等密码技术,在建立 HTTP 通信之前,先在通信双方之间通过 TLS 协议建立了一个安全的通道,只有通信的双方才能解读这些信息,后续建立在它之上的所有协议都是私密的。

TLS 握手是客户端与服务端创建连接之前,交换一系列数据或消息的过程,包含多个步骤,在握手这个过程,双方一来一回商定使用的加密算法、密钥和其它安全参数。根据不同的 TLS 版本,握手所需要的确切步骤和使用的密钥交换算法所有不同。TLS 握手成功之后,后续通信是通过对称密码进行的,这就表示客户端和服务端握手过程会协商一个共享的密钥。

使用 RSA 密钥交换算法

在 TLS 1.2及之前的版本,使用 RSA 密钥交换算法交换密钥,步骤大约是这样:

  • 客户端问候(client hello): 客户端向服务端发送“问候”消息开始握手。内容包含客户端支持的 TLS 版本、密码套件,还有一串由客户端生成的随机字节,称为客户端随机数(client random)。

  • 服务端问候(server hello): 作为对客户端问候的回复,服务端也返回一条消息,内容有服务端的 TLS 证书、服务端选择的密码套件,以及服务端随机数(server random),它是由服务端生成的另一串随机字节。

  • 客户端验证身份: 客户端会确认服务端数字证书的真实性和有效性,目的是确认服务端正是它所声称的身份,正在通信的域名是否匹配证书的域名,证书是否在有效期内等,

  • 客户端生成预主密钥: 客户端再生成另一串随机字节,称为预主密钥(premaster secret),使用刚获得的公钥加密(来自数字证书)后,发送给服务端。

  • 服务端解密预主密钥:服务端收到后,使用配对的私钥解密。

  • 各自创建会话密钥:现在客户端和服务端同时拥有三个相同的随机数:客户端随机数、服务端随机数、预主密钥。它们各自都使用这三个随机数生成会话密钥(是对称密码的密钥),用来加密和解密后续通信内容。客户端和服务端创建的会话密钥应该是相同的。

  • 客户端准备就绪:客户端发送一条“已完成”消息,而且消息用会话密钥进行加密。

  • 服务端准备就绪:服务端发送一条“已完成”消息,而且消息用会话密钥进行加密。

  • 实现安全对称加密:现在已完成握手,并使用会话密钥继续进行通信,后续的通信都通过对称密码进行加密和解密。

RSA 的问题是不支持前向保密,如果服务端的私钥泄露,那么曾经被截获的所有 TLS 流量都会被解密。

Diffie-Hellman 密钥交换算法

除了使用 RSA 传输密钥之外,也可以使用 Diffie-Hellman 算法交换密钥。Diffie-Hellman 的思想也类似于非对称加密。在讲述这个算法的数学原理之前,先举一个小例子,好对它有个形象的理解。

假设小明和小红正在讨论选定一种颜色,但是不能让旁边的小黑知道。于是,他们想到了一种的办法,他们先选择一个基础颜色(比如黄色)和一种混合两种颜色的方法,这个信息可以让小黑知道。然后他们各自还选了一种私有颜色,比如小明选了蓝色,小红选了红色,这两个颜色只有他们自己知道,是不能告诉旁人的。接下来他们各自将自己的私有颜色和公共颜色混合,小明的蓝色+黄色变成了绿色,小红的红色+黄色得到了橙色。

到了交换环节,他们交换各自的混合色,这个过程中即使小黑看到了混合色,也不知道它是由哪些颜色组成的。将混合色与自己私有的颜色混合起来之后,最终他们都会得到相同的混合色。这只是对 Diffie-Hellman 算法思想的类比,不必深究最终的混合色是否一致。

Diffie-Hellman 算法原理比较简单,我们先选两个数,一个大质数 p 和基数 g,这两个数是公开的,谁都可以知道,然后小明和小红分别选择自己的随机数 ab,然后使用下面的公式计算。

小明的结果:$A = g^a \bmod p$
小红的结果:$B = g^b \bmod p$

AB 是可以公开的,他们相互交换各自的结果,使用相同的方式计算之后,会得出相同结果。

小明的结果:$K = B^a \bmod p = (g^b \bmod p)^a \bmod p = g^{ab} \bmod p$
小红的结果:$K = A^b \bmod p = (g^a \bmod p)^b \bmod p = g^{ab} \bmod p$

这就是 Diffie-Hellman 算法原理,即使 pg 是公开的,只要不知道 ab,从 AB 反推 ab 是极困难的。基于这个数学原理,Diffie-Hellman 算法可以用在不安全的信道上交换密钥。

使用 Diffie-Hellman 交换密钥

与 RSA 客户端用公钥加密传输密钥不同,Diffie-Hellman 是通过客户端和服务端给出的参数计算后交换结果生成密钥的。

Diffie-Hellman(以下简称 DH)握手过程大约是这样:

  • 客户端问候:与 RSA 相同。

  • 服务端问候: 除了发送与 RSA 相同的数据之外,还额外多两个数据,一个是服务端 DH 的计算结果;另一个是将前面所有数据计算出一个数字签名。所有这些数据都会一同发回给客户端。

  • 客户端验证身份: 客户端先验证服务端返回消息的数字签名,确认内容没被篡改后,验证服务端的 TLS 数字证书,这里跟 RSA 握手一样。

  • 客户端 DH 参数: 客户端计算自己的 DH 结果,发送到服务端。

  • 客户端和服务端计算会话密钥: 现在客户端和服务端拥有三个相同的值:客户端随机数、服务端随机数、DH 参数,这三个值组成了会话密钥。

  • 客户端就绪: 将之前发送到消息做一个摘要,使用会话密钥加密后发送,用于服务端校验。

  • 服务端就绪:将之前发送到内容生成一个摘要,使用会话密钥加密后发送,用于客户端校验。

  • 实现安全对称加密:握手完成,之后使用会话密钥进行对称加密通信。

如果不特指的话,DH 算法通常是指静态的,即每次密钥交换时有一方的私钥是固定的。通常是服务端的私钥是固定,客户端私钥随机生成。与 RSA 传输密钥那样,都不具备前向安全,如果服务端的私钥泄露,那么曾经被截获的所有 TLS 流量都会被解密,目前 DH 交换密钥方式已被废弃。

非静态的 DH 算法称为 DHE(Diffie-Hellman Ephemeral),临时性的意思,每次密钥交换时双方的密钥都是随机生成的,每次通信过程的私钥不同,保证了前向安全,但是需要做大量乘法运算,性能差。

通常的做法是将 DHE 算法 与 ECC 椭圆曲线特性一起使用,称为 ECDHE,由于椭圆曲线的特性,ECDHE 可以使用更少的计算量来计算公钥和会话密钥,减少 DHE 性能问题的影响。

TLS 1.3

TLS 1.3 不支持 RSA,也不支持易受攻击的其他密码套件和参数。而且还缩短了 TLS 握手步骤,使 TLS 1.3 握手更快更安全。

TLS 1.3 握手的基本步骤为:

客户端问候:发送的问候消息包含了协议版本、客户端随机数、用于计算预主密钥的参数和密码套件列表。由于 TLS 1.3 已经删除了不安全的密码套件,因此存在的密码套件数量大大减少。大体上来说,由于简化了密码套件,客户端会假设它知道服务端的首选密钥交换方法。这减少了握手的总长度,这也是 TLS 1.3 握手与 TLS 1.0、1.1 和 1.2 握手之间的重要区别之一。

服务端生成主密钥:服务器已经接收到客户端的数据,包括随机数、客户端的参数和密码套件。而且它自己可以生成服务端随机数,因此,服务器可以创建主密钥。

服务端问候和“完成”:服务端问候包括服务端的证书、服务端随机数、选择的密码套件以及数字签名。因为它已经有了主密钥,所以它也发送了一个“完成”消息。

客户端“完成”:客户端验证签名和证书,使用服务端参数生成主密钥,并发送“完成”消息。

实现安全对称加密:至此,服务端和客户端都完成了握手,之后使用会话密钥进行对称加密通信。

会话恢复(0-RTT )

TLS 1.3 还支持一个更快的 TLS 握手版本,根本不需要客户端和服务端之间的任何往返沟通。如果客户端和服务器之前已经建立过连接(例如,如果用户之前访问过相应网站),它们可以各自从第一个会话中获取另一个共享密钥,称为“恢复主密钥”。为什么是第一次会话?因为在第一个会话期间,服务器还会向客户端发送称为会话票证的东西。客户端可以使用此共享密钥,在下一次会话的第一条消息中将加密数据连同会话票证一起发送到服务器。然后 TLS 连接会在客户端和服务器之间恢复。

版权声明: 本文为原创内容,未经许可,不得转载。