HTTPS 与 TLS 握手

面试回答: HTTPS 是 HTTP over TLS。HTTP 的方法、状态码和 Header 等语义基本不变,TLS 负责提供机密性、完整性和服务器身份认证,防止 HTTP 数据在传输中被窃听、篡改或由伪造服务器冒充。

以 TLS 1.3 首次完整握手为例,过程可以按下面六步回答:

  1. 客户端发送 ClientHello:声明支持的 TLS 版本、加密套件、SNI、ALPN,并通过 key_share 携带客户端临时 ECDHE 公开参数。这条消息通常明文发送。
  2. 服务端返回 ServerHello:选择 TLS 版本和加密套件,并返回服务端临时 ECDHE 公开参数。ServerHello 通常也是明文。
  3. 双方派生握手密钥:客户端使用自己的临时私有参数和服务端公开参数计算共享秘密,服务端反向计算出同一个结果;双方再通过 HKDF 派生客户端、服务端两个方向的握手密钥。共享秘密和最终密钥都不会直接在网络中传输。
  4. 服务端证明身份:服务端使用握手密钥加密发送 EncryptedExtensions、证书、CertificateVerify(数字签名)和 Finished。客户端按下面的依赖顺序验证:
    • 证书链和域名校验通过 → 确认叶子证书中的公钥可以代表目标域名;
    • 取得可信公钥 → 用它验证 CertificateVerify,确认当前服务端持有配对私钥,并把服务器身份绑定到本次握手;
    • 身份签名验证通过 → 再验证 Finished,确认服务端掌握本次握手密钥,而且双方看到的握手记录一致。
    这三层验证逐层依赖,全部通过后,客户端才能同时接受服务端身份和当前连接。
  5. 客户端发送 Finished:服务端验证后,确认客户端也掌握正确的握手密钥,并且双方看到的握手记录一致,主握手完成。
  6. 开始传输 HTTP:双方使用 HKDF 派生的应用流量密钥,通过 AES-GCM、ChaCha20-Poly1305 等 AEAD 对称认证加密算法保护 HTTP 请求和响应。

整体分工是:ECDHE 负责协商临时共享秘密,证书和数字签名负责认证服务器身份,HKDF 负责派生不同阶段和方向的密钥,AEAD 负责高效加密业务数据。证书私钥主要用于身份签名,不会逐段加密 HTTP。

记忆链路:ClientHelloServerHello → ECDHE/HKDF → 证书认证 → 双方 Finished → AEAD 加密 HTTP。

1. HTTPS 到底解决什么问题?

假设浏览器准备访问:

https://example.com/

如果使用明文 HTTP,浏览器和服务器之间的运营商网络、公共 Wi-Fi、代理设备等中间节点可能看到甚至修改传输内容。

明文 HTTP 的风险可能发生什么HTTPS 提供的能力
窃听看到 URL、Cookie、请求体和响应内容机密性:让旁观者看不懂内容
篡改修改响应脚本、接口结果或下载内容完整性:发现内容是否被修改
冒充伪装成 example.com 与浏览器通信身份认证:确认连接的是目标服务器

所以可以先记住:

HTTPS = HTTP 语义 + TLS 安全通道

但“安全通道”有明确边界:TLS 保护数据在网络中传输的过程,不能修复服务端漏洞、跨站脚本攻击、弱密码、业务越权,也无法保护已经被控制的用户终端。

2. 握手前先认识几个角色

TLS 会同时使用多种密码学机制。先弄清它们各自负责什么,后面的握手就不会变成一串缩写。

2.1 对称加密:适合保护大量数据

对称加密的特点是:发送方和接收方使用同一把密钥,或者说双方持有相同的密钥材料。

发送方:HTTP 明文 + 对称密钥 -> 密文
接收方:密文 + 同一把对称密钥 -> HTTP 明文

它速度快,适合加密大量请求和响应。TLS 最终使用的 AES-GCM、ChaCha20-Poly1305 都属于对称认证加密算法。

“认证加密”表示它同时完成两件事:

  • 加密数据,让旁观者看不懂;
  • 生成认证标签,让接收方发现密文是否被修改。

对称加密的难题是:浏览器第一次连接 example.com 时,双方还没有共同密钥,不能直接开始加密。

2.2 非对称密码:公钥公开,私钥保密

非对称密码使用一对相关的密钥:

  • 公钥可以公开给任何人;
  • 私钥只能由持有者保存。

在现代 TLS 中,非对称密码主要做两件事:

  1. 数字签名:服务端使用“与服务器叶子证书中公钥配对的服务器私钥”对当前握手上下文签名;客户端从服务器叶子证书中取出“服务器公钥”进行验签。验签成功可以证明当前服务端确实持有与证书公钥配对的私钥,而不是仅仅复制了一张公开证书。服务器私钥始终保存在服务端,不会发送给客户端。
  2. 密钥协商:双方交换公开参数,在不发送最终会话密钥的情况下算出相同秘密。

这里不要把服务器密钥和 CA 密钥混在一起:

CertificateVerify 握手签名:
服务器私钥签名 -> 客户端使用服务器叶子证书中的公钥验签

服务器证书上的 CA 签名:
CA 私钥签发证书 -> 客户端使用上级 CA 证书中的公钥验证证书签名

前者证明“当前服务端持有证书对应的私钥”,后者证明“这张证书确实由对应 CA 签发且内容没有被修改”。客户端还要继续检查证书路径、域名、有效期和用途,才能最终接受服务器身份。

因此,“非对称密码”不等于“用服务器公钥加密所有 HTTP 数据”。现代 TLS 不会让证书私钥逐段处理请求和响应。

为什么不直接用证书公钥加密所有 HTTP 数据?

更准确地说,不是所有非对称算法都能加密;即使某种算法可以,也不适合直接处理大量 HTTP 数据。

先看一种看似可行的设想:

浏览器拿到服务器证书公钥
-> 用服务器公钥加密每一段 HTTP 请求
-> 服务端用证书私钥逐段解密

这套方案至少有以下问题。

1. 证书中的公钥不一定具有“加密”能力

证书可以包含不同类型的公钥。RSA 公钥可以在合适的填充方案下用于加密,也可以用于验签;ECDSA 公钥则是用于验证数字签名的,不能拿来加密 HTTP 数据。

TLS 1.3 不要求证书公钥必须支持加密。它只需要证书密钥能够完成身份签名,因此不能把“证书中有公钥”直接理解为“这个公钥能加密业务数据”。

2. 非对称加密一次只能处理很少的数据

RSA 不能直接加密任意长度的消息。能加密的明文长度小于密钥模数长度,还要为安全填充预留空间。

例如,2048 位 RSA 密钥的模数长度是 256 字节;如果使用带 SHA-256 的 RSA-OAEP 安全填充,一次最多只能加密大约 190 字节。一个 HTTP 请求、JSON 响应、图片或 JavaScript 文件通常远大于这个大小。

理论上可以把数据切成很多小块分别做 RSA 加密,但这会产生复杂的分块、排序和错误处理问题,也会带来很大的密文膨胀,不能替代专门设计的安全记录协议。

3. 非对称运算比对称加密昂贵得多

非对称密码涉及大整数模幂或椭圆曲线运算,计算成本远高于 AES-GCM、ChaCha20-Poly1305 等对称算法。

如果每一小段 HTTP 数据都调用服务器证书私钥:

  • 服务端 CPU 消耗会明显增加;
  • 吞吐量和并发能力会下降;
  • 长期私钥需要参与大量在线运算,扩大私钥相关实现的攻击面;
  • 大响应需要执行大量非对称运算,失去实际可用性。

非对称密码适合处理少量关键材料和身份签名,对称密码才适合持续处理大量网络流量。

4. 服务器公钥只能自然解决“发给服务器”的方向

浏览器用服务器公钥加密请求后,服务器可以用自己的私钥解密。但服务器返回响应时,如果仍用服务器公钥加密,只有拥有服务器私钥的服务端自己能解密,浏览器反而无法读取。

要保护服务端到客户端的方向,还需要客户端提供自己的加密公钥,或者双方先协商一份共同密钥。普通 HTTPS 客户端通常没有用于网站身份认证的客户端证书;TLS 选择后一种方式:双方通过密钥协商得到共享秘密,再派生客户端和服务端两个方向的对称密钥。

5. 只做公钥加密不能自动得到完整的 TLS Record 安全性

安全传输不只是“把明文变成密文”,还需要:

  • 检测密文是否被篡改;
  • 区分客户端和服务端方向;
  • 保证同一连接中的记录顺序;
  • 防止旧记录被原样插回当前连接;
  • 为每条记录正确构造唯一 nonce;
  • 在需要时更新流量密钥。

直接对每块数据做一次公钥加密,并不会自动提供这一整套记录层语义。TLS 使用 AEAD 对称认证加密和记录序号共同实现这些能力。

6. 长期证书私钥泄露可能危及历史流量

如果历史 HTTP 密文一直是直接用服务器长期证书公钥生成的,攻击者可以先保存这些密文。未来一旦获得服务器证书私钥,就可能回头解密过去保存的流量。

TLS 1.3 使用临时 ECDHE 参数协商共享秘密。证书私钥只负责签名当前握手,不负责产生或解密应用数据密钥。因此,仅在未来泄露证书私钥,通常不足以恢复过去已经结束的 ECDHE 会话,这就是前向安全性。

所以现代 TLS 使用的是混合密码设计:

证书私钥 + 数字签名
-> 证明服务器身份

ECDHE 临时密钥协商
-> 得到共享秘密

HKDF 密钥派生
-> 得到客户端和服务端不同方向的流量密钥

AEAD 对称认证加密
-> 高效保护大量 HTTP 请求和响应

一句话记忆:

非对称密码解决“第一次见面时如何认证身份、建立共享秘密”,对称密码解决“建立连接后如何高效、安全地传输大量数据”。

2.3 密钥协商:不传输最终密钥,也能得到相同秘密

TLS 1.3 的完整证书握手通常使用 ECDHE。它是 Ephemeral Elliptic Curve Diffie-Hellman 的缩写,可以理解为“使用临时椭圆曲线参数的密钥协商”。

客户端和服务端各自生成一份临时私有参数,只交换对应的公开参数。然后:

客户端:自己的临时私有参数 + 服务端公开参数 -> 共享秘密
服务端:自己的临时私有参数 + 客户端公开参数 -> 同一个共享秘密

最终的共享秘密没有在网络中传输。网络旁观者即使看到双方公开参数,也无法高效算出该秘密。

这里的 ECDHE 属于非对称密码学中的密钥协商,但不是“公钥加密、私钥解密”。

RSA 和 ECDHE 有什么区别?现在使用哪个?

RSA 和 ECDHE 不是完全相同类型的机制,不能简单理解成两个互相替换的“加密算法”。

  • RSA 是一种非对称密码算法,可以根据具体方案用于数字签名,也可以用于加密少量数据。
  • ECDHE 是一种临时密钥协商机制,只负责让双方得到相同的共享秘密;它本身不负责数字签名,也不能单独证明服务器身份。
对比项RSAECDHE
主要能力数字签名;历史上也用于传递预主密钥协商临时共享秘密
是否直接生成本次共享秘密旧式 RSA 密钥交换中由客户端生成秘密,再用服务器 RSA 公钥加密发送双方各自计算,共享秘密不在网络中传输
使用的密钥通常是证书对应的长期 RSA 公私钥每次握手生成的临时私有参数和公开参数
能否单独认证服务器身份RSA 签名可以参与身份认证,但仍需可信证书不能,必须结合证书签名或预共享密钥
前向安全性静态 RSA 密钥交换没有前向安全性每次正确生成并销毁临时参数时提供前向安全性
TLS 1.3 中的角色可以用于证书体系中的数字签名,也可以用于服务端 CertificateVerify完整证书握手中负责密钥协商

“RSA 用于 TLS”有两种完全不同的含义:

历史上的 RSA 密钥交换:
客户端生成预主密钥
-> 使用服务器 RSA 公钥加密
-> 服务端使用证书 RSA 私钥解密

现代握手中的 RSA 签名:
服务端使用证书 RSA 私钥签名握手上下文
-> 客户端使用叶子证书中的 RSA 公钥验签

前一种方式负责建立密钥,但长期 RSA 私钥未来泄露时,攻击者可能解开过去保存的握手流量,因此 TLS 1.3 已经移除静态 RSA 密钥交换。后一种方式只负责身份认证,在 TLS 1.3 中仍然可以使用。

因此,现代 TLS 1.3 完整证书握手通常是“组合使用”,而不是二选一:

ECDHE
-> 协商本次连接的临时共享秘密

RSA-PSS、ECDSA 或其他数字签名算法
-> 证明服务端持有证书私钥,认证服务器身份

AES-GCM 或 ChaCha20-Poly1305
-> 使用派生出的对称密钥保护 HTTP 数据

例如,服务器完全可以持有 RSA 证书,同时使用 ECDHE 协商密钥:

ECDHE:负责“这次连接用什么共享秘密”
RSA:负责“服务端如何证明自己的身份”

如果使用 ECDSA 证书,则只是把身份签名从 RSA 换成 ECDSA,密钥协商仍然可以使用 ECDHE。会话恢复还可能采用 PSK-only 或 PSK + ECDHE,这属于后面的进阶内容。

所以面试中回答“现在使用哪个”时,可以说:

现代 TLS 1.3 的完整证书握手使用 ECDHE 等临时 Diffie-Hellman 机制协商共享秘密,不再使用静态 RSA 密钥交换;但 RSA 没有完全消失,它仍可以作为证书和握手的数字签名算法。典型组合是 ECDHE 负责密钥协商,RSA 或 ECDSA 负责身份认证,AES-GCM 或 ChaCha20-Poly1305 负责对称加密业务数据。

2.4 证书:把域名身份和公钥绑定起来

密钥协商只能让双方得到秘密,不能单独证明对方就是 example.com。主动攻击者也可以与浏览器协商一套密钥。

服务器证书大致表达:

这个公钥可以代表 example.com
证书有效期是……
证书用途是……
颁发者是某个证书机构

什么是服务器叶子证书?

“叶子证书”不是一种特殊文件格式,而是证书在信任链中的位置。它也叫终端实体证书,是证书链最靠近具体网站、直接代表 example.com 的那一张证书。

可以把证书链画成:

受信任根 CA 证书        <- 预置在操作系统或浏览器中
        |
        | 根 CA 私钥签名
        v
中间 CA 证书            <- 可以继续签发下级证书
        |
        | 中间 CA 私钥签名
        v
example.com 叶子证书    <- 直接代表网站,通常不再签发其他证书

从树形结构看,根 CA 在上方,具体网站证书位于没有下级分支的末端,所以叫“叶子证书”。在 TLS 的 Certificate 消息中,它通常是服务端发送的第一张证书。

它是一个什么样的存在?

叶子证书本质上是一份由 CA 数字签名的结构化公开数据。互联网 HTTPS 常用 X.509 证书标准。服务器配置中常看到 .crt.cer.pem 文件;它们可能采用二进制 DER 编码,也可能采用带有下面外壳的 PEM 文本编码:

-----BEGIN CERTIFICATE-----
经过 Base64 编码的证书数据
-----END CERTIFICATE-----

文件扩展名和外层编码可能不同,但核心都是同一类证书结构。证书不是一段由人自由填写的说明文字,里面包含定义明确的字段,例如:

证书内容表示什么
Subject Alternative Name(SAN,主题备用名称)证书适用于哪些域名,例如 example.comwww.example.com;现代浏览器主要据此匹配域名
Subject证书主体信息;在现代 HTTPS 中不能代替 SAN 完成常规域名匹配
Public Key服务器公钥,客户端随后用它验证 CertificateVerify 握手签名
Issuer哪个 CA 签发了这张证书
Validity证书的生效时间和过期时间
Key Usage / Extended Key Usage这把公钥允许用于什么场景,例如服务器身份认证
Serial NumberCA 为证书分配的序列号
Signature Algorithm / SignatureCA 使用什么签名算法,以及 CA 对证书内容生成的数字签名

可以把一张叶子证书简化成下面这份“经过 CA 盖章的数据”:

适用域名:example.com、www.example.com
服务器公钥:Server Public Key
有效期:2026-01-01 至 2026-04-01
用途:TLS 服务器身份认证
签发者:某中间 CA
CA 签名:对以上证书内容生成的数字签名

叶子证书是公开信息,可以在 TLS 握手中发送给任何客户端。它包含服务器公钥,但绝不包含服务器私钥。实际部署中,证书文件和私钥文件通常分别保存:

example.com.crt / fullchain.pem:叶子证书以及可能附带的中间证书,允许公开
example.com.key:服务器私钥,只能由服务器安全保存

为什么必须有叶子证书?

如果服务端只发送一个裸公钥,浏览器无法知道它属于谁。攻击者完全可以拦截连接,换成自己的公钥,并声称“这就是 example.com 的公钥”。

叶子证书解决的是“公钥属于谁”的问题:

  1. CA 在签发前按照规则验证域名控制权;
  2. CA 把 example.com、服务器公钥、有效期和用途等信息写入证书;
  3. CA 使用自己的私钥对这些证书内容签名;
  4. 浏览器使用颁发者 CA 证书中的公钥验证签名;
  5. 浏览器继续沿证书链向上验证,直到本地已经信任的根 CA;
  6. 浏览器再检查访问域名是否匹配 SAN、证书是否有效;
  7. 最后通过 CertificateVerify 确认当前服务端确实持有与叶子证书中公钥配对的服务器私钥。

因此,叶子证书不是独立完成信任的“万能身份证”。它需要同时满足:

有效证书链
+ 本地信任的根 CA
+ 正确的域名和有效期等检查
+ 服务端对证书私钥的持有证明

CA 是 Certificate Authority,即证书颁发机构。操作系统或浏览器预置信任一些根 CA。服务端通常发送自己的叶子证书和必要的中间 CA 证书,客户端据此建立一条到本地受信任根证书的验证路径;根证书通常不由服务端发送,因为它是否可信必须由客户端本地信任库决定。

证书通常是公开信息。真正需要保密的是证书对应的私钥。

2.5 哈希、HMAC 和数字签名有什么区别?

哈希函数把任意长度的数据转换成固定长度摘要。输入只要发生很小变化,摘要通常就会明显不同。

HMAC 是 Hash-based Message Authentication Code,即基于哈希的消息认证码。它在哈希基础上加入双方共享的秘密密钥,用于确认“消息没有被修改,并且计算者掌握这把共享密钥”。

数字签名则使用非对称密钥:私钥签名,公钥验签。它可以把消息与某个身份私钥绑定起来。

机制是否需要秘密谁能验证TLS 中的主要用途
哈希不需要任何人计算握手消息摘要
HMAC双方共享密钥掌握共享密钥的一方确认握手密钥和握手完整性
数字签名签名者持有私钥持有公钥的任何人证明服务端持有证书私钥

2.6 各种机制在 TLS 中如何分工?

机制分类在 TLS 中负责什么不负责什么
ECDHE非对称密钥协商得到临时共享秘密不直接加密 HTTP
证书和握手签名非对称身份认证建立对服务器身份的信任不逐段加密业务数据
HKDF密钥派生从共享秘密派生多把连接密钥本身不传输、不加密 HTTP
Finished基于共享密钥的 HMAC确认握手密钥和握手记录不代替证书证明域名身份
AEAD对称认证加密加密 HTTP 并检测密文篡改不负责建立服务器身份

HKDF 是 HMAC-based Key Derivation Function,即基于 HMAC 的密钥派生函数。AEAD 是 Authenticated Encryption with Associated Data,即带关联数据的认证加密。现在只需要知道它们的职责,具体工作方式会在对应步骤中解释。

3. TLS 1.3 完整握手鸟瞰图

下面以首次访问 https://example.com/、只认证服务器的 TLS 1.3 连接为例。先假设域名系统 DNS 已经把域名解析成 IP,并且传输控制协议 TCP 已经完成建连。

两个关键分界点:

ClientHello、ServerHello:通常明文
ServerHello 之后:双方已经能派生握手密钥,后续大部分握手消息加密
客户端 Finished 之后:客户端可以发送普通 HTTP 应用数据

“明文”不代表攻击者可以成功篡改。前两条消息稍后会被纳入签名和完整性校验;攻击者修改它们,后续验证就会失败。

4. TLS 1.3 握手逐步拆解

每一步都回答四个问题:发送什么、为什么发送、此时双方拥有什么、消息是否加密。

第 0 步:连接前双方知道什么?

  • 发送什么:还没有发送 TLS 消息。
  • 为什么:先明确握手的起点。
  • 此时拥有:浏览器知道目标域名、目标端口以及本地信任的根证书;服务端拥有站点证书和对应私钥。双方还没有本次连接的共享密钥。
  • 加密状态:TLS 尚未开始。

浏览器还不知道对面是否真是 example.com,也不知道本次连接最终使用哪些算法和密钥。

第 1 步:客户端发送 ClientHello

ClientHello 可以理解为“客户端能力清单和第一次出价”。

  • 发送什么:支持的 TLS 版本、加密套件、临时公开参数和部分扩展。
  • 为什么:服务端需要从双方共同支持的能力中选择本次连接参数。
  • 此时拥有:客户端已经生成临时私有参数,并只发送对应公开参数;双方仍没有握手密钥。
  • 加密状态:普通 TLS 1.3 中通常明文发送。

常见字段第一次出现时可以这样理解:

字段含义为什么需要
supported_versions客户端支持的 TLS 版本让服务端选择版本
cipher_suites客户端支持的加密套件TLS 1.3 中主要决定认证加密算法和哈希算法
supported_groups客户端支持的密钥协商参数组告诉服务端能使用哪些椭圆曲线等参数
key_sharekey exchange share,临时公开参数让服务端可以直接参与本次密钥协商
client_random客户端随机数提供本次握手的随机性和唯一性,并进入握手记录
SNIServer Name Indication,服务器名称指示告诉共享同一 IP 的服务器要访问 example.com,便于选择证书和站点
ALPNApplication-Layer Protocol Negotiation,应用层协议协商提出 HTTP/2、HTTP/1.1 等候选协议

传统 SNI 通常可被网络旁观者看到。如何隐藏它属于后面的进阶内容。

第 2 步:服务端返回 ServerHello

ServerHello 可以理解为“服务端确认核心参数,并给出自己的临时公开参数”。

  • 发送什么:选定的 TLS 版本、加密套件、服务端随机数和 key_share
  • 为什么:只有服务端给出选择和公开参数后,双方才能确定核心算法并计算同一个共享秘密。
  • 此时拥有:双方都拥有自己的临时私有参数和对方的临时公开参数,可以独立计算相同的 ECDHE 共享秘密。
  • 加密状态ServerHello 本身通常明文;处理完它以后,双方才能派生握手密钥。

简化理解:

客户端私有参数 + 服务端 key_share -> 共享秘密
服务端私有参数 + 客户端 key_share -> 同一个共享秘密

服务端最终选择的应用层协议不会放在 ServerHello 中,而是在后续已加密的 EncryptedExtensions 中返回。

第 3 步:双方派生握手密钥

ECDHE 共享秘密只是原始密钥材料,不能直接拿来保护所有消息。TLS 需要把不同阶段、不同方向的密钥隔离开。

  • 发送什么:这一步不发送新的消息,双方在本地计算。
  • 为什么:需要从同一份共享秘密派生客户端和服务端各自方向的握手密钥。
  • 此时拥有:双方可以得到相同的客户端握手密钥和服务端握手密钥。
  • 加密状态:从下一条服务端握手消息开始,使用握手密钥保护。

这里使用前面提到的 HKDF。简化后的过程是:

ECDHE 共享秘密
  -> HKDF + 当前握手消息摘要
  -> 客户端握手流量秘密
  -> 服务端握手流量秘密
  -> 各自方向的握手密钥和 IV

“流量秘密”也叫 traffic secret,是继续派生具体密钥的秘密材料。“IV”是 Initialization Vector,即初始化向量;它会参与构造每条加密记录使用的唯一值。基础阶段只需要记住:traffic secret 还会继续派生出真正交给加密算法使用的密钥和 IV。

为什么要分方向?

密钥谁发送时使用谁接收时使用
客户端握手写密钥客户端加密握手消息服务端解密客户端握手消息
服务端握手写密钥服务端加密握手消息客户端解密服务端握手消息

“写密钥”是从发送方视角命名:把加密记录写入连接时使用。双方都能派生两组密钥,只是发送和接收方向相反。

第 4 步:服务端证明身份并确认握手

服务端接下来通常发送四类消息:

消息通俗理解主要作用
EncryptedExtensions其余协商结果返回最终应用层协议等扩展结果
Certificate服务器身份证明材料发送叶子证书和必要的中间证书,通常不发送根证书
CertificateVerify对当前握手的签名证明服务端持有与叶子证书公钥配对的证书私钥
Finished对当前握手的密钥确认证明服务端掌握握手密钥,且握手记录一致
  • 发送什么:上述协商、证书、签名和确认消息。
  • 为什么:密钥协商本身不能证明对方身份,客户端还要确认“与我协商密钥的人就是 example.com”。
  • 此时拥有:客户端验证通过后,既信任服务器身份,也确认双方掌握相同的握手密钥。
  • 加密状态:这些消息都由服务端握手密钥保护。

证书和 CertificateVerify 是什么关系?

它们共同完成服务器身份认证,但职责不同:

Certificate:告诉客户端“应该信任哪一个服务器公钥”
CertificateVerify:证明“当前服务端确实持有与该公钥配对的私钥”

服务端发送的 Certificate 消息中包含叶子证书和必要的中间证书。叶子证书包含服务器公钥、适用域名、有效期和用途,并带有 CA 的签名。客户端需要它来建立证书链并取得一个经过身份绑定的服务器公钥。

但是,证书本身是公开信息。攻击者可以从任何一次正常连接中复制 example.com 的证书,再原样发送给其他客户端。因此,只收到一张合法证书并不能证明当前连接的对端拥有对应私钥。

服务端接着发送 CertificateVerify

服务端:
与叶子证书公钥配对的服务器私钥
+ 当前握手上下文
-> CertificateVerify 数字签名

客户端:
从叶子证书中取出的服务器公钥
+ 同一份握手上下文
+ CertificateVerify 数字签名
-> 验签

如果攻击者只是复制了服务器证书,却没有服务器私钥,就无法为当前握手生成有效的 CertificateVerify

因此,完整关系是:

CA 对证书的签名
-> 让客户端相信“这张证书中的公钥可以代表 example.com”

CertificateVerify
-> 让客户端相信“当前连接的服务端持有该公钥对应的私钥”

对目标域名和有效期等检查
-> 确认这张证书可以用于本次访问

为什么两条消息都要发送给客户端?

  • 只发送证书:客户端能得到可信公钥,但无法确认当前对端持有对应私钥,因为证书可以被复制;
  • 只发送 CertificateVerify:客户端看到一个签名,却不知道应该用哪个可信公钥验证,也不知道该公钥是否属于 example.com
  • 两者一起发送:证书建立“域名与公钥”的可信绑定,CertificateVerify 再把“私钥持有者”绑定到当前这次握手。

CertificateVerify 这个名字容易让人误以为它负责验证证书链。实际上,证书链由客户端使用 CA 公钥单独验证;CertificateVerify 是服务端生成、客户端验证的一条握手签名消息。

客户端如何验证证书?

客户端通常检查:

  1. 能否根据服务端证书和中间证书建立到本地受信任根证书的有效路径;
  2. 路径中的证书签名是否正确;
  3. 证书中的域名是否匹配 example.com
  4. 证书是否在有效期内,用途和浏览器安全策略是否允许;
  5. CertificateVerify 的握手签名是否正确。

证书路径建立对证书公钥的信任,域名校验确认该证书适用于 example.comCertificateVerify 则证明当前对端确实持有对应私钥。这三件事不能互相替代。

transcript 是什么?

transcript 在这里指“截至当前的 TLS 握手消息记录”,可以把它理解为握手聊天记录。transcript hash 就是这些握手消息的哈希摘要。

ClientHello
+ ServerHello
+ EncryptedExtensions
+ Certificate
+ ...
-> transcript hash

实现通常维护持续更新的摘要,而不必反复保存和拼接全部消息。

CertificateVerifyFinished 有什么区别?

CertificateVerify 使用非对称数字签名:

服务端:证书私钥 + 当前握手上下文 -> 数字签名
客户端:证书公钥 + 当前握手上下文 + 签名 -> 验签

它证明“服务端持有可信证书对应的私钥,并认可当前这次握手”。

Finished 使用从握手秘密派生的 finished_key 对 transcript hash 计算 HMAC:

HMAC(finished_key, transcript hash) -> Finished 验证值

它证明“服务端掌握本次握手密钥,并且双方看到的握手记录一致”。

一句话区分:

CertificateVerify:证明“我是证书私钥的持有者”
Finished:证明“我掌握本次握手密钥,而且握手内容一致”

第 5 步:客户端发送自己的 Finished

  • 发送什么:客户端根据自己的 finished_key 和当前 transcript hash 计算出的验证值。
  • 为什么:服务端也要确认客户端掌握正确的握手密钥,并且看到相同的握手记录。
  • 此时拥有:服务端验证通过后,主握手完成;双方已经确认密钥和握手完整性。
  • 加密状态:使用客户端握手密钥保护。

这里只做服务器身份认证,所以客户端没有发送客户端证书。使用双向 TLS 时,服务端也会要求客户端发送证书和 CertificateVerify

第 6 步:使用应用流量密钥传输 HTTP

双方从截至服务端 Finished 的握手上下文派生第一代应用流量秘密,再得到不同方向的应用数据密钥和 IV。

  • 发送什么:HTTP 请求和响应。
  • 为什么:握手已经建立安全上下文,可以开始传输真正的业务数据。
  • 此时拥有:双方拥有客户端方向和服务端方向的应用数据密钥。
  • 加密状态:HTTP 内容使用 AEAD 对称认证加密。

时序上需要注意:服务端发送自己的 Finished 后就可以发送应用数据,不必等待客户端 Finished;客户端发送自己的 Finished 后才能发送普通应用数据。后文所述的 0-RTT(零往返早期数据)是例外。

在基于 TCP 的 HTTPS 中,HTTP 数据会被封装成 TLS Record,也就是 TLS 记录。记录层把连续字节流划分成可以独立保护的记录:

HTTP 请求或响应
-> TLS Record
-> AEAD 加密 + 认证标签
-> TCP 传输

认证标签由加密算法根据密钥、密文和相关信息计算。接收方只有在标签验证通过后才接受数据;攻击者修改密文通常会导致验证失败。

5. 为什么这套流程能抵抗中间人?

如果只有匿名密钥协商,主动中间人可以分别与客户端、服务端建立两套密钥。HTTPS 的关键不是单独使用 ECDHE,而是把多种机制连接起来:

ECDHE:双方得到共享秘密
证书路径 + 域名校验:建立对服务器公钥的信任
CertificateVerify:把服务器身份绑定到本次握手
Finished:确认握手密钥和完整握手记录
AEAD:保护之后的 HTTP 数据

客户端和服务端的 key_share 等关键参数都会进入 transcript。中间人如果替换公开参数,就必须伪造可信服务器的握手签名和正确的 Finished,否则客户端会终止连接。

到这里已经足以应对普通前端面试中的 HTTPS 主流程。后面的内容用于回答网络、安全或高级后端岗位的深入追问。

6. 进阶理解

6.1 ECDHE 为什么能算出同一个秘密?

用椭圆曲线形式简化表示:

公开基点:G
客户端临时私有参数:a,公开参数:aG
服务端临时私有参数:b,公开参数:bG

客户端计算:a(bG) = abG
服务端计算:b(aG) = baG

因为 abG = baG,双方得到同一个共享点。旁观者只能看到 GaGbG,无法高效推出 abG

ECDHE 中的 E 表示 Ephemeral,即临时。每次完整握手生成新的临时参数,会话结束后不再需要这些临时私有参数。

这带来前向安全性:即使服务器证书私钥未来泄露,攻击者通常也不能仅凭过去保存的握手消息和密文恢复当时的 ECDHE 共享秘密。

6.2 HKDF 为什么还要分 Extract 和 Expand?

ECDHE 结果只是原始密钥材料。TLS 1.3 使用 HKDF 建立一棵密钥派生链:

动作输入输出作用
HKDF-Extract原始秘密、salt中间 secret整理原始密钥材料
HKDF-Expand中间 secret、标签、上下文、长度特定用途 secret根据阶段和方向派生彼此隔离的结果

简化后的两条主线是:

ECDHE 共享秘密
-> handshake secret
-> client/server handshake traffic secret
-> 握手密钥和 IV

handshake secret
-> master secret
-> client/server application traffic secret
-> 应用数据密钥和 IV

派生时加入标签和相应阶段的 transcript hash,可以实现阶段隔离、方向隔离,并把密钥绑定到当前握手上下文。

6.3 TLS Record 如何防止篡改和连接内重放?

TLS 1.3 的记录层会从 traffic secret 派生写密钥和 IV,并结合记录序号为每条记录构造 nonce。nonce 是 number used once,即单次使用的数值;同一密钥下不能错误地重复使用相同 nonce。

AEAD 根据密钥、nonce、明文和关联数据生成密文及认证标签。接收方维护自己的预期记录序号,构造对应 nonce 并验证标签。

因此,攻击者修改密文、修改被认证的信息,或者在同一连接中原样插入旧记录,通常都会导致验证失败。记录序号由连接两端维护,不需要作为独立字段直接发送。

6.4 TLS 1.2 与 TLS 1.3 有什么区别?

经典资料常把 HTTPS 描述成:客户端生成预主密钥(Pre-Master Secret),用服务器 RSA 公钥加密发送,服务端再用 RSA 私钥解密。这是 TLS 1.2 及更早版本允许的静态 RSA 密钥交换,不是 TLS 1.3 的流程。

TLS 1.2 也可以使用 ECDHE。此时证书私钥主要签名临时密钥参数,而不是解密客户端发送的预主密钥。

RTT 是 Round-Trip Time,即一次网络往返时间。下面的 RTT 只统计 TLS 握手,不包含 DNS 查询和 TCP 建连。

对比项TLS 1.2TLS 1.3
完整握手往返通常 2-RTT通常 1-RTT
密钥交换可以使用静态 RSA,也可以使用 ECDHE移除静态 RSA 和静态 Diffie-Hellman(DH),完整证书握手使用临时密钥协商
加密套件同时组合密钥交换、签名、对称加密和摘要主要选择 AEAD 和 HKDF 使用的哈希算法
握手加密较多握手消息明文ServerHello 后的大部分握手消息加密
会话恢复会话标识或会话票据(Session ID / Ticket)基于 PSK,可选择 0-RTT

TLS 1.2 的套件名通常较长:

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • ECDHE:密钥协商;
  • RSA:服务端使用 RSA 证书私钥进行签名认证;
  • AES_128_GCM:对称认证加密;
  • SHA256:参与密钥派生和握手校验的哈希算法。

TLS 1.3 把密钥协商组和签名算法放到其他扩展中单独协商,所以套件名更短:

TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256

6.5 会话恢复、PSK 和 0-RTT

PSK 是 Pre-Shared Key,即预共享密钥。首次完整握手后,服务端可以提供会话恢复信息;客户端下次连接时基于之前建立的 PSK 减少握手成本。

恢复方式特点
PSK-only不做新的 ECDHE,省去相关计算,但不为这次连接提供新的前向安全性
PSK + ECDHE在恢复握手中加入新的临时密钥协商,让本次连接获得新的前向安全性

0-RTT 表示客户端可以在尚未收到本次 ServerHello 时,就在第一个发送批次中携带早期应用数据。它有两个关键弱点:

  1. 早期数据只由 PSK 派生的密钥保护,不具备前向安全性;
  2. 攻击者可能在另一条连接中重放早期数据,TLS 层无法为应用保证绝对的“只执行一次”。

支付、创建订单和修改状态等不能安全重试的操作,不应在缺少额外防重放设计时使用 0-RTT。

6.6 HelloRetryRequest 为什么会增加一次往返?

客户端提供的 key_share 不一定是服务端希望使用的参数组。服务端可以发送 HelloRetryRequest,要求客户端使用指定组重新发送 ClientHello 和新的 key_share

因此,“TLS 1.3 完整握手通常是 1-RTT”不是绝对规则。重试消息也会以规定方式进入 transcript,不能被攻击者随意删除或修改。

6.7 HTTPS 保护什么,又暴露什么?

HTTPS 通常保护:

  • HTTP 方法、路径和查询参数;
  • 请求头、请求体、响应头和响应体;
  • Cookie 在传输链路中的内容;
  • HTTP 内容是否在链路中被修改。

但它不能隐藏所有网络元数据:

信息是否通常可见
目标 IP、端口、连接时间和流量大小网络路径上仍可能可见
DNS 查询域名传统明文 DNS 可见
SNI 中的服务器名称传统 SNI 可见

DoH 和 DoT 分别是 DNS over HTTPS 与 DNS over TLS,可以保护 DNS 查询链路。ECH 是 Encrypted Client Hello,即加密客户端问候,它尝试加密 ClientHello 中包括真实服务器名称在内的敏感内容。它们解决的是额外的元数据隐私问题,不改变 HTTPS 保护 HTTP 内容的核心结论。

6.8 HSTS 解决什么问题?

TLS 只能保护已经开始建立的 HTTPS 连接。如果用户最初访问 http://example.com,攻击者可能拦截 HTTP 跳转,让用户一直停留在明文 HTTP,这通常称为 SSL stripping。SSL 是 TLS 的历史前身,这个术语可以理解为“HTTPS 降级剥离”。

HSTS 是 HTTP Strict Transport Security,即 HTTP 严格传输安全。它让浏览器在发出网络请求前把 HTTP URL 升级成 HTTPS。HSTS preload 预加载列表还能减少首次访问尚未收到 HSTS 响应头时的暴露窗口。

HSTS 不是 TLS 握手的一部分,也不能忽略或修复证书错误。

6.9 HTTP/3 有什么不同?

HTTP/1.1 和 HTTP/2 通常在 TCP 上使用 TLS Record。HTTP/3 运行在 QUIC 上;QUIC 是一个基于 User Datagram Protocol(UDP,用户数据报协议)构建的安全传输协议,并把 TLS 1.3 握手集成进自己的连接建立过程。

HTTP/3 仍使用 TLS 1.3 的认证和密钥协商思想,但应用数据由 QUIC 的包保护机制承载,不再套用基于 TCP 的 TLS Record 层描述。

7. 高频面试追问

7.1 HTTPS 和 HTTP 是完全不同的应用层语义吗?

不是。HTTPS 主要是在 HTTP 外增加 TLS 安全通道,HTTP 方法、状态码、Header 和消息语义基本不变。

面试官为什么问: 判断是否只会说“HTTPS 比 HTTP 安全”。

7.2 为什么不能一开始就使用对称加密?

因为第一次连接时双方还没有共同密钥。TLS 先通过密钥协商得到共享秘密,再派生对称密钥。

面试官为什么问: 判断是否理解握手存在的根本原因。

7.3 为什么不用证书公钥加密所有 HTTP?

非对称密码计算成本高,不适合大量数据;现代 TLS 还需要认证加密和前向安全性。证书私钥主要用于身份签名,ECDHE 建立共享秘密,AEAD 才负责业务数据。

面试官为什么问: 判断是否理解非对称密码和对称密码的分工。

7.4 只收到一张服务器证书,为什么还不能立刻相信对方?

公开证书可以被复制。客户端还要验证证书路径、域名、有效期和用途,并通过 CertificateVerify 确认对端持有证书私钥。

面试官为什么问: 判断是否把“拿到公钥”误认为“建立身份信任”。

7.5 CertificateVerifyFinished 是否重复?

不重复。前者用证书私钥签名,证明身份私钥持有并绑定当前握手;后者使用握手密钥计算 HMAC,确认本次密钥和 transcript。

面试官为什么问: 判断是否理解身份认证与密钥确认的区别。

7.6 中间人为什么不能替换双方的 key_share

因为 key_share 会进入 transcript。攻击者若替换它,就需要同时伪造可信服务器的 CertificateVerify 和正确的 Finished,否则验证失败。

面试官为什么问: 判断能否从主动攻击者视角解释 TLS 安全性。

7.7 抓包工具为什么能看到 HTTPS 明文?

常见原因是用户或系统信任了抓包工具生成的根证书。工具分别与浏览器和真实服务器建立两条 TLS 连接,相当于一个被本机主动信任的中间人。

面试官为什么问: 判断是否理解客户端信任库决定了谁可以代表目标域名。

7.8 TLS 1.3 是否一定是 1-RTT、一定使用 ECDHE?

不一定。HelloRetryRequest 会增加一次往返;PSK-only 恢复握手没有新的 ECDHE。

面试官为什么问: 判断是否把常见流程说成绝对规则。

7.9 为什么不能随意开启 0-RTT?

因为早期数据不具备前向安全性,并且可能跨连接重放。只能发送应用明确允许被重试的数据。

面试官为什么问: 判断是否理解性能优化的安全代价。

7.10 证书更新后部分用户仍报错,如何排查?

检查目标域名、证书有效期、完整中间证书链、SNI 下返回的实际证书、各服务节点是否一致、客户端时间和信任库,以及本地代理是否替换证书。

可以使用:

openssl s_client -connect example.com:443 -servername example.com -showcerts

面试官为什么问: 判断能否把协议知识用于真实工程故障。

8. 常见错误回答

容易被追问击穿的说法问题在哪里更准确的表达
HTTPS 使用 RSA 加密 HTTP 数据混淆历史 RSA 密钥交换、身份认证和数据加密现代 TLS 使用证书私钥签名认证、ECDHE 协商秘密、AEAD 保护 HTTP
服务端发送公钥后客户端就能信任它任意攻击者都能发送自己的公钥客户端还要验证证书路径、域名和握手签名
CA 签过的证书一定可信忽略信任根、域名、有效期、用途和本地策略CA 签名只是完整证书验证的一部分
ECDHE 自己可以防止中间人攻击匿名密钥协商仍会遭受主动中间人攻击ECDHE 必须与证书认证、握手签名和 Finished 结合
TLS 握手全部结束后才开始加密TLS 1.3 在 ServerHello 后已经能派生握手密钥ServerHello 后的大部分握手消息已经加密
TLS 1.3 一定是 1-RTT忽略重试情况和 RTT 统计范围完整握手通常为 1-RTT,HelloRetryRequest 会增加往返
0-RTT 表示完全没有握手客户端只是提前发送早期数据后续握手仍然需要完成,早期数据还有重放风险
有 HTTPS 就不会被攻击把传输安全当成完整的网站安全HTTPS 不能替代鉴权、输入校验和应用安全防护

9. 面试时如何组织答案?

1 分钟版本:不依赖密码学术语

HTTPS 是使用 TLS 保护的 HTTP,主要解决明文传输容易被窃听、篡改和冒充的问题。

建立连接时,双方先协商通信能力,再通过公开参数各自算出相同的临时秘密;
客户端验证服务器证书和握手证明,确认对方身份;
最后双方从临时秘密派生连接密钥,用高效的对称加密保护 HTTP 请求和响应。

HTTPS 只保证传输通道安全,不能解决服务端漏洞、跨站脚本攻击或业务越权。

3 分钟版本:加入标准 TLS 术语

在 1 分钟版本上补充:

  1. 客户端发送 ClientHello,服务端返回 ServerHello,确定版本、加密套件和 ECDHE key_share
  2. 双方通过 ECDHE 得到共享秘密,再用 HKDF 派生分阶段、分方向的握手密钥;
  3. 服务端发送证书、CertificateVerifyFinished,客户端验证证书路径、域名、私钥持有证明和 transcript;
  4. 双方切换到应用流量密钥,用 AEAD 保护 HTTP 数据;
  5. 证书私钥用于身份签名,不直接加密业务数据,ECDHE 还提供前向安全性。

10 分钟版本:根据岗位方向展开

  • 偏网络:RTT、Record、ALPN、HTTP/2、HTTP/3 与 QUIC;
  • 偏安全:证书路径、中间人、前向安全性、0-RTT 重放和 HSTS;
  • 偏后端运维:SNI、多证书部署、中间证书链、会话恢复和证书轮换;
  • 偏密码学:ECDHE 数学性质、HKDF 密钥计划、transcript、AEAD nonce 和密钥更新。

不要把所有术语一次说完。先给完整主线,再根据面试官追问展开对应分支。

10. 术语速查

术语表用于复习,首次学习仍应结合前面的握手上下文理解。

术语含义一句话作用
TLSTransport Layer Security为 HTTP 等应用协议建立安全通道
对称加密双方使用相同密钥材料高效保护大量 HTTP 数据
非对称密码使用公钥和私钥完成签名认证和密钥协商
RSA / ECDSA常见非对称签名算法让服务端使用证书私钥生成握手签名
AES-GCM / ChaCha20-Poly1305常见 AEAD 算法对称加密并认证握手或 HTTP 数据
CACertificate Authority签发证书并参与建立信任路径
ECDHE临时椭圆曲线 Diffie-Hellman不传输最终密钥而协商共享秘密
key_share密钥协商公开参数让对方参与本次 ECDHE 计算
SNI服务器名称指示告诉服务端客户端要访问哪个域名
ALPN应用层协议协商选择 HTTP/2、HTTP/1.1 等协议
transcript握手消息记录将身份、密钥和整个握手上下文绑定
transcript hash握手记录的哈希摘要用于签名、密钥派生和 Finished
HKDF基于 HMAC 的密钥派生函数从共享秘密派生多阶段、多方向密钥
traffic secret流量秘密继续派生具体密钥和 IV 的秘密材料
HMAC基于哈希的消息认证码用共享密钥验证消息完整性和密钥持有
CertificateVerify私钥持有证明消息服务端用自己持有的、与叶子证书公钥配对的证书私钥签名当前握手上下文
Finished握手完成确认消息确认握手密钥和 transcript
AEAD带关联数据的认证加密同时加密数据并检测篡改
TLS RecordTLS 记录在基于 TCP 的 TLS 中承载加密消息
IV / nonce初始化向量 / 单次使用数值保证同一密钥下每条记录使用不同输入
PSKPre-Shared Key用预共享密钥恢复之前的会话
RTTRound-Trip Time一次网络往返时间
0-RTT零往返早期数据在收到服务端本次响应前提前发送数据
HSTSHTTP 严格传输安全强制浏览器从 HTTPS 开始访问
ECHEncrypted Client Hello尝试隐藏客户端问候中的敏感扩展

11. 总结

  • HTTPS 是使用 TLS 保护的 HTTP,提供机密性、完整性和服务器身份认证。
  • TLS 1.3 主线是:协商参数、ECDHE 得到共享秘密、HKDF 派生密钥、证书认证、双方 Finished、AEAD 加密 HTTP。
  • ClientHelloServerHello 通常明文;处理完 ServerHello 后,后续大部分握手消息开始加密。
  • 证书路径和域名校验建立身份信任,CertificateVerify 证明对端持有证书私钥,Finished 确认握手密钥和 transcript。
  • 非对称密码负责密钥协商和身份签名,对称密码负责高效保护业务数据,HKDF 负责连接两个阶段。
  • ECDHE 提供前向安全性;PSK-only 不提供新的前向安全性;0-RTT 还存在跨连接重放风险。
  • HTTPS 保护传输通道,但不能替代应用鉴权、输入校验和其他安全防护。