首页/我的作品/密码学/9.2 密码是怎么输的:TLS握手详解
密码学:守护数字世界的秘密

9.2 密码是怎么输的:TLS握手详解

第 51 / 78 篇 · Crazy Boy

在上一节,我们看到了浏览器地址栏的小锁,知道它代表一个加密通道已经建立。但你有没有想过:那个加密通道到底是怎么建成的?在你按下回车后、网页出现前的短短几百毫秒里,客户端和服务器之间到底说了什么?

这背后的流程,就是 TLS 握手(TLS Handshake)。它像两个陌生人第一次见面,互相确认身份、商量暗号、交换钥匙,然后才放心地开始交谈。

握手的目标

TLS 握手有三个主要目标:

  1. 协商加密算法:客户端和服务器都支持哪些加密算法?选一个双方都支持且最安全的。
  2. 身份认证:客户端确认服务器的身份(通过证书),防止中间人攻击。
  3. 生成会话密钥:安全地生成一个临时的对称密钥,用于后续加密传输。

整个过程涉及非对称加密(用于身份认证和密钥交换)和对称加密(最终用于数据加密),以及哈希函数(用于完整性校验)。

TLS 1.2 握手流程详解

下面我们以最常见的 TLS 1.2 为例,一步一步拆解握手过程。为了直观,我们配合一个简化的流程图来理解。

客户端(浏览器)                              服务器(网站)
| |
| 1. ClientHello |
| (支持的TLS版本、加密套件、随机数) |
|--------------------------------------------->|
| |
| 2. ServerHello |
| (选择的版本、加密套件、随机数) |
| ServerCertificate |
| (服务器证书,包含公钥) |
| ServerHelloDone |
|<---------------------------------------------|
| |
| 3. ClientKeyExchange |
| (用服务器公钥加密的预主密钥) |
|--------------------------------------------->|
| |
| 4. 双方各自计算会话密钥 |
| (基于预主密钥和两个随机数) |
| |
| 5. ChangeCipherSpec |
| (通知对方:我准备切换加密) |
| Finished(加密的握手结束消息) |
|<-------------------------------------------->|
| |
| 6. 应用数据(HTTP请求/响应) |
| (全部用会话密钥对称加密) |
|<-------------------------------------------->|

第1步:ClientHello

客户端(浏览器)主动发起连接,发送一条 ClientHello 消息。这条消息包含:

  • 客户端支持的 TLS 版本:比如 TLS 1.2、TLS 1.1 等。
  • 一个随机数:称为 ClientRandom,用于后续生成密钥。
  • 加密套件列表:客户端支持的所有加密算法组合,比如 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 表示用 ECDHE 做密钥交换、RSA 做证书签名、AES 128 GCM 做对称加密、SHA256 做哈希。
  • 扩展字段:如服务器名称指示(SNI),告诉服务器要访问哪个域名(因为一个服务器可能托管多个网站)。

此时所有消息都是明文的,因为还没有任何加密。

第2步:ServerHello + Certificate

服务器收到 ClientHello 后,回复 ServerHello,其中包含:

  • 选择的 TLS 版本(从客户端支持的版本中选一个)。
  • 另一个随机数:ServerRandom。
  • 选择的加密套件(从客户端列表中选一个)。

紧接着,服务器发送自己的 数字证书(Certificate)。证书里包含了服务器的公钥以及网站的身份信息,由 CA 签名。

最后,服务器发送 ServerHelloDone 表示自己的初始消息发送完毕。

此时,客户端已经收到了服务器的证书。它会验证证书的有效性:检查域名、有效期、证书链等。如果验证失败,浏览器会显示警告,并中断连接。

第3步:ClientKeyExchange

客户端确认证书有效后,生成一个 预主密钥(Pre-master Secret)。这是一个临时生成的随机数,长度与加密算法相关(比如 RSA 密钥交换时为 48 字节)。然后,客户端用服务器的公钥加密这个预主密钥,通过 ClientKeyExchange 消息发送给服务器。

由于只有服务器拥有对应的私钥,因此只有它能解密得到预主密钥。任何窃听者都无法解密,因为公钥加密的内容只能用私钥解密。

第4步:计算会话密钥

现在,双方都拥有了三个素材:

  • ClientRandom
  • ServerRandom
  • Pre-master Secret

它们通过一个称为 密钥导出函数(KDF,Key Derivation Function)的算法,分别计算出相同的 会话密钥(Session Key)。这个会话密钥通常是一个对称密钥,用于后续加密数据。根据加密套件,还可能导出其他密钥,比如用于校验的 MAC 密钥。

此时,非对称加密的使命已经完成,接下来的通信将全部使用对称加密,速度快得多。

第5步:ChangeCipherSpec + Finished

客户端发送 ChangeCipherSpec 消息,告诉服务器:“从现在开始,我会用刚刚算出的会话密钥加密后续消息。”紧接着,客户端发送 Finished 消息,这是第一条加密的消息,内容是对整个握手过程(所有之前交换的消息)的哈希值,用于验证握手没有被篡改。

服务器收到后,同样回复 ChangeCipherSpec 和自己的 Finished 消息,也用会话密钥加密。客户端验证服务器的 Finished 消息后,握手正式完成。

第6步:应用数据

此后,所有 HTTP 请求和响应(网页、图片、表单数据等)都通过会话密钥进行对称加密,安全地传输。

关键点:为什么这样安全?

  • 身份认证:通过证书,客户端确认了服务器的身份,防止冒充。
  • 密钥交换:预主密钥用服务器公钥加密,只有服务器能解密,保证了密钥的机密性。
  • 随机数:客户端和服务器各自贡献一个随机数,防止重放攻击(攻击者无法复用之前的握手)。
  • 完整性:Finished 消息使用了整个握手的哈希,任何篡改都会被发现。

TLS 1.3 的简化

TLS 1.2 虽然广泛使用,但握手需要两次往返(2-RTT)。为了提高性能,TLS 1.3 进行了大幅简化,将握手减少到一次往返(1-RTT)。它去掉了复杂的加密套件组合,固定使用安全的算法,并把很多消息合并发送。更重要的是,TLS 1.3 强制要求前向保密(如 ECDHE),使得即使服务器的长期私钥泄露,也无法破解过去的会话。

当你看到小锁时

现在你知道了,那把锁不是凭空出现的。在你输入网址后的短短几百毫秒里,浏览器和服务器已经完成了一轮精心设计的密码学对话:它们交换了随机数,验证了身份,协商了加密算法,生成了只有双方知道的会话密钥。然后,所有的网页内容、图片、表单,都被这个密钥加密,在互联网的汪洋大海中安全地漂流,直到抵达你的屏幕。

这,就是现代互联网安全的基石。