[{"data":1,"prerenderedAt":1127},["ShallowReactive",2],{"blog-https-tls-handshake-guide":3},{"id":4,"title":5,"body":6,"category":1112,"date":1113,"description":16,"extension":1114,"meta":1115,"navigation":1116,"path":1117,"seo":1118,"series":1119,"seriesOrder":1119,"stem":1120,"tags":1121,"__hash__":1126},"blog\u002Fblog\u002Fhttps-tls-handshake-guide.md","HTTPS 请求全链路复盘：从 AES、ECDHE 到 TLS 1.3",{"type":7,"value":8,"toc":1101},"minimark",[9,13,17,20,28,33,36,39,42,45,48,116,119,126,130,133,184,187,196,199,202,231,234,237,242,246,249,367,370,373,376,381,385,388,391,451,462,477,484,491,500,516,533,541,545,548,554,557,614,627,647,659,662,669,676,681,685,688,715,718,721,756,759,762,792,795,800,804,807,926,929,932,965,968,1002,1008,1013,1016,1019,1022,1029,1032,1045,1048,1051,1091,1097],[10,11,5],"h1",{"id":12},"https-请求全链路复盘从-aesecdhe-到-tls-13",[14,15,16],"p",{},"最近重新看量子加密相关资料时，我发现一个很现实的问题：很多更前沿的安全概念，最后还是要落回传统密码学的地基上。",[14,18,19],{},"比如我们说“量子安全”“后量子密码”“密钥分发”，听起来很新，但如果连一次普通的 HTTPS 请求里，AES 在哪里用、证书解决什么问题、ECDHE 为什么能在被监听的网络里协商出共享密钥，都没有讲清楚，后面的概念就很容易飘在空中。",[14,21,22,23,27],{},"所以这篇文章不从算法论文开始，而是讲一个最常见的故事：浏览器访问 ",[24,25,26],"code",{},"https:\u002F\u002Fexample.com\u002Findex.html"," 时，从 DNS、TCP、TLS 到 HTTP 请求，完整发生了什么。",[29,30,32],"h2",{"id":31},"_1-先把密码学积木摆清楚","1. 先把密码学积木摆清楚",[14,34,35],{},"HTTPS 不是一种单独的加密算法，而是一套组合拳。里面至少会用到三类能力：对称加密、非对称身份认证、密钥交换。",[14,37,38],{},"先看对称加密。所谓对称，就是加密和解密使用同一个 key。常见算法是 AES。它的优势非常直接：速度快，适合保护大量数据。真正的 HTTP 请求体、响应体，最终通常就是靠对称加密算法在 TLS Record 层一段一段保护起来的。",[14,40,41],{},"对称加密的风险不在于“算法不够强”，而在于“key 怎么安全交给对方”。如果客户端和服务器已经有同一个 AES key，后续通信就好办；问题是，第一次见面时怎么在公网里协商出这个 key。",[14,43,44],{},"从安全强度上看，AES-128 对传统暴力破解大致提供 128-bit 强度。量子计算里的 Grover 算法可以给无结构搜索带来平方级加速，所以粗略理解下，AES-128 面对理想量子搜索时会下降到约 64-bit 量级。它不是“常量时间破解”，也不是量子计算机一来 AES 就废了；更现实的工程判断是，AES-192、AES-256 提供了更高的安全余量。",[14,46,47],{},"再看非对称密码。非对称的核心是公钥和私钥成对出现：公钥可以公开，私钥必须保密。它常见有三种用途：",[49,50,51,70],"table",{},[52,53,54],"thead",{},[55,56,57,61,64,67],"tr",{},[58,59,60],"th",{},"用途",[58,62,63],{},"怎么用",[58,65,66],{},"解决什么问题",[58,68,69],{},"常见例子",[71,72,73,88,102],"tbody",{},[55,74,75,79,82,85],{},[76,77,78],"td",{},"加密 \u002F 解密",[76,80,81],{},"公钥加密，私钥解密",[76,83,84],{},"让别人安全地告诉我秘密",[76,86,87],{},"RSA 加密",[55,89,90,93,96,99],{},[76,91,92],{},"签名 \u002F 验签",[76,94,95],{},"私钥签名，公钥验证",[76,97,98],{},"向别人证明“确实是我发的，且没被改”",[76,100,101],{},"RSA-PSS、ECDSA、Ed25519",[55,103,104,107,110,113],{},[76,105,106],{},"密钥交换",[76,108,109],{},"双方交换临时公钥，各自用自己的私钥和对方公钥算出同一个共享秘密",[76,111,112],{},"在被监听的网络中协商对称加密材料",[76,114,115],{},"ECDHE",[14,117,118],{},"这里最容易混淆的是：证书里的公钥通常不是用来“直接加密所有 HTTP 数据”的。现代 TLS 里，证书更核心的作用是证明服务器身份：浏览器通过证书链确认“这个公钥确实属于这个域名”，服务器再用对应私钥对握手 transcript 做签名，证明自己确实持有私钥。",[14,120,121,125],{},[122,123,124],"strong",{},"小结："," HTTPS 的底层不是单一算法，而是分工协作：对称加密负责高速保护数据，证书和签名负责身份认证，ECDHE 负责在公网中协商出后续对称加密所需的秘密材料。",[29,127,129],{"id":128},"_2-https-到底在网络栈的哪一层","2. HTTPS 到底在网络栈的哪一层",[14,131,132],{},"最常见的记忆方式是：",[134,135,140],"pre",{"className":136,"code":137,"language":138,"meta":139,"style":139},"language-plaintext shiki shiki-themes github-dark","HTTP\u002F1.1 或 HTTP\u002F2\n        ↓\nTLS\n        ↓\nTCP\n        ↓\nIP\n","plaintext","",[24,141,142,150,156,162,167,173,178],{"__ignoreMap":139},[143,144,147],"span",{"class":145,"line":146},"line",1,[143,148,149],{},"HTTP\u002F1.1 或 HTTP\u002F2\n",[143,151,153],{"class":145,"line":152},2,[143,154,155],{},"        ↓\n",[143,157,159],{"class":145,"line":158},3,[143,160,161],{},"TLS\n",[143,163,165],{"class":145,"line":164},4,[143,166,155],{},[143,168,170],{"class":145,"line":169},5,[143,171,172],{},"TCP\n",[143,174,176],{"class":145,"line":175},6,[143,177,155],{},[143,179,181],{"class":145,"line":180},7,[143,182,183],{},"IP\n",[14,185,186],{},"所以在 HTTP\u002F1.1 和 HTTP\u002F2 的语境下，可以粗略说：",[134,188,190],{"className":136,"code":189,"language":138,"meta":139,"style":139},"HTTPS = HTTP over TLS over TCP\n",[24,191,192],{"__ignoreMap":139},[143,193,194],{"class":145,"line":146},[143,195,189],{},[14,197,198],{},"HTTP 是应用层协议，TCP 是传输层协议。TLS 夹在两者之间，给应用层字节流提供加密、完整性校验和身份认证能力。它并不严格对应 OSI 七层模型里的某一层，更像是应用层和传输层之间的一层安全包装。",[14,200,201],{},"到了 HTTP\u002F3，情况会变成：",[134,203,205],{"className":136,"code":204,"language":138,"meta":139,"style":139},"HTTP\u002F3\n  ↓\nQUIC\n  ↓\nUDP\n",[24,206,207,212,217,222,226],{"__ignoreMap":139},[143,208,209],{"class":145,"line":146},[143,210,211],{},"HTTP\u002F3\n",[143,213,214],{"class":145,"line":152},[143,215,216],{},"  ↓\n",[143,218,219],{"class":145,"line":158},[143,220,221],{},"QUIC\n",[143,223,224],{"class":145,"line":164},[143,225,216],{},[143,227,228],{"class":145,"line":169},[143,229,230],{},"UDP\n",[14,232,233],{},"QUIC 使用 TLS 1.3 的握手和密钥派生机制，但它不是把完整的 TLS Record Layer 原封不动塞到 UDP 里。可以把它理解为：QUIC 把可靠传输、多路复用、拥塞控制和 TLS 1.3 安全能力揉进了一个基于 UDP 的协议里。",[14,235,236],{},"今天做 HTTPS 复习，重点看 TLS 1.2 和 TLS 1.3 就够了。TLS 1.0、TLS 1.1 已经被 RFC 8996 明确弃用，更早的 SSL v2、SSL v3 也早就不应该再进入现代系统设计视野。",[14,238,239,241],{},[122,240,124],{}," 对 HTTP\u002F1.1 和 HTTP\u002F2 来说，HTTPS 可以理解为 HTTP 被 TLS 包起来后跑在 TCP 上；对 HTTP\u002F3 来说，TLS 1.3 的能力被 QUIC 集成进协议本身，不能简单画成“TLS over UDP”。",[29,243,245],{"id":244},"_3-一次-https-请求的总流程","3. 一次 HTTPS 请求的总流程",[14,247,248],{},"先不区分 TLS 1.2 和 TLS 1.3，只看一个大轮廓：",[134,250,252],{"className":136,"code":251,"language":138,"meta":139,"style":139},"浏览器输入 https:\u002F\u002Fexample.com\u002Findex.html\n    ↓\nDNS 解析域名，拿到 IP\n    ↓\n建立传输连接\n    - HTTP\u002F1.1 \u002F HTTP\u002F2: TCP 三次握手\n    - HTTP\u002F3: QUIC 基于 UDP 建连\n    ↓\nTLS 握手\n    - 协商协议版本和密码套件\n    - 验证服务器证书\n    - 完成密钥交换\n    - 派生后续加密 HTTP 数据的 traffic keys\n    ↓\n浏览器构造 HTTP 请求明文\n    ↓\nTLS\u002FQUIC 加密并认证这段应用数据\n    ↓\n服务器解密、校验、交给 HTTP Server\n    ↓\n服务器生成响应，再加密发回浏览器\n",[24,253,254,259,264,269,273,278,283,288,293,299,305,311,317,323,328,334,339,345,350,356,361],{"__ignoreMap":139},[143,255,256],{"class":145,"line":146},[143,257,258],{},"浏览器输入 https:\u002F\u002Fexample.com\u002Findex.html\n",[143,260,261],{"class":145,"line":152},[143,262,263],{},"    ↓\n",[143,265,266],{"class":145,"line":158},[143,267,268],{},"DNS 解析域名，拿到 IP\n",[143,270,271],{"class":145,"line":164},[143,272,263],{},[143,274,275],{"class":145,"line":169},[143,276,277],{},"建立传输连接\n",[143,279,280],{"class":145,"line":175},[143,281,282],{},"    - HTTP\u002F1.1 \u002F HTTP\u002F2: TCP 三次握手\n",[143,284,285],{"class":145,"line":180},[143,286,287],{},"    - HTTP\u002F3: QUIC 基于 UDP 建连\n",[143,289,291],{"class":145,"line":290},8,[143,292,263],{},[143,294,296],{"class":145,"line":295},9,[143,297,298],{},"TLS 握手\n",[143,300,302],{"class":145,"line":301},10,[143,303,304],{},"    - 协商协议版本和密码套件\n",[143,306,308],{"class":145,"line":307},11,[143,309,310],{},"    - 验证服务器证书\n",[143,312,314],{"class":145,"line":313},12,[143,315,316],{},"    - 完成密钥交换\n",[143,318,320],{"class":145,"line":319},13,[143,321,322],{},"    - 派生后续加密 HTTP 数据的 traffic keys\n",[143,324,326],{"class":145,"line":325},14,[143,327,263],{},[143,329,331],{"class":145,"line":330},15,[143,332,333],{},"浏览器构造 HTTP 请求明文\n",[143,335,337],{"class":145,"line":336},16,[143,338,263],{},[143,340,342],{"class":145,"line":341},17,[143,343,344],{},"TLS\u002FQUIC 加密并认证这段应用数据\n",[143,346,348],{"class":145,"line":347},18,[143,349,263],{},[143,351,353],{"class":145,"line":352},19,[143,354,355],{},"服务器解密、校验、交给 HTTP Server\n",[143,357,359],{"class":145,"line":358},20,[143,360,263],{},[143,362,364],{"class":145,"line":363},21,[143,365,366],{},"服务器生成响应，再加密发回浏览器\n",[14,368,369],{},"这里有两个关键点。",[14,371,372],{},"第一，DNS 查询本身不一定受 HTTPS 保护。传统 DNS 是明文的，除非使用 DoH、DoT 或其他加密 DNS 方案。很多人说“访问 HTTPS 网站就是全程加密”，严格说是从 TLS 握手建立保护能力之后，HTTP 应用数据才被加密保护。",[14,374,375],{},"第二，TLS 握手并不只是为了“拿到一个 AES key”。它还要协商协议参数，验证服务器身份，确认握手过程没有被中间人篡改。否则攻击者可以把自己夹在浏览器和服务器之间，各自建立连接，偷偷转发和篡改内容。",[14,377,378,380],{},[122,379,124],{}," HTTPS 的主线可以理解为：先找到服务器并建立传输通道，再通过 TLS 握手完成身份认证和密钥协商，最后把 HTTP 明文作为加密后的 Application Data 在网络里传输。",[29,382,384],{"id":383},"_4-tls-12先握手再切换到加密通信","4. TLS 1.2：先握手，再切换到加密通信",[14,386,387],{},"TLS 1.2 支持多种历史密码套件。为了贴近现代 HTTPS，这里只讨论常见的 ECDHE 场景。",[14,389,390],{},"主流程可以压缩成这样：",[134,392,394],{"className":136,"code":393,"language":138,"meta":139,"style":139},"DNS\n→ TCP 三次握手\n→ ClientHello\n→ ServerHello + Certificate + ServerKeyExchange + ServerHelloDone\n→ 浏览器验证证书链、域名、有效期、用途，并验证 ServerKeyExchange 签名\n→ ClientKeyExchange\n→ 双方分别计算 ECDHE shared secret \u002F premaster secret\n→ premaster_secret + client_random + server_random 派生 master_secret\n→ master_secret 派生 client_write_key \u002F server_write_key 等材料\n→ ChangeCipherSpec + Finished\n→ HTTP 请求\u002F响应作为 TLS Application Data 加密传输\n",[24,395,396,401,406,411,416,421,426,431,436,441,446],{"__ignoreMap":139},[143,397,398],{"class":145,"line":146},[143,399,400],{},"DNS\n",[143,402,403],{"class":145,"line":152},[143,404,405],{},"→ TCP 三次握手\n",[143,407,408],{"class":145,"line":158},[143,409,410],{},"→ ClientHello\n",[143,412,413],{"class":145,"line":164},[143,414,415],{},"→ ServerHello + Certificate + ServerKeyExchange + ServerHelloDone\n",[143,417,418],{"class":145,"line":169},[143,419,420],{},"→ 浏览器验证证书链、域名、有效期、用途，并验证 ServerKeyExchange 签名\n",[143,422,423],{"class":145,"line":175},[143,424,425],{},"→ ClientKeyExchange\n",[143,427,428],{"class":145,"line":180},[143,429,430],{},"→ 双方分别计算 ECDHE shared secret \u002F premaster secret\n",[143,432,433],{"class":145,"line":290},[143,434,435],{},"→ premaster_secret + client_random + server_random 派生 master_secret\n",[143,437,438],{"class":145,"line":295},[143,439,440],{},"→ master_secret 派生 client_write_key \u002F server_write_key 等材料\n",[143,442,443],{"class":145,"line":301},[143,444,445],{},"→ ChangeCipherSpec + Finished\n",[143,447,448],{"class":145,"line":307},[143,449,450],{},"→ HTTP 请求\u002F响应作为 TLS Application Data 加密传输\n",[14,452,453,454,457,458,461],{},"展开看，浏览器先发 ",[24,455,456],{},"ClientHello","，里面会带上支持的 TLS 版本、随机数 ",[24,459,460],{},"client_random","、密码套件列表、SNI、ALPN、支持的椭圆曲线组、签名算法、会话恢复信息等。",[14,463,464,465,468,469,472,473,476],{},"服务器返回 ",[24,466,467],{},"ServerHello","，选择最终使用的 TLS 版本和密码套件，并给出 ",[24,470,471],{},"server_random","。随后服务器发 ",[24,474,475],{},"Certificate","，里面包含证书链。注意，证书里有服务器公钥，没有服务器私钥。",[14,478,479,480,483],{},"如果使用 ECDHE，服务器还会发 ",[24,481,482],{},"ServerKeyExchange","。这里包含服务器临时 ECDHE 公钥，并且服务器会用证书对应的私钥对关键握手参数做签名。浏览器验证这个签名，才能确认这份临时 ECDHE 公钥确实来自证书对应的服务器，而不是中间人塞进来的。",[14,485,486,487,490],{},"随后浏览器发 ",[24,488,489],{},"ClientKeyExchange","，包含客户端临时 ECDHE 公钥。到这里，双方都拥有：",[134,492,494],{"className":136,"code":493,"language":138,"meta":139,"style":139},"自己的临时 ECDHE 私钥 + 对方的临时 ECDHE 公钥\n",[24,495,496],{"__ignoreMap":139},[143,497,498],{"class":145,"line":146},[143,499,493],{},[14,501,502,503,505,506,508,509,512,513,515],{},"于是双方可以各自算出同一个 shared secret。这个 shared secret 不会直接拿来当 AES key，而是继续和 ",[24,504,460],{},"、",[24,507,471],{}," 一起派生出 ",[24,510,511],{},"master_secret","，再从 ",[24,514,511],{}," 派生出两个方向的写入密钥、IV，以及某些非 AEAD 密码套件下的 MAC key。",[14,517,518,519,522,523,526,527,529,530,532],{},"最后双方通过 ",[24,520,521],{},"ChangeCipherSpec"," 切换到新密钥，再发送加密的 ",[24,524,525],{},"Finished","。",[24,528,525],{}," 的意义很大：它证明双方看到的握手 transcript 一致，并且双方确实派生出了同样的密钥。如果中间过程被改过，",[24,531,525],{}," 校验就过不去。",[14,534,535,537,538,540],{},[122,536,124],{}," TLS 1.2 的 ECDHE 握手像一次严谨的交接班：证书先证明服务器身份，ECDHE 协商出共享秘密，密钥派生函数生成后续通信密钥，",[24,539,525],{}," 再确认整场握手没有被篡改。",[29,542,544],{"id":543},"_5-tls-13把常用安全姿势变成默认姿势","5. TLS 1.3：把常用安全姿势变成默认姿势",[14,546,547],{},"TLS 1.3 的核心变化不是“名字从 1.2 升到 1.3”，而是把很多历史包袱清理掉了。",[14,549,550,551,553],{},"TLS 1.3 移除了 static RSA key exchange 和 static DH 这类旧模式，主线变成 ECDHE、PSK、PSK+DHE。常规完整握手下，客户端通常 1-RTT 后就可以发送应用数据，服务器证书也会在 ",[24,552,467],{}," 之后被握手密钥保护起来。",[14,555,556],{},"主流程可以这样记：",[134,558,560],{"className":136,"code":559,"language":138,"meta":139,"style":139},"DNS\n→ TCP 三次握手\n→ ClientHello，带客户端 key_share\n→ ServerHello，带服务器 key_share\n→ 双方立刻计算 ECDHE shared secret\n→ HKDF 派生 handshake traffic secrets \u002F keys\n→ EncryptedExtensions + Certificate + CertificateVerify + Finished，加密\n→ 浏览器验证证书、CertificateVerify 签名、Finished\n→ 客户端 Finished\n→ 双方派生 application traffic secrets \u002F keys\n→ HTTP 请求\u002F响应作为 TLS Application Data 加密传输\n",[24,561,562,566,570,575,580,585,590,595,600,605,610],{"__ignoreMap":139},[143,563,564],{"class":145,"line":146},[143,565,400],{},[143,567,568],{"class":145,"line":152},[143,569,405],{},[143,571,572],{"class":145,"line":158},[143,573,574],{},"→ ClientHello，带客户端 key_share\n",[143,576,577],{"class":145,"line":164},[143,578,579],{},"→ ServerHello，带服务器 key_share\n",[143,581,582],{"class":145,"line":169},[143,583,584],{},"→ 双方立刻计算 ECDHE shared secret\n",[143,586,587],{"class":145,"line":175},[143,588,589],{},"→ HKDF 派生 handshake traffic secrets \u002F keys\n",[143,591,592],{"class":145,"line":180},[143,593,594],{},"→ EncryptedExtensions + Certificate + CertificateVerify + Finished，加密\n",[143,596,597],{"class":145,"line":290},[143,598,599],{},"→ 浏览器验证证书、CertificateVerify 签名、Finished\n",[143,601,602],{"class":145,"line":295},[143,603,604],{},"→ 客户端 Finished\n",[143,606,607],{"class":145,"line":301},[143,608,609],{},"→ 双方派生 application traffic secrets \u002F keys\n",[143,611,612],{"class":145,"line":307},[143,613,450],{},[14,615,616,617,619,620,623,624,626],{},"最大的节奏变化在 ",[24,618,456],{},"。TLS 1.3 客户端一上来就带 ",[24,621,622],{},"key_share","，也就是客户端临时 ECDHE 公钥。服务器在 ",[24,625,467],{}," 里返回服务器临时 ECDHE 公钥。这样双方在很早的阶段就能算出 shared secret，并通过 HKDF 派生握手阶段的 traffic secrets。",[14,628,629,630,505,633,505,635,505,638,640,641,643,644,646],{},"接下来服务器发出的 ",[24,631,632],{},"EncryptedExtensions",[24,634,475],{},[24,636,637],{},"CertificateVerify",[24,639,525],{}," 都已经被握手密钥保护。",[24,642,637],{}," 是服务器用证书私钥对握手 transcript 做签名，证明“我确实持有这张证书对应的私钥”；",[24,645,525],{}," 则证明服务器算出了相同的握手密钥，并且握手 transcript 没被改。",[14,648,649,650,652,653,655,656,658],{},"浏览器验证服务器证书链、域名、有效期、用途，再验证 ",[24,651,637],{}," 和 ",[24,654,525],{},"。验证通过后，浏览器发送自己的 ",[24,657,525],{},"。随后双方派生 application traffic secrets \u002F keys，用来保护真正的 HTTP 应用数据。",[14,660,661],{},"TLS 1.3 还有两个常见扩展点容易误解。",[14,663,664,665,668],{},"第一，会话恢复票据 ",[24,666,667],{},"NewSessionTicket"," 不是“复用本连接的 AES key”。它是给后续连接做 resumption 用的材料。",[14,670,671,672,675],{},"第二，",[24,673,674],{},"KeyUpdate"," 可以让长连接切到下一代 traffic key，但这不代表“每个 HTTP 请求都会换一次 key”。现实中，数据加密的粒度仍然主要是 TLS record，密钥更新是连接级别的安全机制。",[14,677,678,680],{},[122,679,124],{}," TLS 1.3 把现代 HTTPS 常用的安全实践收敛成更清晰的主线：尽早交换 ECDHE 公钥，尽早派生握手密钥，尽早加密后续握手消息，再用 application traffic keys 保护 HTTP 数据。",[29,682,684],{"id":683},"_6-http-明文什么时候变成密文","6. HTTP 明文什么时候变成密文",[14,686,687],{},"TLS 握手完成后，浏览器才真正开始发送 HTTP 请求。以 HTTP\u002F1.1 为例，浏览器内部构造出的明文可能长这样：",[134,689,693],{"className":690,"code":691,"language":692,"meta":139,"style":139},"language-http shiki shiki-themes github-dark","GET \u002Findex.html HTTP\u002F1.1\nHost: example.com\nUser-Agent: ...\nAccept: ...\n","http",[24,694,695,700,705,710],{"__ignoreMap":139},[143,696,697],{"class":145,"line":146},[143,698,699],{},"GET \u002Findex.html HTTP\u002F1.1\n",[143,701,702],{"class":145,"line":152},[143,703,704],{},"Host: example.com\n",[143,706,707],{"class":145,"line":158},[143,708,709],{},"User-Agent: ...\n",[143,711,712],{"class":145,"line":164},[143,713,714],{},"Accept: ...\n",[14,716,717],{},"这段明文不会直接出现在公网里。它会交给 TLS Record Layer，被切成一个或多个 record，再用当前方向的应用数据密钥加密并认证。",[14,719,720],{},"如果用 AEAD 密码套件，可以粗略理解成：",[134,722,724],{"className":136,"code":723,"language":138,"meta":139,"style":139},"TLSCiphertext = AEAD-Encrypt(\n  key        = client application traffic key,\n  nonce      = 根据 IV 和 record sequence number 构造,\n  plaintext  = HTTP request bytes,\n  aad        = record metadata\n)\n",[24,725,726,731,736,741,746,751],{"__ignoreMap":139},[143,727,728],{"class":145,"line":146},[143,729,730],{},"TLSCiphertext = AEAD-Encrypt(\n",[143,732,733],{"class":145,"line":152},[143,734,735],{},"  key        = client application traffic key,\n",[143,737,738],{"class":145,"line":158},[143,739,740],{},"  nonce      = 根据 IV 和 record sequence number 构造,\n",[143,742,743],{"class":145,"line":164},[143,744,745],{},"  plaintext  = HTTP request bytes,\n",[143,747,748],{"class":145,"line":169},[143,749,750],{},"  aad        = record metadata\n",[143,752,753],{"class":145,"line":175},[143,754,755],{},")\n",[14,757,758],{},"服务器收到的是 TLS Application Data 密文。它用客户端方向的 application traffic key 解密，并校验认证标签。校验失败说明数据可能被篡改，连接应当报错；校验成功后，服务器才把还原出来的 HTTP 明文交给 Web Server 或应用框架处理。",[14,760,761],{},"响应方向也一样，只是换成服务器方向的 key：",[134,763,765],{"className":136,"code":764,"language":138,"meta":139,"style":139},"HTTP response plaintext\n→ TLS Record Layer 加密和认证\n→ TLS Application Data 密文\n→ 浏览器解密和校验\n→ 浏览器拿到 HTTP 响应明文\n",[24,766,767,772,777,782,787],{"__ignoreMap":139},[143,768,769],{"class":145,"line":146},[143,770,771],{},"HTTP response plaintext\n",[143,773,774],{"class":145,"line":152},[143,775,776],{},"→ TLS Record Layer 加密和认证\n",[143,778,779],{"class":145,"line":158},[143,780,781],{},"→ TLS Application Data 密文\n",[143,783,784],{"class":145,"line":164},[143,785,786],{},"→ 浏览器解密和校验\n",[143,788,789],{"class":145,"line":169},[143,790,791],{},"→ 浏览器拿到 HTTP 响应明文\n",[14,793,794],{},"这里的“双向 key”很重要。客户端写数据用客户端方向的 key，服务器写数据用服务器方向的 key。这样即使两边都在同一条连接里通信，密钥材料也不会混用。",[14,796,797,799],{},[122,798,124],{}," TLS 保护的不是“一个 HTTP 请求对象”，而是一段段 record。HTTP 请求和响应在应用层是明文结构，进入 TLS Record Layer 后才变成带完整性保护的密文。",[29,801,803],{"id":802},"_7-tls-12-和-tls-13-的差异总表","7. TLS 1.2 和 TLS 1.3 的差异总表",[14,805,806],{},"把两代协议放在一起，对比会更清楚：",[49,808,809,822],{},[52,810,811],{},[55,812,813,816,819],{},[58,814,815],{},"维度",[58,817,818],{},"TLS 1.2",[58,820,821],{},"TLS 1.3",[71,823,824,835,848,858,870,883,902,916],{},[55,825,826,829,832],{},[76,827,828],{},"典型完整握手 RTT",[76,830,831],{},"通常 2-RTT 才能让客户端安全发送应用数据",[76,833,834],{},"通常 1-RTT 后客户端可发送应用数据",[55,836,837,840,843],{},[76,838,839],{},"证书是否明文传输",[76,841,842],{},"服务器证书通常明文传输",[76,844,845,847],{},[24,846,467],{}," 之后的证书消息被握手密钥保护",[55,849,850,852,855],{},[76,851,106],{},[76,853,854],{},"历史上支持 RSA key exchange、DHE、ECDHE 等；现代常见 ECDHE",[76,856,857],{},"移除 static RSA \u002F static DH，主线是 ECDHE \u002F PSK \u002F PSK+DHE",[55,859,860,864,867],{},[76,861,862],{},[24,863,521],{},[76,865,866],{},"正式参与密钥切换",[76,868,869],{},"仅兼容中间设备，不再是核心握手语义",[55,871,872,875,880],{},[76,873,874],{},"密钥派生",[76,876,877],{},[24,878,879],{},"premaster_secret → master_secret → key_block",[76,881,882],{},"HKDF 派生 early \u002F handshake \u002F application traffic secrets",[55,884,885,888,899],{},[76,886,887],{},"后续 HTTP 数据",[76,889,890,891,894,895,898],{},"用 ",[24,892,893],{},"client_write_key"," \u002F ",[24,896,897],{},"server_write_key"," 等材料保护",[76,900,901],{},"用 client\u002Fserver application traffic keys 保护",[55,903,904,907,910],{},[76,905,906],{},"是否每个 HTTP 请求换 key",[76,908,909],{},"通常不是",[76,911,912,913,915],{},"通常不是，但连接可通过 ",[24,914,674],{}," 更新 traffic key",[55,917,918,921,924],{},[76,919,920],{},"数据加密粒度",[76,922,923],{},"TLS record 级别",[76,925,923],{},[14,927,928],{},"再浓缩成两条记忆线。",[14,930,931],{},"TLS 1.2：",[134,933,935],{"className":136,"code":934,"language":138,"meta":139,"style":139},"ClientHello\n→ ServerHello + Certificate + ServerKeyExchange\n→ ClientKeyExchange\n→ premaster_secret \u002F master_secret \u002F key_block\n→ ChangeCipherSpec + Finished\n→ Application Data\n",[24,936,937,942,947,951,956,960],{"__ignoreMap":139},[143,938,939],{"class":145,"line":146},[143,940,941],{},"ClientHello\n",[143,943,944],{"class":145,"line":152},[143,945,946],{},"→ ServerHello + Certificate + ServerKeyExchange\n",[143,948,949],{"class":145,"line":158},[143,950,425],{},[143,952,953],{"class":145,"line":164},[143,954,955],{},"→ premaster_secret \u002F master_secret \u002F key_block\n",[143,957,958],{"class":145,"line":169},[143,959,445],{},[143,961,962],{"class":145,"line":175},[143,963,964],{},"→ Application Data\n",[14,966,967],{},"TLS 1.3：",[134,969,971],{"className":136,"code":970,"language":138,"meta":139,"style":139},"ClientHello(key_share)\n→ ServerHello(key_share)\n→ handshake traffic keys\n→ EncryptedExtensions + Certificate + CertificateVerify + Finished\n→ application traffic keys\n→ Application Data\n",[24,972,973,978,983,988,993,998],{"__ignoreMap":139},[143,974,975],{"class":145,"line":146},[143,976,977],{},"ClientHello(key_share)\n",[143,979,980],{"class":145,"line":152},[143,981,982],{},"→ ServerHello(key_share)\n",[143,984,985],{"class":145,"line":158},[143,986,987],{},"→ handshake traffic keys\n",[143,989,990],{"class":145,"line":164},[143,991,992],{},"→ EncryptedExtensions + Certificate + CertificateVerify + Finished\n",[143,994,995],{"class":145,"line":169},[143,996,997],{},"→ application traffic keys\n",[143,999,1000],{"class":145,"line":175},[143,1001,964],{},[14,1003,1004,1005,1007],{},"还有一个现实边界：TLS 1.3 加密了服务器证书消息，但普通 SNI 仍在 ",[24,1006,456],{}," 里明文出现。要隐藏 SNI，需要 ECH 这类额外机制。因此，“用了 TLS 1.3”不等于“所有元数据都不可见”。",[14,1009,1010,1012],{},[122,1011,124],{}," TLS 1.3 的价值在于更少历史包袱、更快握手、更清晰的密钥派生和更早的握手加密；但 HTTPS 仍然不是魔法罩，哪些内容被保护、哪些元数据仍可见，需要按协议阶段具体分析。",[29,1014,1015],{"id":1015},"总结",[14,1017,1018],{},"一次 HTTPS 请求背后，其实是一场分工明确的协作。",[14,1020,1021],{},"浏览器先通过 DNS 找到服务器，再建立 TCP 或 QUIC 传输通道。TLS 握手阶段，证书链和签名解决“我连的是不是目标服务器”的问题，ECDHE 解决“如何在公网中协商共享秘密”的问题，HKDF 或 TLS 1.2 的 PRF 再把这些秘密材料派生成真正用于通信的 key。",[14,1023,1024,1025,1028],{},"等握手完成后，HTTP 请求和响应才作为 Application Data 被加密传输。网络上看到的是密文 record，不是 ",[24,1026,1027],{},"GET \u002Findex.html"," 这样的明文。",[14,1030,1031],{},"理解 HTTPS，关键不是背下每个报文名，而是抓住三条主线：",[1033,1034,1035,1039,1042],"ol",{},[1036,1037,1038],"li",{},"身份认证：证书链、域名校验、私钥签名。",[1036,1040,1041],{},"密钥协商：ECDHE 让双方在不安全网络里得到同一个 shared secret。",[1036,1043,1044],{},"数据保护：Record Layer 用双向 traffic keys 对 HTTP 数据做加密和完整性校验。",[14,1046,1047],{},"有了这条主线，再回头看量子加密、后量子密码、QUIC、ECH 或证书体系，就不会觉得它们是散落的名词，而是围绕“身份、密钥、数据保护”这三个问题的不同工程答案。",[29,1049,1050],{"id":1050},"参考资料",[1052,1053,1054,1063,1070,1077,1084],"ul",{},[1036,1055,1056],{},[1057,1058,1062],"a",{"href":1059,"rel":1060},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc5246",[1061],"nofollow","RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2",[1036,1064,1065],{},[1057,1066,1069],{"href":1067,"rel":1068},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc8446",[1061],"RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3",[1036,1071,1072],{},[1057,1073,1076],{"href":1074,"rel":1075},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc8996",[1061],"RFC 8996: Deprecating TLS 1.0 and TLS 1.1",[1036,1078,1079],{},[1057,1080,1083],{"href":1081,"rel":1082},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9001",[1061],"RFC 9001: Using TLS to Secure QUIC",[1036,1085,1086],{},[1057,1087,1090],{"href":1088,"rel":1089},"https:\u002F\u002Fcsrc.nist.gov\u002Fpubs\u002Ffips\u002F197\u002Ffinal",[1061],"NIST FIPS 197: Advanced Encryption Standard (AES)",[14,1092,1093],{},[1057,1094,1096],{"href":1095},"\u002Fblog\u002F","返回博客列表",[1098,1099,1100],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":139,"searchDepth":152,"depth":152,"links":1102},[1103,1104,1105,1106,1107,1108,1109,1110,1111],{"id":31,"depth":152,"text":32},{"id":128,"depth":152,"text":129},{"id":244,"depth":152,"text":245},{"id":383,"depth":152,"text":384},{"id":543,"depth":152,"text":544},{"id":683,"depth":152,"text":684},{"id":802,"depth":152,"text":803},{"id":1015,"depth":152,"text":1015},{"id":1050,"depth":152,"text":1050},"网络安全","2026-07-06","md",{},true,"\u002Fblog\u002Fhttps-tls-handshake-guide",{"title":5,"description":16},null,"blog\u002Fhttps-tls-handshake-guide",[1122,1123,1124,1125,115],"HTTPS","TLS","密码学","AES","WXAHqJIM5lwcMNYPI5xSdchahUJeQGNwnISQES9AfPY",1783309725603]