SSL/TLS 握手详解:从第一次连接到加密通信的全过程

当你访问一个 HTTPS 网站时,浏览器与服务端之间会发生一系列精密的"握手"操作——这就是 SSL/TLS 握手。整个过程在毫秒级完成,却完成了密钥协商、身份验证、加密算法确认,最终建立起一条安全的通信隧道。本文用图文并茂的方式,详细拆解 TLS 握手的每一个阶段。

👉 如果你想快速获取一张受浏览器信任的 SSL 证书,请访问 **EveryoneTrust SSL 证书购买与价格页**

什么是 SSL/TLS 握手?

SSL/TLS 握手是 TLS 协议中客户端与服务端之间协商加密参数、建立安全连接的过程。它发生在 TCP 握手完成之后、数据加密传输之前。

握手的核心目标有四个:

  1. 协商加密算法——双方约定使用哪套加密套件
  2. 验证服务端身份——确认服务器不是伪造的
  3. 生成会话密钥——双方独立计算出相同的对称密钥
  4. 建立安全通道——后续所有数据均使用对称加密传输

握手本身是明文传输的(不包含密钥),但握手中的每一步都是为了确保后续加密通信的安全。

TLS 握手的完整流程

总体时序图

客户端                                                    服务端
  |                                                         |
  |  1. ClientHello  (支持的TLS版本、加密套件、随机数)       |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  2. ServerHello  (选定TLS版本、加密套件、服务端随机数)    |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  3. Certificate  (服务端证书链)                         |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  4. ServerKeyExchange  (ECDH 参数 / 签名)              |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  5. CertificateRequest  (可选:请求客户端证书)           |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  6. ServerHelloDone  (服务端 Hello 完成)                |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  7. Certificate  (客户端证书,可选)                    |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  8. ClientKeyExchange  (ECDHE 参数 / 预主密钥)         |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  9. CertificateVerify  (客户端签名,可选)               |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  10. ChangeCipherSpec  (客户端:切换到加密模式)          |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  11. Finished  (加密的握手消息摘要)                      |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  12. ChangeCipherSpec  (服务端:切换到加密模式)          |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  13. Finished  (加密的握手消息摘要)                      |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  =========  加密的 Application Data 通信  =========       |
  |  <────────────────────────────────────────────────────> |

阶段一:建立安全连接(ClientHello & ServerHello)

ClientHello — 客户端发起请求

客户端(浏览器)首先发送 ClientHello 消息,包含以下关键信息:

字段说明示例
TLS 版本客户端支持的最高 TLS 版本TLS 1.3(最新版)
客户端随机数32 字节随机数据,参与密钥计算Client_Random
Session ID复用会话时使用(避免完整握手)abcd1234...
加密套件列表客户端支持的加密套件(按优先级排序)TLS_AES_256_GCM_SHA384
压缩方法支持的压缩方法(通常为空)null
扩展SNI、DNS 记录、ALPN、0-RTT 等server_name: example.com

SNI(Server Name Indication) 是一个关键扩展。当同一个 IP 上托管多个 HTTPS 网站时,浏览器通过 SNI 告诉服务器要访问的具体域名,服务器才能返回正确的证书:

ClientHello
  extension: server_name
    server_name: example.com      ← 浏览器告诉服务器要访问哪个网站

ServerHello — 服务端回应

服务端收到 ClientHello 后,选定最终使用的加密参数并回应:

字段说明
TLS 版本双方协商一致的 TLS 版本
服务端随机数32 字节随机数据(Server_Random
Session ID复用会话时返回
选定的加密套件从客户端列表中选出一个
选定的压缩方法通常为 null

阶段二:服务端身份验证(Certificate)

服务端证书链

服务端紧接着发送 Certificate 消息,包含证书链

Certificate (证书链)
  └── leaf.crt   (服务端证书,绑定你的域名)
      └── intermediate.crt  (中间 CA 证书)
          └── root.crt    (根证书,浏览器内置信任)

浏览器会逐级验证证书链:

验证步骤:
1. 叶子证书 → 中间证书:用中间 CA 的公钥验证签名
2. 中间证书 → 根证书:用根 CA 的公钥验证签名(部分由浏览器直接内置信任中间 CA)
3. 检查证书是否过期(notBefore ~ notAfter)
4. 检查证书是否被吊销(CRL / OCSP)
5. 检查域名是否匹配(CN 或 SAN)
6. 验证证书链完整(无中间证书缺失)

为什么需要证书链? 根证书由 CA 离线保管,通过中间证书向下签发。这样即使中间证书泄露,也可以吊销而不影响根证书的安全性。

服务端密钥交换(ServerKeyExchange)

对于 ECDHE(前向保密)加密套件,服务端需要发送 ECDHE 公钥参数:

ServerKeyExchange
  named_curve: secp256r1          ← 椭圆曲线
  public_exchange: 服务端 ECDHE 公钥
  signature: 用服务端私钥对 (Client_Random + Server_Random + ECDHE参数) 的签名

签名的作用是证明这些 ECDHE 参数确实来自真实的服务器,防止中间人篡改。

阶段三:客户端响应(ClientKeyExchange)

客户端密钥交换

对于 ECDHE 密钥交换,客户端也生成自己的 ECDHE 公钥:

ClientKeyExchange
  named_curve: secp256r1          ← 必须与服务端一致
  public_exchange: 客户端 ECDHE 公钥

双方独立计算得到相同的密钥

这是 TLS 握手中最精妙的部分——双方各自独立计算,数学上保证得出相同结果,且不会在网络上传输私钥:

密钥计算过程(ECDHE):

1. 双方各自生成 ECDHE 密钥对
   - 客户端:(client_priv, client_pub)
   - 服务端:(server_priv, server_pub)

2. 双方交换公钥(client_pub 和 server_pub)

3. 各自计算 ECDHE 共享密钥
   - 客户端:client_priv × server_pub = 共享点 G × a × b
   - 服务端:server_priv × client_pub = 共享点 G × b × a
   数学上:a × G × b = b × G × a
   因此两边计算出完全相同的共享密钥!

4. 由共享密钥派生出真正的会话密钥
   pre_master_secret → master_secret → session keys
   session keys = {
     client_write_MAC_key,    ← 客户端消息认证
     server_write_MAC_key,    ← 服务端消息认证
     client_write_key,        ← 客户端加密密钥
     server_write_key,        ← 服务端加密密钥
     client_write_IV,         ← 客户端初始化向量
     server_write_IV          ← 服务端初始化向量
   }

为什么 ECDHE 能实现前向保密? 因为每次会话使用临时的密钥对,即使攻击者记录了所有加密流量并后来获取了服务器的长期私钥,也无法解密历史通信——因为会话密钥由临时的 ECDHE 密钥对决定,而不是由服务器的长期私钥直接派生。

阶段四:握手完成(ChangeCipherSpec & Finished)

ChangeCipherSpec — 切换到加密模式

双方各自发送 ChangeCipherSpec 消息,通知对方:"从现在起,后续消息将使用协商出的加密算法和密钥。"

Finished — 验证握手完整性

Finished 消息是第一条加密的握手消息,包含双方对整个握手过程的摘要:

Finished
  verify_data = PRF(
      master_secret,
      "client finished" / "server finished",
      Hash(所有握手消息)
  )

双方计算对方发来的 Finished 消息,如果能正确解密并验证摘要,就证明:

  1. 握手没有被中间人篡改
  2. 双方持有相同的会话密钥
  3. TLS 连接已成功建立

⚠️ 注意:TLS 1.3 优化了握手流程(见下节),但 ChangeCipherSpecFinished 的作用逻辑不变。

TLS 1.3 对握手的重大优化

TLS 1.3(2018 年发布,RFC 8446)将握手从两轮往返(2-RTT)优化为一轮往返(1-RTT),并在安全性和性能上有大幅提升。

TLS 1.3 vs TLS 1.2 握手对比

对比维度TLS 1.2TLS 1.3
握手往返数2-RTT1-RTT(初访)/ 0-RTT(重连)
完整握手耗时~100–300ms~50–100ms
服务端证书传输紧跟 ServerHello紧跟 ServerHello(不变)
ServerKeyExchange需要(RSA 密钥交换)不再需要单独的 ServerKeyExchange
RSA 密钥交换支持(不支持前向保密)已移除(仅保留 ECDHE)
前向保密可选(ECDHE)强制要求
0-RTT 重连不支持支持(0-RTT)
支持的加密套件数百种仅 5 种推荐套件
加密算法大量遗留算法仅保留 AEAD(AES-GCM, ChaCha20-Poly1305)
密钥导出函数MD5/SHA-1/SHA-256 混用仅使用 HKDF

TLS 1.3 握手时序(1-RTT)

客户端                                                    服务端
  |                                                         |
  |  1. ClientHello                                         |
  |     + supported_versions(TLS 1.3)                       |
  |     + key_share(ECDHE 客户端公钥)    ← 提前携带密钥参数  |
  |     + supported_groups(secp256r1...)                    |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  2. ServerHello                                         |
  |     + version(TLS 1.3)                                  |
  |     + key_share(ECDHE 服务端公钥)    ← 服务端直接响应密钥  |
  | ─────────────────────────────────────────────────────── |
  |                                                         |
  |  === 双方立即计算出相同的会话密钥 ===                     |
  |                                                         |
  |  3. {EncryptedExtensions} (加密)                        |
  |  4. {Certificate} (加密)                               |
  |  5. {CertificateVerify} (加密)   ← 用证书私钥签名        |
  |  6. {Finished} (加密)   ← 含握手摘要验证                 |
  | ──────────────────────────────────────────────────────> |
  |                                                         |
  |  7. {Finished} (加密)                                   |
  | <────────────────────────────────────────────────────── |
  |                                                         |
  |  =========  加密的 Application Data 通信  =========       |
  |  <────────────────────────────────────────────────────> |

TLS 1.3 的关键安全改进

1. 强制前向保密(PFS)

TLS 1.3 完全移除了不支持前向保密的 RSA 密钥交换和静态 DH 密钥交换,仅允许 ECDHE 和 DHE。这意味着即使攻击者获取了服务器的长期私钥,也无法解密历史通信记录。

2. 减少攻击面

TLS 1.3 仅保留以下 5 种推荐加密套件(cipher suites):

套件说明
TLS_AES_256_GCM_SHA384AES-256-GCM + SHA-384(最高安全)
TLS_CHACHA20_POLY1305_SHA256ChaCha20-Poly1305 + SHA-256(移动端优化)
TLS_AES_128_GCM_SHA256AES-128-GCM + SHA-256
TLS_AES_128_CCM_SHA256AES-128-CCM + SHA-256(受限环境)
TLS_AES_128_CCM_8_SHA256AES-128-CCM-8 + SHA-256(受限环境)

3. 0-RTT 数据

TLS 1.3 支持 0-RTT 模式,在首次连接时就发送应用数据(如 HTTP 请求),大幅降低延迟。但 0-RTT 存在重放攻击风险,适用于对安全性要求相对较低且可接受重放风险的场景(如 CDN 读取静态资源)。

握手过程中的常见安全问题

问题一:证书链不完整

如果服务端只配置了叶子证书,缺少中间 CA 证书,部分客户端(尤其是 Java 客户端)无法构建完整的证书链,导致握手失败。

错误日志:
javax.net.ssl.SSLHandshakeException:
  PKIX path building failed: ... unable to find valid certification path to requested target

解决方案:在服务器配置中加载完整证书链(Nginx:ssl_certificate_chain

问题二:SNI 与证书不匹配

当多个 HTTPS 站点共享同一 IP 时,SNI 尤为重要。某些旧版 TLS 库不支持 SNI,可能导致服务器返回默认证书,而该证书与请求的域名不匹配。

解决方案:升级 TLS 库版本,确保 SNI 支持

问题三:中间人攻击(MITM)

如果攻击者拦截了握手过程,替换了服务端证书,就会发生中间人攻击。攻击者持有自己的证书(由攻击者自己的 CA 签发),无法通过浏览器内置的受信任根证书库验证。

浏览器的防护机制

  1. 验证证书链至受信任根 CA
  2. 检查证书吊销状态(OCSP / CRL)
  3. 检查证书透明度(Certificate Transparency)

问题四:TLS 版本降级攻击

攻击者可能诱导双方使用过时的 TLS 版本(如 TLS 1.0),绕过现代安全保护。

防护措施:服务器端禁用 TLS 1.0/1.1,仅启用 TLS 1.2 和 TLS 1.3:

ssl_protocols TLSv1.2 TLSv1.3;

会话恢复与会话 ID

完整握手的计算成本较高(尤其是 ECDHE 的椭圆曲线运算),TLS 支持会话恢复机制,避免重复握手。

会话 ID 机制(Session Resumption)

首次握手:
  服务端生成 Session ID,随 ServerHello 返回
  客户端缓存 Session ID 和 master_secret

重连时:
  客户端在 ClientHello 中带上 Session ID
  服务端查找并恢复会话,跳过完整握手

  ClientHello (Session ID: "abc123")
  ServerHello (Session ID: "abc123") + ChangeCipherSpec + Finished
  握手完成!仅 1-RTT

TLS 1.3 PSK(预共享密钥)机制

TLS 1.3 将会话恢复升级为 PSK(Pre-Shared Key) 机制,允许双方在首次握手时就约定一个"会话票据"(ticket),后续凭此票据直接恢复会话:

0-RTT 重连(TLS 1.3):
  客户端在 ClientHello 中直接携带加密的应用数据
  服务端验证 PSK 后立即解密并响应
  适合 CDN 等对延迟极度敏感的场景

HTTPS 连接的安全保障总结

SSL/TLS 握手通过以下机制保障连接安全:

安全保障实现机制
身份验证证书链验证 + CA 根证书信任 + 域名匹配
密钥安全ECDHE 密钥交换 + 前向保密(PFS)
数据机密性AES-GCM / ChaCha20-Poly1305 对称加密
数据完整性AEAD(认证加密)同时提供加密和完整性校验
握手完整性Finished 消息摘要验证
防降级攻击强制 TLS 1.2+,TLS 1.3 完全移除旧版本

如何检查自己网站的 TLS 握手

# 使用 OpenSSL 查看 TLS 握手详情
openssl s_client -connect example.com:443 -tls1_3 -debug

# 查看完整握手过程(包括证书链)
openssl s_client -connect example.com:443 -showcerts

# 测试 TLS 1.3 握手
openssl s_client -connect example.com:443 -tls1_3

# 使用 SSL Labs 在线检测(最全面)
# 访问 https://www.ssllabs.com/ssltest/

总结

SSL/TLS 握手是互联网安全的基础机制,其核心逻辑清晰而严密:

  • ClientHello / ServerHello 协商加密参数
  • Certificate 验证服务端身份(通过证书链)
  • ECDHE 密钥交换 双方独立计算出相同的会话密钥
  • Finished 验证握手完整性,确认双方持有相同的密钥
  • 之后所有数据通过 AEAD(AES-GCM / ChaCha20-Poly1305)加密传输

TLS 1.3 通过强制前向保密、简化加密套件、减少握手往返,将这一过程提升到了一个新的安全与性能水准。升级到 TLS 1.3 是目前保障 HTTPS 安全的最佳实践。

👉 立即为你的网站部署 TLS 1.3 兼容的 SSL 证书:**EveryoneTrust SSL 证书购买入口**