计算机网络是CS的核心课,但是这个领域内容很多,只凭课程难以面面俱到。
为了避免只学到一些八股一样的框架,很多地方都得自己去研究研究。
Tufts的计算机网络的最后一个项目是HTTPS相关的,实现的时候用了OpenSSL的库,导致很多细节都被封装的方法隐藏了。
比如,TLS 握手到底是怎么运作的。
我的疑问还是很多。所以得细致地捋一下。
首先,为什么 TLS 会存在,还需要证书之类的麻烦事?
为什么不直接双向对称加密?
- 只要中间人截到一个密钥,双方的通讯就变成纯明文了
为什么不直接双向非对称加密?
- 服务器公钥如果被劫持,可以随便解密
- 每次都非对称加密,很慢
那直接非对称加密交换密钥,再对称加密通讯呢?
- 如果有中间人向 client 伪装成 server, 向 server 伪装成 client,拿自己伪造的公钥私钥加解密双方的通讯,双方都觉察不出来。
所以 TLS 找上了 CA 打配合
(这个CA可不是全战的开发商Creative Assembly哦)
TLS 做了两件事:
- 握手(Handshake):协商加密算法,交换密钥,验证身份
- 加密通信(Record Protocol):把 HTTP 数据加密后传输
浏览器看到 https:// 就知道:先 TCP 三次握手,再 TLS 握手,最后才发 HTTP 请求。
基本概念
-
明文(Plaintext):未加密的数据,如
Hello World -
密文(Ciphertext):加密后的数据,如
a8f3c2d1... -
对称加密:用同一个密钥加解密(如 AES)
-
非对称加密:公钥加密,私钥解密(如 RSA、ECDHE)
-
证书(Certificate):包含网站公钥和身份信息,由 CA 签发。现代浏览器都内置了一些Cert。
-
CA (Certificate Authority):证书颁发机构。
-
非对称加密安全但慢,用来交换密钥
-
对称加密快但需要共享密钥,用来加密数据
-
TLS 握手用非对称加密协商对称密钥,后续通信用对称加密
TLS 1.3 握手流程
- ClientHello:包含支持的 TLS 版本、cipher suites、扩展(SNI、ALPN、supported_groups、key_share),以及随机数
client_random。 - ServerHello:服务器选择 cipher suite,返回
server_random和对应的key_share。从此刻开始双方已经拥有 EC(DH) 密钥材料,可派生handshake_secret。 - EncryptedExtensions:服务器发送握手后续信息,包括 ALPN、早期数据许可等。
- Certificate:服务器提供证书链(包含中间证书);如果开启 mutual TLS,还会发送
CertificateRequest。 - CertificateVerify:对握手 transcript 做签名,证明服务器持有私钥。
- Finished:服务器派生
finished_key,对前面所有握手消息做 HMAC,完成后改用application_data密钥加密。 - Client Finished:客户端验证服务器
Finished,再发送自己的Finished。
自此握手结束,HTTP 数据通过 record 层发送。与 TLS 1.2 相比,1.3 把握手轮次减少到 1-RTT,并移除大量过时算法(RSA key exchange、CBC 等)。
数字签名的制作过程:
-
CA机构拥有非对称加密的私钥和公钥。
-
CA机构对证书明文数据T进行hash。
-
对hash后的值用私钥加密,得到数字签名S。
-
明文和数字签名共同组成了数字证书,这样一份数字证书就可以颁发给网站了。
-
那浏览器拿到服务器传来的数字证书后,如何验证它是不是真的?(有没有被篡改、掉包)
浏览器验证过程:
-
拿到证书,得到明文T,签名S。
-
用CA机构的公钥对S解密(由于是浏览器信任的机构,所以浏览器保有它的公钥。详情见下文),得到S’。
-
用证书里指明的hash算法对明文T进行hash得到T’。
-
显然通过以上步骤,T’应当等于S‘,除非明文或签名被篡改。所以此时比较S’是否等于T’,等于则表明证书可信。
所以,浏览器只要信任CA机构,就能信任CA机构签发的证书,这就能保证没有什么乱七八糟的人偷偷伪造证书来冒充网站啦!
密钥派生
TLS 1.3 使用 HKDF:
early_secret = HKDF-Extract(0, PSK)
handshake_secret = HKDF-Extract(DH, early_secret)
master_secret = HKDF-Extract(0, handshake_secret)
然后根据不同阶段派生 client_handshake_traffic_secret、server_handshake_traffic_secret、client_application_traffic_secret 等。每个 secret 又通过 HKDF-Expand 生成具体的 AEAD 密钥与 IV,通常使用 AES-GCM 或 CHACHA20-POLY1305。密钥派生依赖 transcript hash,确保任何握手消息被篡改都会导致验证失败。
证书与信任链
- 服务器证书包含公钥、有效期、Subject、Subject Alternative Name (SAN)、签名算法等。
- 浏览器根据内置根证书(Root CA)验证链条:叶子证书由中间 CA 签发,中间由根 CA 签发。验证过程包括:
- 检查域名是否在 SAN 列表。
- 验证时间在有效期内。
- 使用签发者公钥验证签名。
- 校验 revocation(CRL 或 OCSP)。
证书链一般是这样,逐级向上检查,直到Root CA:
Leaf/Server Cert ← Intermediate CA ← Root CA
SNI 与多域托管
Server Name Indication 扩展允许客户端在握手初始就告知目标域名,以便服务器选择匹配证书,为什么需要这个?因为一个 IP 可能托管很多域名,不这么做,不知道给你啥证书。
部分老旧客户端不支持 SNI,会导致必须用独立 IP 或 fallback 证书。
SNI 有个问题是, 外人能看见你访问啥域名,所以 TLS 1.3 进一步支持 Encrypted Client Hello (ECH),将 SNI 加密以保护隐私。
ALPN 与 HTTP/2
TLS 握手建立后,才开始传 HTTP。那服务器支持不支持 HTTP 2, 浏览器怎么知道?
Application-Layer Protocol Negotiation 让客户端声明支持的上层协议(如 h2, http/1.1),服务器在 EncryptedExtensions 中选择。
没有 ALPN 时只能默认 HTTP/1.1。实现一个支持 HTTP/2 的网关时务必在握手里开启 ALPN,否则浏览器只会退回 HTTP/1.1。
会话复用与 0-RTT
TLS 握手还是很昂贵的。所以服务器在握手完了会给 client 一个 Session Resumption (ID/Session Ticket)。ticket 包含对称密钥加密的 session 参数, client 拿这 ticket 就能后续连接时,跳过一些昂贵的步骤。如果没过期的话。
TLS 1.3的 0-RTT
一般 TLS 需要两三个 RTT,为了加速, TLS 1.3 支持了 0-RTT,允许客户端在 ClientHello 中附带早期数据。
这个的缺点是重放风险,因此服务端一般只给GET用,如果是POST就必须确保可重放操作是幂等的,否则禁用 0-RTT。
TLS 记录层
每条记录格式:ContentType | Version | Length | EncryptedPayload。payload 内部包含:
nonce = IV XOR seq_num
ciphertext = AEAD(plaintext, nonce, aad)
aad 包含 record 头部,确保加密数据在传输过程中无法被重排。TLS 1.3 使用隐式序列号,减少报文尺寸。
双向 TLS(mTLS)
- 服务端发送
CertificateRequest,列出可接受的 CA。 - 客户端回传自己的证书链,并用私钥对握手 transcript 签名。
- 常用于服务间通信、零信任接入。Nginx/Envoy 可通过
ssl_verify_client on或require_client_certificate开启。
HTTPS 栈中的关键配置
- TLS 版本策略:关闭 1.0/1.1,优先 1.3 -> 1.2。
- Cipher Suites:推荐
TLS_AES_128_GCM_SHA256、TLS_AES_256_GCM_SHA384、TLS_CHACHA20_POLY1305_SHA256。 - 证书管理:使用自动化续期,部署 HSTS(
Strict-Transport-Securityheader)防止降级。 - OCSP Stapling:服务器在握手时附带 OCSP 响应,减少浏览器对 CA 的查询延迟。
- Session Cache:在负载均衡层配置共享缓存或使用 Session Tickets,减少全量握手带来的 CPU 压力。
硬件与性能
- AES-NI/ARMv8 Crypto Extensions:现代 CPU 支持 AES 指令,可以让 GCM 性能提升数量级。
- TLS Termination in Load Balancer:将 TLS 终止放在 ALB/NLB、Envoy、Nginx 上,再把明文 HTTP 代理到后端或再启用内部 TLS。
- SSL Offload 卡:少数高吞吐银行会使用硬件加速卡(例如 Cavium、Mellanox),在万兆场景下极大降低 CPU 占用。
调试工具
openssl s_client -connect example.com:443 -tls1_3 -servername example.com查看协商结果。nmap --script ssl-enum-ciphers扫描服务端支持的 cipher suite。Wireshark捕获握手,结合ssl.keylogfile在浏览器里导出密钥,可解密应用层流量(用于测试)。sslyze,testssl.sh做合规性检测,确保未启用弱算法。
常见问题排查
- 证书链缺失:若只部署叶子证书,部分客户端会报
unable to get local issuer certificate。需要将 fullchain.pem 上传。 - 时间不同步:证书验证依赖系统时钟,NTP 失效会导致 TLS 握手失败。
- SNI 错配:CDN 或反向代理没转发 SNI,导致上游发回默认证书;开启
proxy_ssl_server_name on解决。 - Cipher 不匹配:老旧客户端如 Java 6 不支持现代 cipher,需要在服务端额外启用
TLS_RSA_WITH_AES_128_CBC_SHA等兼容套件,但要评估风险。
结语
HTTPS/TLS 设计看似复杂,但本质是一套严格定义的状态机:通过握手协商算法和密钥,再用记录层提供认证与加密。掌握每一步的输入输出,你就能根据抓包迅速定位问题、优化性能、设计安全策略。无论是开发 API 网关、移动 SDK 还是 IoT 终端,只要建立在对 TLS 机制的深入理解之上,就能更自信地保障数据安全。