# TLS 工作原理：加密、完整性与身份认证一次讲清

> HTTPS 背后的 TLS 怎么工作：密钥交换、证书、对称加密、记录层，以及一次请求从 TCP 到 HTTP/3 的完整过程。

Source: https://ssl.sh/zh/learn/how-tls-works/
Last updated: 2026-11-10

TLS（传输层安全协议）就是让 HTTP 变成 HTTPS 的那一层。它只在开头很短的握手里用一次公钥密码：验证服务器身份、协商出一组全新的会话密钥；之后所有数据都用快速的对称算法加密并校验。结果是：中间人看不懂内容、改不了数据，没有该域名的证书就冒充不了服务器。

## TLS 到底保护了什么

TLS 提供三项保证：机密性、完整性和身份认证，三者分别靠不同的机制实现。

| 保证 | 含义 | TLS 1.3 中的实现 | 防住的攻击 |
|---|---|---|---|
| 机密性 | 只有通信双方能读懂数据 | AEAD 加密（AES-GCM、ChaCha20-Poly1305），密钥来自临时密钥交换 | 公共 Wi-Fi、运营商、骨干网上的被动监听 |
| 完整性 | 数据被改动一定会被发现 | 每条记录带认证标签，序号参与 nonce | 运营商插广告、注入脚本、重排或重放记录 |
| 身份认证 | 你连的确实是 `example.com` | 证书链 + 服务器对握手内容的签名（CertificateVerify） | 伪造服务器的中间人攻击 |

身份认证通常是单向的：服务器出示证书，客户端在 TLS 层保持匿名，之后再用密码或 Token 登录。双方都出示证书的叫双向 TLS，见[双向 TLS](https://ssl.sh/zh/learn/mtls/)。

TLS **不**负责隐藏一切。服务器 IP、流量大小和时序、以及大多数情况下的域名（通过 DNS 和 [SNI](https://ssl.sh/zh/glossary/sni/) 字段）对网络仍然可见。TLS 也不担保网站本身是好是坏：DV 证书只证明申请人在签发那一刻控制着这个域名（见 [DV、OV、EV 的区别](https://ssl.sh/zh/learn/dv-ov-ev/)）。

## 两类密码学分工合作

TLS 把慢而灵活的非对称密码用在建立连接上，把快的对称密码用在传数据上，两者缺一不可。

| | 非对称（公钥）密码 | 对称密码 |
|---|---|---|
| TLS 里的例子 | ECDHE（X25519、P-256）、混合算法里的 ML-KEM、RSA/ECDSA 签名 | AES-128-GCM、AES-256-GCM、ChaCha20-Poly1305 |
| 密钥形式 | 公钥 + 私钥一对 | 每个方向一把共享密钥 |
| 速度 | 每秒几千次运算 | 有硬件加速时每秒数 GB |
| 在 TLS 中的任务 | 协商密钥、证明身份 | 加密并认证每条记录 |

握手中跑的是临时 Diffie-Hellman 交换：双方各自生成一次性的密钥对，把公钥发给对方，再用自己的私钥和对方的公钥算出同一个共享秘密。窃听者能看到两个公钥，却算不出这个秘密。由于这对临时密钥用完即丢，哪怕日后服务器的证书私钥泄露，也解不开之前录下的流量，这就是[前向保密](https://ssl.sh/zh/glossary/forward-secrecy/)。TLS 1.3 的完整握手强制具备前向保密。

共享秘密不会直接拿来加密，而是先经过 HKDF 密钥派生，为每个方向、每个阶段分别生成密钥和 IV。之后所有数据都用 [AEAD](https://ssl.sh/zh/glossary/aead/) 算法保护，加密和认证一步完成。

2024 年 11 月起（Chrome 131），Chrome 默认使用混合密钥交换 X25519MLKEM768，把经典的 X25519 和后量子算法 ML-KEM-768 叠在一起；2025 年 4 月发布的 OpenSSL 3.5 也默认启用。这样即使将来量子计算机攻破 X25519，今天录下的流量依旧安全，详见[后量子 TLS](https://ssl.sh/zh/learn/post-quantum-tls/)。360、QQ 等国内主流浏览器基于 Chromium 内核，行为通常跟随对应版本的 Chrome。

## 证书在哪里起作用

光有密钥交换，还不知道“跟谁”交换了密钥。中间人完全可以分别和你、和真服务器各做一次密钥交换。证书就是用来解决这个问题的。

TLS 服务器证书是一份 [X.509](https://ssl.sh/zh/glossary/x509/) 文档，把一个公钥和一个或多个域名（写在 SAN 扩展里）绑定在一起，并由[证书颁发机构（CA）](https://ssl.sh/zh/glossary/certificate-authority/)签名。握手时客户端会：

1. 收到服务器证书和中间证书；
2. 从服务器证书一路向上构建证书链，直到操作系统或浏览器信任库里的某个[根证书](https://ssl.sh/zh/glossary/root-certificate/)；
3. 检查访问的域名是否在 SAN 里、当前时间是否在有效期内、密钥用途是否允许 TLS 服务器认证，并视客户端而定检查吊销状态和证书透明度证明；
4. 验证服务器用私钥对握手记录所做的 **CertificateVerify** 签名。只有持有私钥的人才签得出来，所以单纯复制一份证书毫无用处。

任何一步失败，浏览器就会弹出 [NET::ERR_CERT_AUTHORITY_INVALID](https://ssl.sh/zh/errors/net-err-cert-authority-invalid/)、[NET::ERR_CERT_COMMON_NAME_INVALID](https://ssl.sh/zh/errors/net-err-cert-common-name-invalid/) 之类的拦截页。证书链的细节见[证书链](https://ssl.sh/zh/learn/certificate-chains/)。

## 用一段话讲完握手

TLS 1.3 里，客户端先发 ClientHello，里面已经带好了它猜服务器会选的密钥交换公钥（key share），以及支持的密码套件、签名算法、要访问的域名（SNI）和应用层协议（ALPN）。服务器回 ServerHello，附上自己的 key share。此时双方都能算出握手密钥，于是之后的 EncryptedExtensions、Certificate 证书链、CertificateVerify 和 Finished 全部加密传输。客户端验证证书和签名，发出自己的 Finished，紧接着就可以发应用数据。整个过程只要 1 个往返，每条消息的细节见 [TLS 握手详解](https://ssl.sh/zh/learn/tls-handshake/)。

## 记录层：数据实际怎么传

握手之后，所有数据都装在一条条 TLS 记录里传输，每条最多 16KB 明文。记录由 5 字节头部、加密后的内容和认证标签组成：

```text
+--------+---------+---------+------------------------------------+-----------+
| 类型   | 版本    | 长度    | 加密内容                           | 认证标签  |
| 1 字节 | 2 字节  | 2 字节  | （数据 + 真实类型 + 可选填充）     | 16 字节   |
+--------+---------+---------+------------------------------------+-----------+
  0x17     0x0303    <= 2^14+256
  （TLS 1.3 里外层类型永远是 application_data）
```

实际运维中值得知道的几点：

- **大小**：每条记录明文上限 2^14 = 16384 字节，大的 HTTP 响应会被切成很多条记录。
- **nonce**：AEAD 的 nonce 由每个方向的 IV 与 64 位记录序号异或得到。记录被丢弃、重排或重放，下一次标签校验就会失败。
- **隐藏真实类型**：TLS 1.3 外层类型统一写成 `application_data`（23），真实类型（握手、告警还是数据）藏在密文里，还可以加填充掩盖长度。
- **告警**：出错时发送告警记录，例如 `handshake_failure`、`bad_certificate`；正常关闭发送 `close_notify`，接收方据此判断响应是完整的还是被截断了。

TLS 需要一条可靠的字节流，通常就是 TCP。基于 UDP 的版本叫 DTLS，QUIC 则走了另一条路，下面会讲。

## 一次 HTTPS 请求的完整过程

首次访问 `https://example.com/`（HTTP/1.1 或 HTTP/2，TLS 1.3）的时序如下：

```text
浏览器                                                example.com (203.0.113.10)
   |  DNS 查询 example.com 的 A/AAAA/HTTPS 记录 ---> 解析器       |
   |  <--- 203.0.113.10                                          |
   |                                                             |
   |  TCP SYN  ------------------------------------------------->|   第 1 个往返
   |  <------------------------------------------------- SYN-ACK |   （TCP）
   |  ACK + TLS ClientHello（key_share、SNI、ALPN h2）---------->|
   |                                                             |   第 2 个往返
   |  <---- ServerHello + {EncryptedExtensions, Certificate,     |   （TLS）
   |          CertificateVerify, Finished}                       |
   |  校验证书链、域名、签名                                      |
   |  {Finished} + [HTTP GET /] -------------------------------->|
   |  <-------------------------------------- [HTTP 200 + 正文]  |   第 3 个往返
   |                                                             |
   { } = 握手密钥加密    [ ] = 应用数据密钥加密
```

也就是说，冷启动时不算 DNS，要 3 个往返才能收到响应的第一个字节；TLS 1.2 的完整握手多 1 个往返。TLS 1.3 恢复会话时可以用 0-RTT 把请求塞进第一个包里，代价是存在重放风险（见 [TLS 1.3](https://ssl.sh/zh/learn/tls-1-3/)）。

国内很多站点前面挂着阿里云、腾讯云的负载均衡或 CDN，这时浏览器的 TLS 握手是和云厂商的边缘节点完成的，协议版本、套件由云控制台里的 TLS 策略决定，源站上的 Nginx 配置只影响“边缘到源站”这一段。

## TLS 与 HTTP/2、HTTP/3 的关系

各个 HTTP 版本都建立在 TLS 之上，只是用法不同。

| | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | 基于 UDP 的 QUIC |
| 安全层 | TLS 1.2 或 1.3 | TLS 1.2 及以上（浏览器只支持加密的 HTTP/2） | QUIC 内嵌 TLS 1.3 握手 |
| 协商方式 | ALPN `http/1.1` 或不协商 | ALPN `h2` | ALPN `h3`，通过 `Alt-Svc` 头或 HTTPS DNS 记录发现 |
| 数据封装 | TLS 记录 | TLS 记录 | QUIC 包保护，没有 TLS 记录 |
| 新连接发出请求前的往返数 | 2（TLS 1.3） | 2（TLS 1.3） | 1 |

HTTP/1.1 和 HTTP/2 之间的选择就发生在 TLS 握手里：客户端通过 [ALPN](https://ssl.sh/zh/glossary/alpn/) 报出 `h2,http/1.1`，服务器在 EncryptedExtensions 里选定一个。RFC 9113 还要求 HTTP/2 在 TLS 1.2 下禁用一大批弱套件。

HTTP/3 的做法不一样：QUIC 把 TLS 1.3 的握手消息放进自己的 CRYPTO 帧里，用 TLS 派生的密钥保护 QUIC 数据包，传输层握手和加密握手合并成 1 个往返完成。QUIC 离不开 TLS 1.3，所以不存在明文的 HTTP/3。

## TLS 版本一览

公网服务器只应开启 TLS 1.2 和 TLS 1.3。

| 版本 | 年份 | 规范 | 现状 |
|---|---|---|---|
| SSL 2.0 | 1995 | （Netscape） | RFC 6176 禁用 |
| SSL 3.0 | 1996 | RFC 6101（历史文档） | RFC 7568 废弃（POODLE） |
| TLS 1.0 | 1999 | RFC 2246 | RFC 8996 废弃（2021 年 3 月） |
| TLS 1.1 | 2006 | RFC 4346 | RFC 8996 废弃 |
| TLS 1.2 | 2008 | RFC 5246（已被 RFC 9846 取代，要求并入其中） | 仍在使用，配合 ECDHE + AEAD 套件是安全的 |
| TLS 1.3 | 2018 | RFC 8446，2026 年 7 月修订为 RFC 9846 | 当前版本，优先使用 |

主流浏览器在 2020 年前后都移除了 TLS 1.0/1.1。关掉旧版本后如果老客户端连不上，浏览器一般报 [ERR_SSL_VERSION_OR_CIPHER_MISMATCH](https://ssl.sh/zh/errors/err-ssl-version-or-cipher-mismatch/)。

2026 年 7 月 IETF 发布了 RFC 9846，这是 TLS 1.3 的一次小修订：版本号仍是 0x0304，和现有实现向后兼容，主要是把含糊的条文写清楚，并收紧少数要求，例如禁止在不同连接之间复用 key share、禁止协商 TLS 1.0/1.1。它同时取代了 RFC 5246，TLS 1.2 的实现要求也一并写进了这份文档。

另外，国内还有一套基于国密算法的体系：RFC 8998 为 TLS 1.3 定义了 SM4/SM3 套件，政务、金融等场景还会用到国家标准的 TLCP 协议（签名证书 + 加密证书的双证书体系）。这类连接需要支持国密的浏览器或客户端，普通 Chrome 并不支持。

## 自己动手看一看

`openssl s_client` 会在终端里完成一次真实握手，并打印协商结果：

```bash
openssl s_client -connect example.com:443 -servername example.com -brief </dev/null
```

```text
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=example.com
Hash used: SHA256
Signature type: ecdsa_secp256r1_sha256
Verification: OK
Negotiated TLS1.3 group: X25519MLKEM768
DONE
```

这段 OpenSSL 3.6 的输出已经说明了大半：TLS 1.3、AES-256-GCM 套件、ECDSA P-256 证书签名、证书链验证通过、密钥交换用的是后量子混合算法。macOS 自带的 `openssl` 其实是 LibreSSL，想看到上面的字段请用 Homebrew 安装的 OpenSSL 3。`curl -v https://example.com/` 也会打印类似信息。STARTTLS、客户端证书、强制指定版本等更多用法见[连接测试命令手册](https://ssl.sh/zh/cookbook/test-connections/)，想要完整体检可以用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)。

## 常见误区

**“证书负责加密流量。”** 不对。证书私钥只用来给握手签名，流量是用临时密钥交换得到的会话密钥加密的。所以私钥泄露也解不开以前的 TLS 1.3 会话。

**“RSA 密钥越长，加密越强。”** 证书密钥长度影响的是服务器身份认证的强度，和数据加密算法是两回事。截至 2026 年 11 月，2048 位 RSA 或 P-256 ECDSA 证书都够用，数据加密算法另行协商（见[密码套件](https://ssl.sh/zh/learn/cipher-suites/)）。

**“只有登录页需要 HTTPS。”** 没有 TLS，链路上任何一跳都能读 Cookie、往页面里注入脚本。浏览器会把纯 HTTP 标记为“不安全”，HTTPS 页面里的[混合内容](https://ssl.sh/zh/glossary/mixed-content/)也会被拦截。

**“配好一次就一劳永逸。”** 证书会过期，而且最长有效期在不断缩短：2026 年 3 月 15 日起从 398 天降到 200 天，2027 年 3 月 15 日降到 100 天，2029 年 3 月 15 日降到 47 天。用宝塔面板的话可以开启自动续签，自建服务器就要配 ACME 客户端，详见 [47 天证书](https://ssl.sh/zh/learn/47-day-certificates/)和[证书自动续期](https://ssl.sh/zh/learn/automate-certificate-renewal/)。
