# TLS 1.3 详解：相比 1.2 改了什么、怎么开启

> TLS 1.3 删掉了哪些老算法，1-RTT/0-RTT 怎么省时间，证书为何被加密，降级保护与兼容性问题，以及 Nginx、Apache、宝塔、云负载均衡的开启方法。

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

TLS 1.3 是传输层安全协议的当前版本，最初于 2018 年 8 月以 RFC 8446 发布，现行规范是 2026 年 7 月的 RFC 9846。和 TLS 1.2 相比，新连接只要 1 个往返，握手大部分内容（包括服务器证书）都被加密，过去十年引发过攻击的老算法全部删除。截至 2026 年 11 月，主流浏览器、操作系统和 TLS 库都已支持，服务器应开启 TLS 1.3，同时保留加固过的 TLS 1.2 兼容老客户端。

## 和 TLS 1.2 相比改了什么

TLS 1.3 是一次重新设计，删掉的东西比新增的多。

| 方面 | TLS 1.2（RFC 5246，现已被 RFC 9846 取代） | TLS 1.3（RFC 9846，最初为 RFC 8446） |
|---|---|---|
| 完整握手 | 2 个往返 | 1 个往返 |
| 会话恢复 | Session ID 或票据，1 个往返 | PSK 票据，可选 0-RTT |
| 密钥交换 | 静态 RSA、DHE、ECDHE | 只有临时 (EC)DHE 或混合算法，或 PSK + (EC)DHE |
| 前向保密 | 只有 (EC)DHE 套件才有 | 完整握手始终具备 |
| 加密算法 | CBC、RC4、3DES、GCM、ChaCha20…… | 只有 AEAD：AES-GCM、ChaCha20-Poly1305、AES-CCM |
| 密码套件 | 数百种组合 | 5 个 |
| DH 参数 | 服务器可以发任意参数 | 只能用命名组（X25519、P-256、RFC 7919 的 FFDHE……） |
| 握手签名 | 允许 RSA PKCS#1 v1.5、SHA-1、MD5 | RSA-PSS、ECDSA、EdDSA；PKCS#1 v1.5 仅限老式客户端密钥（RFC 9963）；不再用 SHA-1、MD5 |
| 证书 | 明文发送 | 加密发送 |
| 压缩 | 支持（CRIME，2012） | 删除 |
| 重协商 | 支持 | 删除，由 KeyUpdate 和握手后认证代替 |
| ChangeCipherSpec | 真实的协议消息 | 删除（为兼容中间设备保留一个假消息） |
| 密钥派生 | 自定义 PRF | HKDF + 结构化密钥调度 |

删掉的东西都不是摆设：静态 RSA 密钥交换没有前向保密，还招来了 ROBOT（2017）这类 Bleichenbacher 式攻击；CBC 填充招来了 Lucky Thirteen；压缩招来了 CRIME；重协商在 2009 年需要紧急打补丁；任意 DH 参数招来了 Logjam。每一项删除关掉的是一整类漏洞。

2026 年 7 月，IETF 把 TLS 1.3 重新发布为 RFC 9846。这是一次向后兼容的小修订，版本号不变：澄清了含糊的条文，禁止在不同连接之间复用 key share，禁止协商 TLS 1.0 和 1.1，限制 KeyUpdate 消息的数量，新增 `general_error` 告警，并把 "master" secret 的叫法改为 "main"。它同时取代了 RFC 5246，TLS 1.2 的实现要求也写进了同一份文档。

## 更快：1-RTT 与 0-RTT

TLS 1.3 靠“客户端先猜”省掉一个往返。ClientHello 里已经带了客户端估计服务器会接受的 key share，服务器第一次回复就能算出密钥，并把证书、签名和 Finished 一起发回来。客户端发出自己的 Finished 后马上就能发 HTTP 请求，前后只用 1 个往返。

```text
TLS 1.2 完整握手                            TLS 1.3 完整握手
C: ClientHello              --->            C: ClientHello + key_share      --->
S: ServerHello, Certificate,                S: ServerHello + key_share,
   ServerKeyExchange,                          {EncryptedExtensions,
   ServerHelloDone          <---               Certificate, CertificateVerify,
C: ClientKeyExchange,                          Finished}                     <---
   ChangeCipherSpec, Finished --->          C: {Finished} + [GET /]         --->
S: ChangeCipherSpec, Finished <---          S: [200 OK]                     <---
C: [GET /]                  --->
S: [200 OK]                 <---
   = 发请求前 2 个往返                         = 发请求前 1 个往返
```

猜错了服务器就回 HelloRetryRequest，握手变成 2 个往返，也不比 TLS 1.2 差。每条消息的细节见 [TLS 握手详解](https://ssl.sh/zh/learn/tls-handshake/)。

恢复会话时还能更进一步用 0-RTT：客户端用上次会话票据里的预共享密钥，在第一个包里就带上应用数据。代价在安全上：早期数据无法防重放，因为服务器没法确认它是新鲜的；它相对票据密钥也没有前向保密。TLS 1.3 规范要求应用显式开启，RFC 8470 规定了 HTTP 的处理方式：`Early-Data: 1` 请求头和 `425 Too Early` 状态码。大多数服务器默认关闭 0-RTT，Nginx 的 `ssl_early_data` 默认就是 `off`。见 [0-RTT](https://ssl.sh/zh/glossary/0-rtt/)。

## 更隐私：握手被加密

TLS 1.3 中 ServerHello 之后的一切都加密，包括服务器证书链。在 TLS 1.2 下，被动观察者能直接从线路上读到证书，也就知道了 SAN 里的所有域名；TLS 1.3 下它只能看到 ClientHello 和 ServerHello。

剩下的泄露点是 ClientHello 里的 [SNI](https://ssl.sh/zh/glossary/sni/)：服务器得先知道域名才能选证书。加密 ClientHello（[ECH](https://ssl.sh/zh/glossary/ech/)，2026 年 3 月标准化为 RFC 9849）补上了这个缺口：客户端从 HTTPS DNS 记录里取得服务器的 ECH 公钥，把真正的 ClientHello 加密后包在一个只写公共前端域名的外层 ClientHello 里。ECH 只能在 TLS 1.3 上用，这也是升级的理由之一。

## 降级保护

如果攻击者能让双方都以为对方只支持 TLS 1.2，就可能把连接引向更弱的算法。TLS 1.3 有两层防护。

第一层，真实版本在 `supported_versions` 扩展里协商，整个握手又被 Finished 和服务器签名保护。第二层，支持 TLS 1.3 的服务器一旦协商出更低版本，会在 ServerHello random 的最后 8 字节写入标记：

```text
支持 1.3 的服务器协商到 TLS 1.2：   44 4F 57 4E 47 52 44 01   （"DOWNGRD" 01）
协商到 TLS 1.1 或更低：             44 4F 57 4E 47 52 44 00   （"DOWNGRD" 00）
```

server random 在服务器签名范围内，攻击者删不掉这个标记，支持 TLS 1.3 的客户端看到它就中止连接。它取代了早先的 `TLS_FALLBACK_SCSV`（RFC 7507），后者只能防住浏览器自己降版本重试那一种情况。

## 中间设备兼容模式

TLS 1.3 握手被故意伪装成 TLS 1.2 的会话恢复。标准制定期间的实测发现，不少防火墙、代理和流量审计设备会丢弃“看起来不像 TLS 1.2”的连接，所以最终设计里加了几层伪装：

- `ClientHello.legacy_version` 固定写 `0x0303`（TLS 1.2），真实版本列表放在 `supported_versions`；
- 记录层头部也写 `0x0303`，加密记录的外层类型一律是 `application_data`；
- 客户端发送随机的 32 字节 `legacy_session_id`，服务器原样回显；
- 双方各发一条没有意义的 `ChangeCipherSpec`。

所以抓 TLS 1.3 的包会看到“TLS 1.2”的记录头和 ChangeCipherSpec。`openssl s_client -msg` 输出里出现 `<<< TLS 1.2, RecordHeader` 后面跟着 `<<< TLS 1.3, ChangeCipherSpec` 是正常的。国内企业网常见的上网行为管理、SSL 解密网关如果版本太老，正是这类兼容问题的高发区。

## 后量子密钥交换只在 TLS 1.3 上

新的密钥交换方式以命名组的形式加入 TLS 1.3，不需要改密码套件。最重要的是 `X25519MLKEM768`（编号 `0x11EC`），由 X25519 和 NIST 标准化的 ML-KEM-768 组合而成，2026 年 8 月与 SecP256r1MLKEM768、SecP384r1MLKEM1024 一起在 RFC 10024 中标准化。Chrome 131 于 2024 年 11 月默认启用，2025 年 4 月发布的 OpenSSL 3.5 也把它放进了默认 key share。这些混合算法只为 TLS 1.3 定义，停留在 TLS 1.2 的服务器根本无法提供抗量子的密钥交换。详见[后量子 TLS](https://ssl.sh/zh/learn/post-quantum-tls/)。

## 哪些东西不变

开启 TLS 1.3 不需要换证书、换私钥或改证书链。同一张 RSA 或 ECDSA 证书可以同时服务 TLS 1.2 和 TLS 1.3 客户端；OCSP 装订和 SCT 照常工作（改为挂在 Certificate 条目的扩展里）；[HSTS](https://ssl.sh/zh/learn/hsts/) 等 HTTP 层设置不受影响；ALPN 照样协商 `h2` 或 `http/1.1`，HTTP/2 无需改动。

RSA 密钥在 TLS 1.3 下有一条限制：TLS 1.3 服务器的 RSA 密钥必须用 RSA-PSS 签握手，哪怕证书本身是 PKCS#1 v1.5 签发的；最初的 RFC 8446 对客户端也是同样要求。软件密钥对此无感，但一些老的硬件加密模块、智能卡、USB Key 和 TPM 里的客户端密钥只能做 PKCS#1 v1.5 签名。RFC 9963（2026 年 4 月）为这类客户端密钥新增了 `rsa_pkcs1_*_legacy` 编号，服务器可以在 CertificateRequest 里提供。客户端或服务器任一方没实现 RFC 9963 时，这类密钥仍是在 TLS 1.2 下正常、到了 TLS 1.3 就失败，典型表现是升级后基于客户端证书的登录（比如网银、政务系统常见的 U 盾类方案）突然不能用。ECDSA 密钥没有这个问题。

会话恢复的形态也变了：TLS 1.2 的 Session ID 和票据换成了 TLS 1.3 的 PSK 票据。负载均衡后的多台服务器仍要同步票据密钥，而旧的 Session ID 缓存对 TLS 1.3 客户端不再起作用。

## 谁支持 TLS 1.3

当前版本的软件全都支持，问题出在老环境上。

| 软件 | 首个支持正式版 TLS 1.3 的版本 |
|---|---|
| OpenSSL | 1.1.1（2018 年 9 月） |
| Chrome | 70 |
| Firefox | 63 |
| Safari | 12.1 |
| Android | 10 |
| Java | 11（并向后移植到 8u261） |
| Windows（Schannel） | Windows 11、Windows Server 2022 |
| Apache httpd | 2.4.36，需 OpenSSL 1.1.1 |
| Nginx | 1.13.0，需 OpenSSL 1.1.1 |

薄弱环节是 TLS 库早于 2018 年的服务器。CentOS 7、RHEL 7 自带 OpenSSL 1.0.2，国内不少老服务器还跑着它；Windows Server 2019 及更早版本的 Schannel 没有 TLS 1.3，IIS 站点要升级系统。Java、Go 这类自带 TLS 实现的软件不受系统 OpenSSL 影响。

## 怎么开启 TLS 1.3

在较新的环境里，开启 TLS 1.3 只是在协议列表里加一个词。

```nginx
# Nginx（需用 OpenSSL 1.1.1 或更高版本编译）
ssl_protocols TLSv1.2 TLSv1.3;
```

```apache
# Apache httpd 2.4.36+，OpenSSL 1.1.1+
SSLProtocol -all +TLSv1.2 +TLSv1.3
```

先确认库版本：`nginx -V 2>&1 | grep -i openssl` 能看到 Nginx 编译时用的 OpenSSL；Apache 可以用 `httpd -V` 和 `apachectl -M`。然后从外部验证：

```bash
openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief </dev/null
curl -sv --tlsv1.3 -o /dev/null https://example.com/ 2>&1 | grep -E 'SSL connection|TLSv1.3'
```

服务器不支持 TLS 1.3 时，`s_client` 会因协议版本告警而失败。[SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)会列出支持的版本，[套件与协议命令手册](https://ssl.sh/zh/cookbook/ciphers-and-protocols/)里有逐个版本测试的命令。

国内常见环境：

- **宝塔面板**：网站设置的 SSL 页面通常可以勾选 TLS 版本，也可以直接改配置文件里的 `ssl_protocols`。改完用上面的命令从外网验证一次。
- **CentOS 7 自编译 Nginx**：系统 OpenSSL 1.0.2 不能用，编译时用 `--with-openssl=` 指向新版 OpenSSL 源码，或者迁移到更新的发行版。
- **阿里云、腾讯云负载均衡与 CDN**：在 HTTPS 监听或域名配置里选择包含 TLS 1.3 的安全策略。这时源站配置不影响访客，访客只和边缘节点握手。

## 常见部署问题

大多数 TLS 1.3 问题出在 Web 服务器外围，而不是协议本身。

- **对外的不是你的服务器**：CDN、云负载均衡或 WAF 终止 TLS 时，协议由它们的策略决定，源站 Nginx 怎么配访客都看不到。
- **只开 TLS 1.3 会挡掉老客户端**：Mozilla modern 配置列出的最老客户端是 Firefox 63、Chrome 70、Safari 12.1、Android 10、Java 11。更老的安卓手机、旧版 Java 程序、嵌入式设备会报 [ERR_SSL_VERSION_OR_CIPHER_MISMATCH](https://ssl.sh/zh/errors/err-ssl-version-or-cipher-mismatch/) 或笼统的[协议错误](https://ssl.sh/zh/errors/err-ssl-protocol-error/)。
- **按路径要客户端证书**：TLS 1.2 时代常用重协商只对某些路径要求客户端证书。TLS 1.3 没有重协商，HTTP/2 下又不允许握手后认证（RFC 8740），也不是所有客户端都实现了它。应改为整个域名统一要求客户端证书，见[双向 TLS](https://ssl.sh/zh/learn/mtls/)。
- **0-RTT 重放**：打开早期数据却不在应用层做检查，重放的请求就会被执行。
- **流量审计设备**：老旧的 SSL 解密代理可能处理不了 TLS 1.3 或更大的后量子 ClientHello，真正的解决办法是升级设备。

TLS 1.0 和 1.1 应该早就关掉了：RFC 8996 在 2021 年 3 月正式废弃了这两个版本，RFC 9846 进一步禁止协商它们，主流浏览器在 2020 年前后已经移除。截至 2026 年 11 月，TLS 1.2 + 1.3 配合[密码套件](https://ssl.sh/zh/learn/cipher-suites/)一文里的套件列表，是稳妥的默认配置。
