【计算基础|网络06】HTTPS(上):加密体系与 RSA 握手
打开浏览器访问银行、邮箱、支付页面,地址栏那把小绿锁到底在保护什么?为什么我们天天说"别用 HTTP 要用 HTTPS",但真被问到 HTTPS 到底解决了什么问题、握手过程里为什么要发三个随机数、RSA 密钥交换又为什么被 TLS 1.3 踢出去------多数人是说不清楚的。
这一篇不背八股,先把 HTTP 的裸奔问题讲透,再把支撑 HTTPS 的四块"砖头"------对称加密、非对称加密、哈希函数、数字签名------每一块都用 Python 代码跑一遍,让你亲眼看到密文长什么样、签名怎么验。然后顺藤摸瓜讲清楚证书链和 CA 信任体系,最后把 TLS 1.2 的 RSA 握手完整画出来。下篇再讲 ECDHE、TLS 1.3 和性能优化。
环境:macOS + Python 3.14 + OpenSSL 3.5.6,Python 侧用
cryptography库(pip install cryptography)。所有命令均可在本机复现。
一、HTTP 到底哪里不安全
结论先给:HTTP 本身是一张明信片,谁都能看、谁都能改、寄给谁也不核对。它有三个根本性缺陷:
| 缺陷 | 后果 | 典型场景 |
|---|---|---|
| 明文传输 | 路由上任何一跳(运营商、公共 Wi-Fi、公司网关)都能看到全部内容 | 你在咖啡馆 Wi-Fi 登录邮箱,密码被旁边的人嗅探到 |
| 身份不验证 | 你无法确认对面那台服务器真的是 mail.google.com,还是中间人伪造的钓鱼站 |
公共 Wi-Fi 弹个"登录认证"页,其实在偷你的 Cookie |
| 完整性不保护 | 中间人可以在传输过程中篡改内容,你和服务器都发现不了 | 网页里被注入广告 JS、下载链接被换成木马 |
🔴 这三件事不是"加密一下数据"就能同时解决的。光把数据加密,你仍然不知道对面是不是真服务器;光验证身份,内容仍可能被篡改。HTTPS 的设计目标就是同时解决这三个问题,它靠的不是某一个魔法算法,而是把下面四类工具拼起来用。
二、加密体系四大基石
这四个工具各自解决不同问题,缺一个都不行。先放一张总表,后面逐个拆开实测:
| 工具 | 解决什么 | 速度 | 密钥分发 | 典型算法 |
|---|---|---|---|---|
| 对称加密 | 内容机密性(加密后看不懂) | 极快 | 难(双方要先共享密钥) | AES、DES、3DES |
| 非对称加密 | 安全地共享密钥 / 身份认证 | 慢 | 易(公钥随便公开) | RSA、ECC |
| 哈希函数 | 内容完整性(防篡改) | 快 | 无密钥 | MD5、SHA-1、SHA-256 |
| 数字签名 | 身份认证 + 完整性 | 中 | 公钥公开 | RSA-PSS、ECDSA |
2.1 对称加密:快,但密钥怎么送过去是个死结
对称加密的核心思想:加解密用同一把密钥。就像一个带锁的盒子,你和朋友各拿一把相同的钥匙,你放东西进去锁上寄给他,他用钥匙打开。
主流算法演进:DES(56 位,已破)→ 3DES(三倍 DES,慢且即将淘汰)→ AES(现在的事实标准,128/192/256 位密钥)。TLS 1.2 之后基本只用 AES。
⚠️ 对称加密的死结是密钥分发:你和朋友第一次通信,怎么把钥匙安全地交给他?当面给可以,跨网络给------钥匙本身又得加密,于是问题无限套娃。这就是为什么必须有非对称加密。
下面用 Python cryptography 库实测 AES-256-CBC 加解密(CBC 是 TLS 里最常用的分组模式之一):
python
# aes_demo.py
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
key = os.urandom(32) # AES-256:密钥 32 字节 = 256 位
iv = os.urandom(16) # CBC 模式需要 16 字节初始向量
plaintext = "Hello HTTPS! 这是一段需要加密的敏感数据。".encode("utf-8")
# PKCS7 填充:明文长度必须是分组(16字节)的整数倍
padder = padding.PKCS7(128).padder()
padded = padder.update(plaintext) + padder.finalize()
# 加密
enc = Cipher(algorithms.AES(key), modes.CBC(iv)).encryptor()
ciphertext = enc.update(padded) + enc.finalize()
# 解密
dec = Cipher(algorithms.AES(key), modes.CBC(iv)).decryptor()
dec_padded = dec.update(ciphertext) + dec.finalize()
unpadder = padding.PKCS7(128).unpadder()
recovered = unpadder.update(dec_padded) + unpadder.finalize()
print(f"密文(hex): {ciphertext.hex()}")
print(f"解密还原: {recovered}")
print(f"还原一致: {recovered == plaintext}")
实测输出(密钥每次随机,你的输出会和这里不同):
text
密文(hex): 09bdbc85a9ee938de9b7d85f47be6063e2157c1fb57b62221f9a95b45a867384dbe86bc46aefb576b5d71c718a52f2121d0afffcd32b9c040f80c4938ea7db42
解密还原: b'Hello HTTPS! \xe8\xbf\x99\xe6\x98\xaf...'
还原一致: True
⚠️ 三个新手必踩的坑:
- IV(初始向量)必须每次随机,且不需要保密。IV 不是密钥,它的作用是让相同明文每次加密出不同密文。IV 可以明文跟在密文前面发出去。
- CBC 模式本身不防篡改,只保证机密性。TLS 1.2 用 CBC 时还要额外拼 HMAC 做完整性校验;TLS 1.3 直接改用 AES-GCM(认证加密,一把搞定机密性+完整性)。
- AES 极快。在现代 CPU 上有 AES-NI 硬件指令集,加密一 MB 数据是微秒级,这就是为什么大批量应用数据要用对称加密。
2.2 非对称加密:公钥随便公开,私钥自己保管
非对称加密彻底换了个思路:每个人生成一对钥匙------公钥(public key)和私钥(private key)。公钥可以公开给全世界,私钥自己藏好。两种用法:
- 公钥加密、私钥解密:用我的公钥加密,只有我的私钥能解开 → 解决"安全地把密钥送给我"的问题。
- 私钥签名、公钥验证:用我的私钥签个名,任何人拿我的公钥都能验 → 解决"这东西确实是我发的"的问题。
主流算法:RSA (基于大整数分解难题,TLS 1.2 时代主力)和 ECC(椭圆曲线,更短的密钥达到同等安全,TLS 1.3 起全面转向 ECC)。
⚠️ 非对称加密极慢,RSA-2048 加密一次几毫秒,比 AES 慢几个数量级。所以它从不用来加密大块数据,只用来:① 加密一个小的"会话密钥";② 做数字签名。
实测 RSA-2048 公钥加密/私钥解密:
python
# rsa_demo.py
import time
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
# 生成 RSA-2048 密钥对(实际服务器证书里的公钥就是这么来的)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
secret = b"pre_master_secret_123456" # 这就是 TLS 握手里要送的"预主密钥"
# 公钥加密:用服务器公钥把预主密钥加密
t0 = time.time()
enc_data = public_key.encrypt(
secret,
padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None))
print(f"RSA 加密耗时: {(time.time()-t0)*1000:.2f} ms")
print(f"密文长度: {len(enc_data)} 字节 (RSA-2048 固定 256 字节)")
# 私钥解密:只有服务器自己能解
t0 = time.time()
dec_data = private_key.decrypt(
enc_data,
padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None))
print(f"RSA 解密耗时: {(time.time()-t0)*1000:.2f} ms")
print(f"解密还原: {dec_data == secret}")
实测输出:
text
生成 RSA-2048 密钥对耗时: 0.097s
RSA 加密耗时: 0.43 ms
密文长度: 256 字节 (RSA-2048 固定 256 字节)
RSA 解密耗时: 1.75 ms
解密还原: True
🔴 看两个关键数字:RSA 密文固定 256 字节(2048 位 = 256 字节),所以 RSA 一次只能加密很短的数据(OAEP 填充后明文上限大约 190 字节)。这就是为什么 TLS 里 RSA 只用来加密 48 字节的预主密钥,而不是直接加密 HTTP 请求。
2.3 哈希函数:单向、雪崩、定长
哈希函数把任意长度的数据压缩成固定长度的摘要(digest),三个性质:
- 单向性:从摘要无法反推原文。
- 抗碰撞:找两段不同数据算出相同摘要,计算上不可行。
- 雪崩效应:原文改一个比特,摘要翻天覆地。
| 算法 | 摘要长度 | 现状 |
|---|---|---|
| MD5 | 128 位(16 字节) | ❌ 已被破解,可伪造碰撞,禁止用于安全 |
| SHA-1 | 160 位(20 字节) | ❌ 2017 年 Google 已做出实际碰撞,TLS 1.3 移除 |
| SHA-256 | 256 位(32 字节) | ✅ 现在的标准,TLS 1.2/1.3 默认 |
实测:
python
# hash_demo.py
from cryptography.hazmat.primitives import hashes
msg = b"hello https"
for name in ["MD5", "SHA1", "SHA256"]:
h = hashes.Hash(getattr(hashes, name)())
h.update(msg)
print(f"{name:8s} = {h.finalize().hex()}")
# 改一个字母,看雪崩效应
h1 = hashes.Hash(hashes.SHA256()); h1.update(b"hello https")
h2 = hashes.Hash(hashes.SHA256()); h2.update(b"hello httpS") # s 大写
print(f"原文: {h1.finalize().hex()}")
print(f"改后: {h2.finalize().hex()}")
实测输出:
text
MD5 = e9aded2a1b0dd6d497f079b71f504315
SHA1 = 2658f6e013c6d9f56f14310daca008d4018e7166
SHA256 = d151a4b5ca6a480026e716d49117d738dc7eac4b05dbb21123e4ae095c015d3d
原文 hello https : d151a4b5ca6a480026e716d49117d738dc7eac4b05dbb21123e4ae095c015d3d
改后 hello httpS : 7dab81e9ce47c25e90b65c816d6fc1041f88b74529571d667571fae25f9f9eaa
看,只把小写 s 改成大写 S,SHA-256 摘要从 d151... 变成 7dab...,完全不同。哈希不是加密(没有密钥、不能解密),它是"指纹"------你把文档的哈希值和别人对一对,就知道文档有没有被改过。
2.4 数字签名:私钥签名 + 公钥验签,把"身份"和"完整性"焊死
光有哈希只能验证完整性(两份东西是否一致),但谁来对这个哈希负责?数字签名就是解决这个:
- 发送方对文档算哈希,用自己的私钥对这个哈希加密 → 这就是签名。
- 接收方用发送方的公钥解密签名,得到原始哈希;再自己对收到的文档算哈希,两个一对比,一致就说明:① 文档确实由私钥持有者发出(身份);② 文档中途没被改过(完整性)。
实测 RSA-PSS 签名验签:
python
# sign_demo.py
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
doc = b"contract: pay 100 yuan to alice, order=20260922"
# 私钥签名(PSS 是 RSA 签名的现代填充方案)
signature = private_key.sign(
doc,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256())
print(f"签名长度: {len(signature)} 字节")
# 公钥验签:不抛异常即通过
public_key.verify(signature, doc,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256())
print("验签通过")
# 篡改文档再验签,必然失败
doc_tampered = b"contract: pay 10000 yuan to mallory, order=20260922"
try:
public_key.verify(signature, doc_tampered,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256())
except Exception:
print("篡改金额后验签失败(符合预期)")
实测输出:
text
签名长度: 256 字节
验签通过:文档确实由对应私钥签署,且未被篡改
篡改金额后验签失败(符合预期:签名与文档绑定)
🔴 一句话记住这四块砖头怎么配合:
- 对称加密管"快"------加密应用数据。
- 非对称加密管"安全地送密钥"------把对称密钥安全递给对方。
- 哈希管"指纹"------算数据摘要。
- 数字签名管"身份"------用私钥给证书/握手消息盖章。
三、数字证书与 CA 体系:你凭什么信任这个公钥
非对称加密有个新问题:我怎么知道手里的"服务器公钥"真的属于 github.com,而不是中间人自己生成的假公钥?这就引出了证书 和CA。
3.1 证书是什么
证书(X.509 格式)本质上是一张被权威机构背书的公钥"身份证"。一张服务器证书里核心字段:
| 字段 | 含义 | 例子 |
|---|---|---|
| Subject(主体) | 这张证书属于谁 | CN=example.com |
| Subject Alternative Name (SAN) | 覆盖哪些域名 | DNS:example.com, DNS:www.example.com |
| Public Key | 服务器的公钥 | 2048 位 RSA 或 256 位 ECC |
| Issuer(颁发者) | 哪个 CA 签的 | Cloudflare TLS Issuing ECC CA 3 |
| Validity(有效期) | 生效/过期时间 | 2026-07-29 ~ 2026-10-27 |
| Signature | CA 用自己私钥对上述内容的签名 | SHA256 签名 |
🔴 关键点:证书本身不加密任何东西,它只是把"某公钥属于某域名"这件事用 CA 的私钥签了个名。浏览器预装了全球可信 CA 的公钥,所以能验证这个签名。
3.2 证书链:根 CA → 中间 CA → 服务器证书
现实中 CA 不会用根证书直接给每个网站签证书(根私钥泄露后果太大),而是分层:

验证时从叶子往上逐级验签:用中间 CA 的公钥验服务器证书的签名 → 用根 CA 的公钥验中间 CA 的签名 → 根 CA 在浏览器信任列表里,到此为止。
下面用 openssl s_client 实测 example.com 到底发了几张证书、是谁签的:
bash
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null > ex_chain.pem
然后把 PEM 里每张证书拆开看 subject/issuer(macOS 自带 openssl):
bash
# 提取每张证书的主题和颁发者
grep -c "BEGIN CERTIFICATE" ex_chain.pem # 看有几张
实测拿到 4 张证书,逐级解析结果:
text
=== 第1张 服务器证书 ===
subject=CN=example.com
issuer=C=US, O=SSL Corporation, CN=Cloudflare TLS Issuing ECC CA 3
=== 第2张 中间CA ===
subject=C=US, O=SSL Corporation, CN=Cloudflare TLS Issuing ECC CA 3
issuer=C=US, O=SSL Corporation, CN=SSL.com TLS Transit ECC CA R2
=== 第3张 中间CA ===
subject=C=US, O=SSL Corporation, CN=SSL.com TLS Transit ECC CA R2
issuer=C=US, O=SSL Corporation, CN=SSL.com TLS ECC Root CA 2022
=== 第4张 根/中间 ===
subject=C=US, O=SSL Corporation, CN=SSL.com TLS ECC Root CA 2022
看出来了吗?上一张的 issuer 正好是下一张的 subject,一级一级往上推,最后到根 CA。这就是证书链。
3.3 浏览器拿到证书后到底检查什么
浏览器收到服务器证书链后,要做四件事,任何一件不过就弹红字警告:
- 逐级验签:从叶子证书往上,每张都用下一张的公钥验签,最后一级必须能在操作系统/浏览器内置的根 CA 列表里找到。
- 有效期 :当前时间必须在
notBefore和notAfter之间。过期/未生效都不行。 - 域名匹配 :证书的 CN 或 SAN 必须覆盖你访问的域名。访问
paypal.com却拿到一张给paypa1.com的证书,直接拒绝。 - 吊销状态 :检查这张证书是否被 CA 提前吊销(私钥泄露了)。两种方式:
- CRL(Certificate Revocation List):CA 定期发布一个吊销清单,浏览器下载下来查。
- OCSP(Online Certificate Status Protocol):浏览器实时向 CA 发一个查询,问"这张证书现在有效吗"。
⚠️ 你日常看到的证书错误,九成是前三条:证书过期、域名不匹配、证书链不完整(服务器漏发了中间证书)。下一篇"排错"小节会系统讲。
四、RSA 握手全过程(TLS 1.2)
前面四块砖头都备齐了,现在看它们怎么在 TLS 握手里配合。RSA 握手是 TLS 1.2 时代最经典的密钥交换方式,也是面试必画的时序图。
4.1 七个消息,一图看懂

4.2 逐个消息拆解
| 消息 | 谁发 | 关键内容 | 作用 |
|---|---|---|---|
| ClientHello | 客户端 | TLS 版本、客户端随机数(32 字节)、支持的 cipher suites 列表、SNI | 客户端打招呼,报上自己会什么 |
| ServerHello | 服务器 | 选定版本、服务器随机数(32 字节)、选定的 cipher suite | 服务器从列表里挑一个双方都支持的 |
| Certificate | 服务器 | 证书链(服务器证书 + 中间 CA) | 把服务器公钥"身份证"给客户端 |
| ServerHelloDone | 服务器 | 空 | 告诉客户端"我说完了" |
| ClientKeyExchange | 客户端 | 用服务器公钥加密的 PreMasterSecret(48 字节) | 这是握手的核心:安全地把密钥材料送过去 |
| ChangeCipherSpec | 双向 | 一个小通知 | "从下一条消息开始,我改用会话密钥加密了" |
| Finished | 双向 | 对前面所有握手消息的哈希 | 校验握手过程没被篡改 |
🔴 为什么要三个随机数? 这是面试高频题。
- 客户端随机数(Client Random):客户端生成,明文传输。
- 服务器随机数(Server Random):服务器生成,明文传输。
- 预主密钥 (PreMasterSecret):客户端生成,用服务器公钥加密后传输,只有服务器私钥能解。
三者拼在一起,通过 PRF(伪随机函数)推导出主密钥(Master Secret),再派生出客户端/服务器各自的加密密钥、MAC 密钥。
为什么不能只用一个预主密钥?因为前向安全性 和防止重放。三个随机数都参与密钥推导,意味着:
- 每次握手随机数都不同 → 每次会话的密钥都不同,即使同一客户端同一服务器。
- 攻击者录下历史密文,没有当时的随机数组合,就算以后破解了算法也推不出当时的会话密钥(但 RSA 静态密钥交换做不到前向保密,见下节)。
4.3 密钥推导链路

有了这组会话密钥,后面所有 HTTP 请求/响应就用对称加密(AES-GCM 之类)跑,又快又安全。
4.4 用 Python 亲眼看看一次真实握手
下面这段脚本用标准库 ssl 连 github.com,把协商出来的协议版本、密码套件、证书信息全打出来:
python
# ssl_demo.py
import ssl, socket
ctx = ssl.create_default_context()
with socket.create_connection(("github.com", 443), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname="github.com") as ssock:
print(f"协议版本 : {ssock.version()}")
c = ssock.cipher()
print(f"密码套件 : {c[0]}")
print(f"认证算法 : {c[1]}")
print(f"密钥长度 : {c[2]} bit")
cert = ssock.getpeercert()
cn = dict(x[0] for x in cert['subject']).get('commonName')
print(f"证书 CN : {cn}")
print(f"有效期 : {cert['notBefore']} ~ {cert['notAfter']}")
实测输出:
text
协议版本 : TLSv1.3
密码套件 : TLS_AES_128_GCM_SHA256
认证算法 : TLSv1.3
密钥长度 : 128 bit
证书 CN : github.com
有效期 : Sep 1 00:00:00 2026 GMT ~ Nov 29 23:59:59 2026 GMT
注意:现在主流大站默认走的是 TLS 1.3(我们下一篇细讲)。想看 TLS 1.2 的 RSA 握手细节,可以用老站点或者用 openssl s_client -connect 某站:443 -tls1_2 -msg 抓握手报文。
五、RSA 握手的致命缺陷:没有前向保密
RSA 握手虽然经典,但它有一个被 TLS 1.3 彻底抛弃的硬伤------不支持前向保密(Forward Secrecy)。
问题出在哪?在 RSA 握手里,预主密钥是用服务器的长期公钥(来自证书)加密后发出去的。这意味着:
- 攻击者只要把你今天和昨天、和去年所有 HTTPS 通信全录下来(密文),存着。
- 哪天服务器私钥泄露了(被偷、被勒索、被法院调取),攻击者用这把私钥把每段历史握手里的预主密钥都解出来。
- 有了预主密钥 + 当时录下来的两个随机数,就能还原所有历史会话密钥 → 所有历史密文全部可解密。
🔴 这就是"没有前向保密"的含义:长期密钥一旦泄露,过去的通信全部沦陷。
对比一下:
| 场景 | RSA 密钥交换 | 临时(ECDHE)密钥交换 |
|---|---|---|
| 每次会话的密钥 | 由长期公钥加密预主密钥推导 | 每次生成临时密钥对 |
| 长期私钥泄露后 | 历史全部密文可解密 | 历史密文仍不可解密 |
| 握手轮次 | 2-RTT | 2-RTT(TLS 1.3 压到 1-RTT) |
怎么解决?答案就是 Diffie-Hellman 临时密钥交换 ,更准确地说是 ECDHE。它让每次会话都用一对临时生成的密钥,会话一结束就扔掉,即使服务器长期私钥泄露,攻击者也没法回溯解密历史。这正是下一篇要讲的核心。
六、总结:你真正需要记住的 8 件事
- HTTP 有三个死穴:明文传输、不验证身份、不保护完整性。HTTPS 就是来同时解决这三件事的。
- 四大基石各司其职:对称加密快 (AES)、非对称加密慢但安全地送密钥 (RSA/ECC)、哈希是指纹 (SHA-256)、数字签名绑身份和完整性。
- 证书不是加密工具,它是CA 用私钥给"某公钥属于某域名"签的名。浏览器靠内置根 CA 公钥验证这条链。
- 证书链 = 服务器证书 → 中间 CA → 根 CA,浏览器逐级验签 + 查有效期 + 对域名 + 查吊销。
- TLS 1.2 RSA 握手七步:ClientHello → ServerHello → Certificate → ServerHelloDone → ClientKeyExchange → ChangeCipherSpec → Finished。
- 三个随机数(客户端随机、服务器随机、预主密钥)共同经 PRF 推导出会话密钥,保证每次会话密钥不同。
- RSA 握手用服务器长期公钥加密预主密钥 → 没有前向保密:私钥泄露则历史密文全部可解。
- 现代实战中,用 Python
cryptography跑一遍 AES/RSA/哈希/签名,比背十遍概念都管用。
验证清单
- 能用一句话说清 HTTP 明文、身份、完整性三个问题分别对应什么攻击
- 能跑通 AES-256-CBC 加解密,并解释 IV 的作用
- 能跑通 RSA 公钥加密/私钥解密,说出为什么 RSA 不能直接加密大块数据
- 能用 SHA-256 算一段文本的哈希,并演示"改一个字节哈希完全不同"
- 能跑通 RSA-PSS 签名验签,并篡改文档验证验签失败
- 能用
openssl s_client -connect 某站:443 -showcerts拉出证书链,并逐级指出 subject/issuer - 能在纸上画出 TLS 1.2 RSA 握手的 7 个消息和三个随机数的流向
- 能解释"前向保密"为什么在 RSA 握手里不成立
参考资源
- RFC 5246:TLS 1.2 协议 datatracker.ietf.org/doc/html/rf...
- MDN:TLS 协议概述 developer.mozilla.org/zh-CN/docs/...
- MDN:证书与 CA 简介 developer.mozilla.org/zh-CN/docs/...
- Python cryptography 官方文档(对称/非对称/签名)cryptography.io/
- OpenSSL s_client 手册 www.openssl.org/docs/man3.0...
- OWASP Transport Layer Protection 指南 cheatsheetseries.owasp.org/cheatsheets...
下一篇《HTTPS(下):ECDHE 握手与优化》讲:Diffie-Hellman 与 ECDHE 怎么实现前向保密、TLS 1.3 为什么把握手从 2-RTT 压到 1-RTT、Session Ticket / OCSP Stapling / TLS False Start 等性能优化手段,以及证书过期、域名不匹配、证书链不完整这三类最常见的 HTTPS 排错。