目錄
- 準(zhǔn)備
- 分析
2.1. 三次握手
2.2. 創(chuàng)建 HTTP 代理(非必要)
2.3. TLS/SSL 握手
2.4. 數(shù)據(jù)傳輸
2.5. 四次揮手 - 擴(kuò)展
3.1. Session ID 和 Session Ticket
3.2. SNI(Server Name Indication)
3.3. ALPN(Application Layer Protocol Negotiation) - 資料
1. 準(zhǔn)備
我的操作是這樣的,讓手機(jī)和電腦在同一個(gè)局域網(wǎng)內(nèi)(比如連接同一個(gè) wifi),接著在手機(jī)的wifi上設(shè)置代理,電腦使用 Charles 做代理,IP 為電腦在局域網(wǎng) IP,我這邊的環(huán)境,手機(jī) IP 為 172.17.32.117,電腦 IP 為 172.17.32.19。再設(shè)置代理端口為 8888。設(shè)置代理后,接下來(lái)手機(jī)的請(qǐng)求都會(huì)通過(guò)電腦的網(wǎng)卡代理請(qǐng)求發(fā)送出去。
其實(shí)可以不用這么繞。我之所以多設(shè)了一個(gè)代理,是因?yàn)樽约弘娔X創(chuàng)建的 wifi 熱點(diǎn),手機(jī)接收不到。為了讓手機(jī)的包能經(jīng)過(guò)電腦網(wǎng)絡(luò)嗅探到才這么處理的。
最便捷的方式,就是電腦放個(gè) wifi 熱點(diǎn)給手機(jī)連接完事。
創(chuàng)建后代理連接后,然后使用 Wireshark 嗅探網(wǎng)卡,比如我這里使用的是 etho0 網(wǎng)卡去訪問(wèn)網(wǎng)絡(luò)的。這時(shí)候玩玩手機(jī),打開幾個(gè)請(qǐng)求,Wireshark 上面就會(huì)出現(xiàn)捕捉的大量的包,各種各樣的協(xié)議都有,有 ARP 尋人啟事(尋找 IP 對(duì)應(yīng)的物理地址),有 TCP 連接包,有 HTTP 請(qǐng)求包。

這里我設(shè)置了一下過(guò)濾規(guī)則,把對(duì)網(wǎng)易的一個(gè) https://nex.163.com 的一個(gè)的請(qǐng)求過(guò)濾出來(lái)如下:

整個(gè)完整的 HTTPS 請(qǐng)求的過(guò)程如下:
- TCP 三次握手。
- 因?yàn)槲沂褂秒娔X作為代理,所以還有一個(gè) CONNECT 請(qǐng)求用來(lái)建立 HTTP 代理。
- 使用 TLSv1.2 進(jìn)行 SSL 握手。
- 使用握手協(xié)商好的密鑰對(duì) HTTP 進(jìn)行加密傳輸。
- TCP 四次揮手。
接下來(lái)把手機(jī)稱為 A(172.17.32.211),電腦稱為 B(172.17.32.19),對(duì)完整的過(guò)程進(jìn)行簡(jiǎn)要分析。
2. 分析
2.1. 三次握手
2.1.1. TCP 協(xié)議內(nèi)容
作為整個(gè)過(guò)程的第一個(gè) TCP 包,這里對(duì)它做一個(gè)詳細(xì)的剖析,理解一下 TCP 報(bào)文的格式和內(nèi)容。TCP 是傳輸層協(xié)議,負(fù)責(zé)可靠的數(shù)據(jù)通信,它在整個(gè)體系結(jié)構(gòu)的位置如下:

作為傳輸層協(xié)議,主要為上層協(xié)議提供三個(gè)功能:
- 可靠傳輸,為每個(gè)字節(jié)安排好序號(hào),排好序,并且有重傳機(jī)制保證信息不丟失。
- 流量控制,有滑動(dòng)窗口,避免發(fā)送端和接收端速率不一致導(dǎo)致發(fā)包過(guò)快來(lái)不及接收。
- 擁塞避免,在網(wǎng)絡(luò)環(huán)境差的時(shí)候,控制好傳包的時(shí)間間隔,避開高峰期,不給原本已經(jīng)很擁堵的網(wǎng)絡(luò)添堵。
TCP 協(xié)議為 HTTP 和 SSL 協(xié)議提供了基礎(chǔ)的通信功能。所以 SSL 協(xié)議是基于 TCP 的。
三次握手的內(nèi)容有:
| No. | Time | Source | Destionation | Protocol | Length | Info |
|---|---|---|---|---|---|---|
| 379 | 4.623811 | 172.17.32.211 | 172.17.32.19 | TCP | 74 | 35973 → 8888 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM=1 TSval=15986187 TSecr=0 WS=256 |
| 380 | 4.623860 | 172.17.32.19 | 172.17.32.211 | TCP | 74 | 8888 → 35973 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1 TSval=59355465 TSecr=15986187 |
| 393 | 4.781431 | 172.17.32.211 | 172.17.32.19 | TCP | 66 | 35973 → 8888 [ACK] Seq=1 Ack=1 Win=87808 Len=0 TSval=15986193 TSecr=59355465 |
對(duì)每個(gè)包進(jìn)行詳細(xì)的分析:
2.1.2. Round 1
A 發(fā)出一個(gè)帶 SYN 同步位的包,通知服務(wù)端要建立連接。
第一次握手,發(fā)出的 TCP 包的數(shù)據(jù)和 Wireshark 解析的結(jié)果如下:

灰色部分就是 TCP 報(bào)文的數(shù)據(jù)內(nèi)容,第一個(gè)兩個(gè)字節(jié) 0x8c85 = 35973 表示源端口。
TCP 報(bào)文的格式如下,對(duì)應(yīng)的如上圖的灰色部分。非灰色部分分別為 IP 首部和數(shù)據(jù)幀首部。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 源端口 | 目的端口 |
+-----------------------------------------------+-----------------------------------------------+
| 序列號(hào) |
+-----------------------------------------------------------------------------------------------+
| 確認(rèn)號(hào) |
+-----------+-----------------+-----------------+-----------------------------------------------+
| 首部長(zhǎng)度 | 保留位 | U| A| P| R| S| F| 窗口 |
+-----------+-----------------+-----------------+-----------------------------------------------+
| 校驗(yàn)和 | 緊急指針 |
+-----------------------------------------------+-----------------------+-----------------------+
| 選項(xiàng) | 填充 |
+=======================================================================+=======================+
| 數(shù)據(jù) |
參考謝希仁版本的 《計(jì)算機(jī)網(wǎng)絡(luò)》一書,對(duì)照著整個(gè)報(bào)文格式表,把整個(gè) TCP 報(bào)文的二進(jìn)制信息和相關(guān)意義做些說(shuō)明:
源端口,2 字節(jié),0x8c85 = 35973。
目的端口,2字節(jié),0x22b8 = 8888。
序號(hào),4 字節(jié),0xc7a3ce5c。TCP 連接傳送的每一個(gè)字節(jié)都有編號(hào),而這里表示的是要發(fā)送的數(shù)據(jù)(data)的第一個(gè)字節(jié)的序號(hào)。后面按照這個(gè)序號(hào)遞增。這里發(fā)送的是 TCP 連接的第一個(gè)包,這里的序號(hào)是隨機(jī)產(chǎn)生的。
確認(rèn)號(hào),4 字節(jié),0x00000000。期望收到的下一個(gè)報(bào)文段數(shù)據(jù)部分第一個(gè)字節(jié)的序號(hào)。如果發(fā)出了 N 確認(rèn)號(hào),就表示 N-1 之前的數(shù)據(jù)都收到了。
首部長(zhǎng)度(偏移),4 位,0xa = 10。這里每一位表示4字節(jié),所以 10*4 = 40 字節(jié)。TCP 首部的長(zhǎng)度,因?yàn)?4 位最大的數(shù)為 15,所以整個(gè)首部最大 60 字節(jié)。首部后面就是數(shù)據(jù)字段。
保留位,6 位,0x000。這里還沒(méi)有用到為 0,這里的設(shè)計(jì)作為一個(gè)擴(kuò)展以便未來(lái)會(huì)用上。
-
控制位,6 位,0x002 = 二進(jìn)制 000010。每一位都有意義,這里對(duì)應(yīng) 6 種類型:
- URG,Urgent,表示緊急數(shù)據(jù)要提交,有了這個(gè)標(biāo)記為整個(gè)報(bào)文就有了插隊(duì)的特權(quán),和緊急指針一起用。
- ACK,Acknowledge,1 表示這是一個(gè)確認(rèn)報(bào)文,用來(lái)確認(rèn)收到了包,確認(rèn)報(bào)文是不帶數(shù)據(jù)的。
- PSH,Push,1 表示這是一個(gè)推送報(bào)文,通知對(duì)方盡快響應(yīng)。因?yàn)榉?wù)端可能因?yàn)榫彺鎲?wèn)題要等了一會(huì)兒才發(fā)包。這里就是催促一下對(duì)方趕緊發(fā)包。
- RST,Reset,1 表示拒絕了這個(gè)包,網(wǎng)絡(luò)發(fā)生錯(cuò)誤的時(shí)候這種包會(huì)非常多。比如重復(fù)的包就會(huì)被 reset 掉。
- SYN,Synchronization,1 表示建立連接用來(lái)同步序號(hào)。用來(lái)握手階段,通知對(duì)方包的初始序號(hào)。
- FIN,F(xiàn)inish,1 表示發(fā)送方 B 完成數(shù)據(jù)發(fā)送,通知接收方 A 該結(jié)束了。
我們這里作為整個(gè)請(qǐng)求過(guò)程中的第一個(gè) TCP 報(bào)文,僅僅設(shè)置一個(gè)控制位 SYN,一方面通知響應(yīng)者 B 要建立連接了,另一方面同步一下序號(hào)。
窗口,2 字節(jié),0xffff = 65535。發(fā)送本報(bào)文的接收窗口大小,比如 A 發(fā)送了這個(gè)報(bào)文,表示能接收的數(shù)據(jù)量為從確認(rèn)號(hào)算起來(lái)加上窗口大小,B 發(fā)送的報(bào)文字節(jié)數(shù)不能超過(guò)這個(gè)限制。這個(gè)值受 A 的緩存影響,是動(dòng)態(tài)變化的。前面給出確認(rèn)號(hào)為 0, 然后窗口大小 65535,表示還有 65535 的緩存空間可以接受序號(hào) 0 ~ 65535 的字節(jié)。
檢驗(yàn)和,2 字節(jié),0x068f。接收方受到報(bào)文要計(jì)算一下數(shù)據(jù)包的檢驗(yàn)和是否和該值匹配,確保數(shù)據(jù)包的完整。這里只保證了數(shù)據(jù)完整性,并沒(méi)有確認(rèn)服務(wù)端身份,也沒(méi)有用摘要算法。目的在于能快速檢驗(yàn),而且采用檢驗(yàn)和的方式還可以使用硬件加速。
緊急指針,2 字節(jié),0x0000f。和控制位 URG 配合使用,意義在于,有緊急數(shù)據(jù)要處理,這里的值表示緊急數(shù)據(jù)在報(bào)文中的位置。
到這里的話,TCP 數(shù)據(jù)報(bào)首部固定部分結(jié)束,固定部分一共有 20 字節(jié)。也就是 TCP 首部,至少要有 20 字節(jié)。
固定首部后,就是可長(zhǎng)度可以變化的選項(xiàng)了:
-
選項(xiàng),可達(dá) 40 字節(jié)
最大報(bào)文長(zhǎng)度,0x020405b4。0x2 表示這是個(gè) MSS 選項(xiàng),0x04 表示該選項(xiàng)一共有 4 字節(jié),這里的 0x05b4 = 1460 為該選項(xiàng)的值。這個(gè)表示 TCP 數(shù)據(jù)部分的最大字節(jié)數(shù)。
時(shí)間戳,0x080a00f3ee0b00000000。0x8 表示這是個(gè)時(shí)間戳選項(xiàng),0x0a 表示該選項(xiàng)一共有 10 字節(jié),0x00f3ee = 15986187 就是發(fā)送者的發(fā)送時(shí)間,0x00000000 = 0 表示接收端的時(shí)間。
整個(gè)所以 TCP 數(shù)據(jù)包的大小可以這樣表示:
+------------------------+--------------------------------------------------------------+
| TCP 首部,20 ~ 60 字節(jié) | 數(shù)據(jù)部分,受 MSS 大小限制 |
+------------------------+--------------------------------------------------------------+
我們 Wireshark 后面的一長(zhǎng)串的信息就指出了該 TCP 報(bào)文的一些主要信息:
35973 → 8888 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM=1 TSval=15986187 TSecr=0 WS=256
- 源端口和目的端口,35973->8888。
- 序號(hào),Seq=0,這是一個(gè)相對(duì)值而非絕對(duì)值,相對(duì)第一個(gè)包的序列號(hào)。因?yàn)槭钦麄€(gè) TCP 流的第一包,所以 Wireshark 認(rèn)定該包的序列號(hào)為 0。
- 窗口大小,win=65535,也就是發(fā)送端的當(dāng)前窗口最多容納 65535 個(gè)字節(jié)。
- 數(shù)據(jù)部分大小,Len=0,不帶數(shù)據(jù)。
- 最大報(bào)文大小選擇,MSS=1460,數(shù)據(jù)部分最多有 1460 個(gè)字節(jié)。
- 選擇確認(rèn)選項(xiàng),SACK_PERM=1。
- 發(fā)送時(shí)間戳,TSval=15986187,發(fā)出這個(gè)數(shù)據(jù)包的時(shí)候的時(shí)間戳。
- 應(yīng)答時(shí)間戳,TSecr=0,當(dāng)前要發(fā)送的包應(yīng)答的那個(gè)包的發(fā)送時(shí)間戳,因?yàn)槭堑谝粋€(gè)包,應(yīng)答的時(shí)間戳為 0。
- 窗口擴(kuò)大,WS=256。
從上面的分析可以看出,這個(gè) SYN 包并沒(méi)有攜帶數(shù)據(jù),但是按協(xié)議這里要消耗一個(gè)序號(hào)。
在發(fā)出 SYN 包后,A 端進(jìn)入 SYN-SENT 狀態(tài)。
2.1.3. Round 2
B 收到 SYN 包,發(fā)出 SYN + ACK 確認(rèn)包。
這個(gè)包,既是確認(rèn)收到了第一次握手的包,也是一個(gè)由 B 端發(fā)出的同步包,表示自己準(zhǔn)備好了,可以開始傳數(shù)據(jù)了。
因?yàn)?Wireshark 已經(jīng)幫我們分析好包的內(nèi)容了,上面列舉的包二進(jìn)制數(shù)據(jù)和 TCP 報(bào)文結(jié)構(gòu)只是為了學(xué)習(xí),實(shí)際應(yīng)用可以直接看 Wireshark 的解析內(nèi)容。包的內(nèi)容如下:
TCP 報(bào)文包相對(duì)于第一次握手的包可以窺見(jiàn)一些變化:
源端口和目的端口:8888 -> 35973。
窗口大小:8192,可以看成接收端的窗口只有 8192,和發(fā)送端差距還是挺大的。
控制位:既有 SYN 又有 ACK。因?yàn)檫@既是一個(gè)接收端 B 的自己同步包,里面有一個(gè)接收端的初始序號(hào),Wireshark 轉(zhuǎn)化為相對(duì)序號(hào) 0;同時(shí)這也是對(duì)第一次握手的包的確認(rèn)。因此這個(gè)包也不帶數(shù)據(jù)。
發(fā)送時(shí)間戳:59355465
應(yīng)答時(shí)間戳:15986187
可以看到,這個(gè)包的應(yīng)答時(shí)間戳剛好是第一次握手的發(fā)送時(shí)間戳。從這里也可以理解到,這個(gè)包就是在響應(yīng)第一次握手的包
所以,接收方 A 可以利用這個(gè)值來(lái)計(jì)算這一次 RTT,收到第二次握手的包后,計(jì)算當(dāng)前時(shí)間戳減去該包的應(yīng)答時(shí)間戳就是一個(gè) RTT 的延時(shí)了。
這雖然是 ACK 包,但也是 SYN 包,所以也要消耗一個(gè)序號(hào)。
在發(fā)出這個(gè)包后,B 端進(jìn)入 SYN-REVD 狀態(tài)。
2.1.4. Round 3
A 收到后,再發(fā)出一個(gè) ACK 確認(rèn)包
發(fā)出的包如下:
| No. | Time | Source | Destionation | Protocol | Length | Info |
|---|---|---|---|---|---|---|
| 393 | 4.781431 | 172.17.32.211 | 172.17.32.19 | TCP | 66 | 35973 → 8888 [ACK] Seq=1 Ack=1 Win=87808 Len=0 TSval=15986193 TSecr=59355465 |
這里我們產(chǎn)生一個(gè)疑問(wèn),這里發(fā)送端 A 發(fā)連接請(qǐng)求信息、接收端 B 發(fā)確認(rèn)信息,又互相同步了序號(hào),是不是已經(jīng)可以傳輸數(shù)據(jù)了?但實(shí)際上 A 還要再發(fā)一個(gè) ACK 確認(rèn)報(bào)文,如圖所示,確認(rèn)收到了 B 第二次握手發(fā)出的包,這個(gè)時(shí)候,在這個(gè) ACK 包后 A 和 B 才正式進(jìn)入 ESTABLISHED 狀態(tài)。這就是第三次握手。
這是為什么呢?
假設(shè)我們用兩次握手,然后在第一次握手期間,A 發(fā)了第一次握手包后出現(xiàn)了這樣的場(chǎng)景:一直沒(méi)有得到響應(yīng)而進(jìn)行超時(shí)重傳,又發(fā)了一次包,然后我們稱上一次包為失效包。
然后我們可以看到:
- 新包到達(dá)接收端 B,然后因?yàn)閮纱挝帐?B 覺(jué)得連接建立,于是等著發(fā)送端 A 發(fā)數(shù)據(jù),A 也發(fā)了數(shù)據(jù)。
- 失效的包經(jīng)過(guò)艱苦跋涉,也到達(dá)了接收端 B,B 并不清楚這是失效包,又開始等待發(fā)送端 A 發(fā)數(shù)據(jù)。然而此時(shí) A 不發(fā)數(shù)據(jù),所以 B 的資源就浪費(fèi)掉了。
所以,只有接收端 B 在發(fā)送端 A 發(fā)出了第三次握手包后,才認(rèn)為連接已經(jīng)建立,開始等待發(fā)送端 A 發(fā)送的數(shù)據(jù),才不會(huì)因?yàn)槭У倪B接請(qǐng)求報(bào)文導(dǎo)致接收端異常。
2.1.5. 小結(jié)
TCP 三次握手的時(shí)序圖如下:

三次握手,有幾個(gè)重要的任務(wù),一個(gè)是同步序號(hào),接收端和發(fā)送端都發(fā)出同步包來(lái)通知對(duì)方初始序號(hào),這樣子后面接收的包就可以根據(jù)序號(hào)來(lái)保證可靠傳輸;另一個(gè)是讓發(fā)送端和接收都做好準(zhǔn)備。然后就開始傳數(shù)據(jù)了。
整個(gè)過(guò)程都發(fā)生在 HTTP 報(bào)文發(fā)出之前。HTTP 協(xié)議就是依靠著 TCP 協(xié)議來(lái)做傳輸?shù)墓芾?。TCP 可以認(rèn)為是它的管家,管理著傳輸?shù)拇蟠笮⌒〉氖聞?wù),比如要不要保證包順序一致?什么時(shí)候發(fā)包?要不要收包?TCP 是很嚴(yán)格的。
三次握手在 Java API 層面,對(duì)應(yīng)的就是 Socket 的連接的創(chuàng)建(最終調(diào)用的是 native 層的 socket 創(chuàng)建):
socket.connect(address, connectTimeout);
這里的 connectTimeout 對(duì)應(yīng)的是三次握手的總時(shí)長(zhǎng),如果超時(shí)了就會(huì)被認(rèn)為連接失敗。
比如一個(gè)場(chǎng)景,客戶端發(fā)出一個(gè) SYN 報(bào)文后,遲遲沒(méi)有收到服務(wù)端的 SYN + ACK。這時(shí)候客戶端觸發(fā)重傳機(jī)制,每次重傳的間隔時(shí)間加倍,同樣沒(méi)有收到包。然后如果這段時(shí)間超出了連接超時(shí)時(shí)間的設(shè)置,那么建立連接超時(shí)就發(fā)生了。
所以,如果三次握手要花的時(shí)間,總是大于這里的 connectTimeout 時(shí)間,這個(gè) Socket 就無(wú)法建立連接。
我們這一次請(qǐng)求的三次握手時(shí)間在 180ms 左右。
像在 OkHttp 中,如果是三次握手階段的連接超時(shí),是會(huì)有重試機(jī)制的。也就是重新建聯(lián),重新發(fā)出 SYN 報(bào)文發(fā)起 TCP 連接。重新建聯(lián)的時(shí)候會(huì)更換連接的路由,如果已經(jīng)沒(méi)有可選擇路由的話,那么這個(gè)就真的失敗了。
在 OkHttp 3.9.0 的默認(rèn)配置中,連接超時(shí)的時(shí)間為 10000ms = 10s。在 OkHttpClient.Builder 中。
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
實(shí)際應(yīng)用的時(shí)候,根據(jù)業(yè)務(wù)場(chǎng)景來(lái)調(diào)整。
2.2. 創(chuàng)建 HTTP 代理(非必要)
這次請(qǐng)求,為了讓 Wireshark 抓到手機(jī)的包,我使用了電腦作為代理。
其實(shí)就是客戶端 A 使用 HTTP 協(xié)議和代理服務(wù)器 B 建立連接。和普通的 HTTP 請(qǐng)求一樣,需要攜帶 IP + 端口號(hào),如果有身份驗(yàn)證的時(shí)候還會(huì)帶上授權(quán)信息,代理服務(wù)器 B 會(huì)使用授權(quán)信息進(jìn)行驗(yàn)證。然后代理服務(wù)器會(huì)去連接遠(yuǎn)程主機(jī),連接成功后返回 200。
Wireshark 抓到的包有這樣兩條信息,就是在創(chuàng)建代理:
| No. | Time | Source | Destionation | Protocol | Length | Info |
|---|---|---|---|---|---|---|
| 396 | 4.798832 | 172.17.32.211 | 172.17.32.19 | HTTP | 284 | CONNECT nex.163.com:443 HTTP/1.1 |
| 401 | 4.816127 | 172.17.32.19 | 172.17.32.211 | HTTP | 105 | HTTP/1.0 200 Connection established |
請(qǐng)求報(bào)文:
CONNECT nex.163.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (Linux; Android 6.0.1; MI 5 Build/MXB48T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36
Host: nex.163.com
響應(yīng)報(bào)文:
HTTP/1.0 200 Connection established
HTTP CONNECT 是在 HTTP1.1 新增的命令,用于支撐 https 加密。
因?yàn)槲也捎么淼姆绞阶グ庞羞@一個(gè)步驟。如果是直接抓 PC 機(jī)上瀏覽器發(fā)出的 HTTPS 包,不會(huì)有這個(gè)過(guò)程。
然后我們思考一下,為什么代理服務(wù)器需要這些信息,要連接的主機(jī)名和端口號(hào)?
這是因?yàn)楹竺孢M(jìn)行 SSL 加密 HTTP協(xié)議,因?yàn)榇矸?wù)器拿不到加密密鑰,是無(wú)法獲取到 HTTP 首部的,進(jìn)而無(wú)法這個(gè)請(qǐng)求是要發(fā)到哪個(gè)主機(jī)的。所以,這里先使用 CONNECT 方法,把主機(jī)名和對(duì)應(yīng)的端口號(hào)通知代理服務(wù)器。
這個(gè)也被稱為 HTTPS SSL 隧道協(xié)議。建立這個(gè) SSL 隧道后,這個(gè)特殊代理就會(huì)對(duì)數(shù)據(jù)進(jìn)行盲轉(zhuǎn)發(fā)。
2.3. TLS/SSL 握手
2.3.1. TLS/SSL 協(xié)議內(nèi)容
SSL 整個(gè)協(xié)議實(shí)際上分兩層,SSL 記錄協(xié)議和其他子協(xié)議(SSL握手協(xié)議,SSL改變密碼協(xié)議,SSL警告協(xié)議):

這兩層協(xié)議的關(guān)系,其實(shí)就是數(shù)據(jù)封裝的關(guān)系,SSL 握手封裝協(xié)議封裝其他上層協(xié)議。
封裝握手協(xié)議:
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 153
Handshake Protocol: Client Hello
封裝應(yīng)用數(shù)據(jù)協(xié)議,比如 HTTP:
Secure Sockets Layer
TLSv1.2 Record Layer: Application Data Protocol: http
Content Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 1072
Encrypted Application Data: 6d9b3c9089271630c33506fe28cd6a61fed1f4bd2808f537...
封裝交換密碼協(xié)議:
Secure Sockets Layer
TLSv1.2 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
Content Type: Change Cipher Spec (20)
Version: TLS 1.2 (0x0303)
Length: 1
Change Cipher Spec Message
封裝警報(bào)協(xié)議:
Secure Sockets Layer
TLSv1.2 Record Layer: Encrypted Alert
Content Type: Alert (21)
Version: TLS 1.2 (0x0303)
Length: 48
Alert Message: Encrypted Alert
所以 SSL 記錄協(xié)議其實(shí)就是一個(gè)其他協(xié)議的載體,只是提供了一個(gè)封裝的功能。它的格式為:
| 內(nèi)容類型 | 主要版本 | 次要版本 | 長(zhǎng)度 |
| 明文/加密/壓縮 數(shù)據(jù)包 |
| MAC(0, 16, 20) |
MAC 就是消息驗(yàn)證碼,用來(lái)驗(yàn)證數(shù)據(jù)的完整性,保證中途沒(méi)有篡改。這個(gè)消息驗(yàn)證碼比數(shù)字簽名弱一些,使用的是對(duì)稱密鑰加密摘要。數(shù)字簽名使用的是非對(duì)稱密鑰加密,有區(qū)分公鑰私鑰。
記錄協(xié)議的主要目的有這幾個(gè),為其他 SSL 子協(xié)議提供了以下服務(wù):
- 分組、組合。如果有幾個(gè)協(xié)議的內(nèi)容,是可以不用等客戶端確認(rèn)后再發(fā)送的,這里會(huì)進(jìn)行組合合并,然后在同一個(gè) TCP 包中發(fā)送。接收方接收到組合的 SSL 記錄協(xié)議報(bào)文,也會(huì)根據(jù)協(xié)議來(lái)進(jìn)行分組。
壓縮、解壓縮??蛇x項(xiàng),如果需要壓縮的話。
消息認(rèn)證(MAC)。注意,這里不提供數(shù)字簽名。消息認(rèn)證用的是用對(duì)稱密鑰,解密算法效率比公鑰算法快,安全性弱一些。如果已經(jīng)明確了,加密用的對(duì)稱密鑰只有合法的客戶端和服務(wù)端獲得,那么這個(gè)的安全效果和數(shù)字簽名一樣。
-
加密傳輸 。比如已經(jīng)協(xié)商好加密密鑰和算法了,直接對(duì)應(yīng)用層協(xié)議加密傳輸:
Encrypted Application Data: 6d9b3c9089271630c33506fe28cd6a61fed1f4bd2808f537...
TCP 三次握手結(jié)束并且和代理服務(wù)器成功連接后,建聯(lián)成功,客戶端 A 就開始發(fā)起 SSL 連接,首先會(huì)進(jìn)入 SSL 握手階段。
SSL 握手階段的主要目的有這么幾個(gè):
- 協(xié)商加密算法。為了能夠提供效率,使用對(duì)稱密鑰。對(duì)稱加密使用的是位運(yùn)算,速度快,甚至可以硬件加速。非對(duì)稱加密比如 RSA,使用了大數(shù)乘法等,整體會(huì)比較慢。對(duì)稱加密只要密鑰沒(méi)有泄漏,那也是非常安全的。這也是后面 SSL 握手協(xié)議要確保的。
- 協(xié)商加密密鑰 。用來(lái)對(duì)后面的 HTTP 協(xié)議等應(yīng)用協(xié)議內(nèi)容進(jìn)行加密。這個(gè)密鑰又稱為主密鑰,為加密算法的密鑰。
- 驗(yàn)證身份 。通常情況下,只要驗(yàn)證服務(wù)端身份。特殊情況下,比如一些安全級(jí)別高的應(yīng)用場(chǎng)景,還要驗(yàn)證客戶端身份。服務(wù)端會(huì)返回證書鏈,有根 CA 證書在里頭。通過(guò)證書的鏈?zhǔn)綋?dān)保,可以確認(rèn)服務(wù)端是否是可信任的。同時(shí),在握手期間,公鑰傳輸成功后,還會(huì)對(duì)某些信息進(jìn)行數(shù)字簽名,確保數(shù)據(jù)沒(méi)有被篡改且身份無(wú)誤。
SSL 握手的流程并不是一成不變的,根據(jù)實(shí)際的應(yīng)用場(chǎng)景來(lái)。主要有三種:
- 只驗(yàn)證服務(wù)端。這個(gè)用三個(gè)階段就完成握手,我們這次的請(qǐng)求也是這樣。一般的網(wǎng)絡(luò)請(qǐng)求也僅僅到這個(gè)程度。
- 驗(yàn)證服務(wù)端和客戶端。在安全性要求較高的場(chǎng)景,服務(wù)端也要驗(yàn)證客戶端的身份。方式也是發(fā)證書證明自己。
- 恢復(fù)原有會(huì)話 。這個(gè)屬于HTTPS 優(yōu)化的范疇。使用 Session Ticket 或者 Session ID 機(jī)制恢復(fù)之前已經(jīng)完成握手的會(huì)話。這個(gè)是可以允許在不同的 TCP 上進(jìn)行的。因?yàn)槲帐值募用軘?shù)據(jù)已經(jīng)保存,直接恢復(fù)就可以開始傳遞了。Session Ticket 由客戶端保存加密信息,Session ID 的方式由服務(wù)端保存加密信息。不過(guò) Session Ticket 在 Android 客戶端還沒(méi)有得到廣泛的支持,和具體機(jī)型和內(nèi)置的 OpenSSL 的版本有關(guān)。
SSL 握手的完整的交互過(guò)程如下,這里是驗(yàn)證服務(wù)端又驗(yàn)證了客戶端的情況:

我們的請(qǐng)求只驗(yàn)證服務(wù)端,所以 7,8,9 是不存在的。
現(xiàn)在具體分析每一個(gè)階段的內(nèi)容。
2.3.2. 階段一
Client Hello
作為 SSL 握手的第一個(gè)握手包,我們?cè)敿?xì)分析和理解一下包的內(nèi)容。
下面是 Wireshark 解析好的這個(gè) SSL 協(xié)議的數(shù)據(jù)包:
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 153
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 149
Version: TLS 1.2 (0x0303)
Random
GMT Unix Time: Dec 1, 2050 02:37:13.000000000 ?й???????
Random Bytes: 4e12e967b6169c4d67caf0575079f34b277d12318385f5a9...
Session ID Length: 0
Cipher Suites Length: 40
Cipher Suites (20 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 68
Extension: server_name
Extension: Extended Master Secret
Extension: signature_algorithms
Extension: ec_point_formats
Extension: elliptic_curves
這個(gè)包如何解讀,按照之前對(duì) SSL 協(xié)議的分析,其實(shí)分成兩個(gè)部分:
- SSL 握手協(xié)議。
- SSL 記錄協(xié)議。
因?yàn)槭俏帐诌^(guò)程,密鑰還沒(méi)協(xié)商,這里還是使用明文傳輸,記錄協(xié)議的數(shù)據(jù)載體就是明文的 SSL 握手協(xié)議。
SSL 握手協(xié)議的格式為:
+-------+--------+----------------------
| 類型 | 長(zhǎng)度 | 內(nèi)容
+-------+--------+----------------------
我們可以從握手協(xié)議的數(shù)據(jù)包中得到這些信息:
-
版本,TLS 1.2 也是 SSLv3.2。這是 SSL 客戶端能夠支持的 SSL 最高版本,主版本號(hào) 3,此版本號(hào) 2。TLS 目前的版本如下:
Major Version Minor Version Version Type 3 0 SSLv3 3 1 TLS 1.0 3 2 TLS 1.1 3 3 TLS 1.2 最后使用什么樣的版本,得由服務(wù)端決定。如果服務(wù)端不支持的話,客戶端得降版本。
-
隨機(jī)數(shù) ,生成一個(gè)32字節(jié)隨機(jī)數(shù)。最后加密數(shù)據(jù)用的主密鑰,需要客戶端和服務(wù)端一起協(xié)商出來(lái)。后面服務(wù)端的 Server Hello 階段也會(huì)生成一個(gè)隨機(jī)數(shù)。一同用來(lái)計(jì)算出主密鑰。
Random Bytes: 4e12e967b6169c4d67caf0575079f34b277d12318385f5a9... 會(huì)話ID ,這里為 0。這個(gè) Session ID 是可以重用的,具體看服務(wù)端資源和支持情況。如果要復(fù)用 Session ID, SSL 服務(wù)端需要維護(hù)連接的狀態(tài)和上次握手成功留下的加密信息。因?yàn)檫@是這是第一次訪問(wèn)該網(wǎng)址,會(huì)話 ID 尚未創(chuàng)建,客戶端沒(méi)記錄,這里為 0。如果客戶端保存了 Session ID 的信息,下次發(fā)起 SSL 請(qǐng)求的時(shí)候會(huì)帶上。
-
加密套件 ,客戶端可以支持的密碼套件列表。這些套件會(huì)根據(jù)優(yōu)先級(jí)排序。每一個(gè)套件代表一個(gè)密鑰規(guī)格。以 “TLS” 開頭,接著是密鑰交換算法,然后用 “WITH” 連接加密算法和認(rèn)證算法。一個(gè)加密套件有這么幾個(gè)內(nèi)容:密鑰交換算法、加密算法(會(huì)帶有支持的最高密鑰位數(shù))、認(rèn)證算法還有加密方式
密鑰交換算法用在 SSL 握手階段的交換協(xié)商好的對(duì)稱密鑰的階段,為非對(duì)稱加密,比如:
- EC Deffie-Hellman 密鑰交換算法。這里會(huì)被縮寫為 ECDHE,也稱為 DH 加密。
- RSA 密鑰加密算法。
加密算法,是最后要用來(lái)加密 HTTP 數(shù)據(jù)的,為對(duì)稱加密算法,比如:
- DES
- 3DES
- AES
摘要算法,也是對(duì)數(shù)據(jù)進(jìn)行摘要。后面可以用來(lái)做數(shù)據(jù)的校驗(yàn),保證數(shù)據(jù)的一致性,讓中途被篡改的包失效,比如:
- MD5
- SHA1
- SHA256
所以這里一共應(yīng)用了三種密鑰技術(shù),非對(duì)稱密鑰,對(duì)稱密鑰和摘要算法。用一句話總結(jié):用非對(duì)稱加密算法來(lái)傳遞對(duì)稱加密算法的密鑰,同時(shí)用摘要算法保證數(shù)據(jù)的完整性。
這一次請(qǐng)求,客戶端提供了 20 種密碼套件供服務(wù)端選擇,最終使用什么密碼套件是服務(wù)端決定的。要什么密碼套件會(huì)在 Server Hello 中進(jìn)行反饋。
Cipher Suites (20 suites) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) Cipher Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e) Cipher Suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014) Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033) Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039) Cipher Suite: TLS_ECDHE_ECDSA_WITH_RC4_128_SHA (0xc007) Cipher Suite: TLS_ECDHE_RSA_WITH_RC4_128_SHA (0xc011) Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c) Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d) Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f) Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) Cipher Suite: TLS_RSA_WITH_RC4_128_SHA (0x0005) Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)
- 壓縮算法 ,這里為 0,說(shuō)明不支持壓縮算法
- 擴(kuò)展字段 ,一些擴(kuò)展信息,比如 SNI 的支持,ALPN 的信息等等。
密碼套件隨著密碼學(xué)的發(fā)展而發(fā)展,而且根據(jù)現(xiàn)實(shí)應(yīng)用中,可能會(huì)有某些密碼被破解,從而導(dǎo)致密碼套件可能會(huì)導(dǎo)致安全問(wèn)題,所以一般都會(huì)使用當(dāng)前最新最安全的密碼套件。
在 Android 系統(tǒng)中,一般情況下,使用 SSLSocket進(jìn)行連接的時(shí)候,會(huì)帶上系統(tǒng)默認(rèn)的支持的密碼套件。但是這個(gè)有個(gè)缺點(diǎn),比如某些密碼套件的加密算法被破解或者出現(xiàn)安全漏洞,而且要跟著系統(tǒng)升級(jí)反應(yīng)緩慢。OkHttp 在進(jìn)行 SSL 握手的時(shí)候,會(huì)使用 ConnectionSpec 類中帶上提供了一系列最新的密碼套件??梢詮淖⑨屔峡矗@些密碼套件在 Chrome 51 和 Android 7.0 以上得到了完全支持。

然后,再把這些密碼套件和 Android 系統(tǒng)支持的密碼套件取交集,提交給服務(wù)端。這樣,萬(wàn)一哪個(gè)密碼套件有問(wèn)題,OkHttp 官方會(huì)下降支持。網(wǎng)絡(luò)庫(kù) OkHttp 庫(kù)會(huì)隨著版本的迭代,不斷地去提供比較新的密碼套件,并且放棄那些不安全的密碼套件。接入應(yīng)用即時(shí)更新 OkHttp,就不用等待緩慢的系統(tǒng)更新了。
如果提供的所有密碼套件服務(wù)端都不支持,OkHttp 有回退機(jī)制,退而求其次,選比較舊的套件。
2.3.3. 階段二
Server Hello
服務(wù)端收到了客戶端的 Hello,通過(guò)客戶端的配置信息,結(jié)合服務(wù)端的自身情況,給出了最終的配置信息。
Wireshark 解析后的內(nèi)容如下:
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 93
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 89
Version: TLS 1.2 (0x0303)
Random
GMT Unix Time: Jul 4, 2022 12:28:57.000000000 ?й???????
Random Bytes: 6e15dfda5067399e9cd552ba30fb961914c5ce0e3f61d8f8...
Session ID Length: 32
Session ID: 7a92546002c514f9a0b11ef585935c7cc5182d9db3ef0db3...
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Compression Method: null (0)
Extensions Length: 17
Extension: server_name
Extension: renegotiation_info
Extension: ec_point_formats
具體內(nèi)容如下:
版本,指定這次 SSL 使用 TLSv1.2 版本。
-
隨機(jī)數(shù) ,上面的 Client Hello 過(guò)程也生產(chǎn)了一個(gè) 32 位隨機(jī)數(shù),這兩個(gè)隨機(jī)數(shù)將參與主密鑰(master key)的創(chuàng)建。
Random Bytes: 6e15dfda5067399e9cd552ba30fb961914c5ce0e3f61d8f8... -
會(huì)話ID ,這里不為 0。說(shuō)明服務(wù)端允許客戶端再以后的 SSL 連接中復(fù)用這次會(huì)話。在使用 HTTPS 的 Session Ticket 可以用到。會(huì)話 ID 由服務(wù)端維持,采用恢復(fù)會(huì)話的方式創(chuàng)建 SSL 連接。
Session ID: 7a92546002c514f9a0b11ef585935c7cc5182d9db3ef0db3... -
加密套件 ,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA。這個(gè)是從客戶端 Client Hello 上傳的 20 個(gè)加密套件中選中的,根據(jù)密碼套件的格式,上面的信息有,交換加密算法為
ECDHE,就是EC Diffie-Hellman,RSA 表示后面 Server Key Exchange 階段的攜帶 DH 加密算法的公鑰的包的數(shù)字簽名的加密算法是 RSA。加密算法為
AES,最高密鑰支持 128 位,使用 CBC 分組。認(rèn)證算法
SHA。所謂 CBC 就是 AES 的機(jī)密模式,為分組加密。ECDHE_RSA,表示交換加密算法為 ,RSA是后面的 獲取ECDHE的參數(shù)的包進(jìn)行的數(shù)字簽名用的算法。 壓縮方法 ,這里為 0,表示不使用壓縮算法。
Certificate
上面的 Server Hello 已經(jīng)制定了接下來(lái)的非對(duì)稱加密算法
服務(wù)端下發(fā)證書,客戶端驗(yàn)證服務(wù)端的身份,并且取出證書攜帶的公鑰,這個(gè)公鑰是交換加密算法的公鑰。也就是在 Server Hello 階段指定的 ECDHE (EC Diffie-Hellman)算法,也是通常說(shuō)的 DH 加密。
這個(gè) Certificate 消息下發(fā)了從攜帶自己公鑰的數(shù)字證書和 CA 證書的證書鏈,在 Certificates 字段中:
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Certificate
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 2403
Handshake Protocol: Certificate
Handshake Type: Certificate (11)
Length: 2399
Certificates Length: 2396
Certificates (2396 bytes)
Certificate Length: 1283
Certificate: 308204ff308203e7a0030201020210248b4dd17f3ef11a77...
Certificate Length: 1107
Certificate: 3082044f30820337a0030201020203023a6f300d06092a86...
CA 是 PKI 體系的重要組成部分,稱為認(rèn)證機(jī)構(gòu)。
那什么是 CA 證書?就是用來(lái) CA 中心發(fā)布的,認(rèn)證該服務(wù)單證書的合法性,可以確保該證書來(lái)源可靠而不是被中間人替換了。但是 CA 證書也可能被中間人攔截造假?那就再用一個(gè)證書來(lái)認(rèn)證它??雌饋?lái)好像沒(méi)完沒(méi)了。實(shí)際上到最后有一個(gè)根 CA 證書,這個(gè)證書存儲(chǔ)再瀏覽器或者操作系統(tǒng)中,是系統(tǒng)直接信任的。
服務(wù)端證書需要 CA 證書做認(rèn)證。使用的還是數(shù)字簽名方式,從數(shù)據(jù)中摘要一段信息,用 CA 證書的加密。然后驗(yàn)證的時(shí)候時(shí)候,用 CA 證書的公鑰解密,用同樣的摘要算法摘要數(shù)據(jù)部分和解密好的信息進(jìn)行比較。

客戶端在驗(yàn)證服務(wù)端證書的有效性有這樣的一個(gè)過(guò)程。首先會(huì)找到該證書的認(rèn)證證書,也就是中級(jí) CA 證書。然后找中級(jí) CA 證書的認(rèn)證證書,可以是另一個(gè)中級(jí) CA 證書,也可能是根 CA 證書。這樣直到根 CA 證書。
接著從根 CA 證書開始往下去驗(yàn)證數(shù)字簽名。比如有這樣的證書鏈:根 CA 證書-> 中級(jí) CA 證書 -> 服務(wù)端證書。用 CA 證書的公鑰去驗(yàn)證中級(jí)證書的數(shù)字簽名,再用中級(jí)證書的公鑰去驗(yàn)證服務(wù)器證書的數(shù)字簽名。任何一個(gè)環(huán)節(jié)驗(yàn)證失敗,就可以認(rèn)為證書不合法。
這就是整個(gè)證書鏈的認(rèn)證過(guò)程:

查看抓到的包的數(shù)據(jù),發(fā)現(xiàn)只有兩個(gè)證書。為服務(wù)端證書和中級(jí) CA 證書。根 CA 證書呢?順藤摸瓜找到它。
首先看服務(wù)端證書。它內(nèi)容如下:
Certificate: 308204ff308203e7a0030201020210248b4dd17f3ef11a77...
signedCertificate
version: v3 (2)
serialNumber: 0x248b4dd17f3ef11a7733fead4cd68c21
signature (sha256WithRSAEncryption)
issuer: rdnSequence (0)
rdnSequence: 3 items
RDNSequence item: 1 item (id-at-countryName=US)
RDNSequence item: 1 item (id-at-organizationName=GeoTrust Inc.)
RDNSequence item: 1 item (id-at-commonName=GeoTrust SSL CA - G3)
validity
notBefore: utcTime (0)
utcTime: 15-10-14 00:00:00 (UTC)
notAfter: utcTime (0)
utcTime: 17-12-30 23:59:59 (UTC)
subject: rdnSequence (0)
rdnSequence: 6 items
RDNSequence item: 1 item (id-at-countryName=CN)
RDNSequence item: 1 item (id-at-stateOrProvinceName=Zhejiang)
RDNSequence item: 1 item (id-at-localityName=Hangzhou)
RDNSequence item: 1 item (id-at-organizationName=NetEase (Hangzhou) Network Co., Ltd)
RDNSequence item: 1 item (id-at-organizationalUnitName=MAIL Dept.)
RDNSequence item: 1 item (id-at-commonName=*.163.com)
subjectPublicKeyInfo
algorithm (rsaEncryption)
Padding: 0
subjectPublicKey: 3082010a0282010100ce9da6fb3a940ae04a939b85a1a961...
extensions: 8 items
Extension (id-ce-subjectAltName)
Extension (id-ce-basicConstraints)
Extension (id-ce-keyUsage)
Extension (id-ce-cRLDistributionPoints)
Extension (id-ce-certificatePolicies)
Extension (id-ce-extKeyUsage)
Extension (id-ce-authorityKeyIdentifier)
Extension (id-pe-authorityInfoAccessSyntax)
algorithmIdentifier (sha256WithRSAEncryption)
Padding: 0
encrypted: ad75f99ff40e582bc9a17c01c98edf02696aa4f821dfe870...
從這個(gè)證書中我們可以窺見(jiàn)這些信息:
首先是 signedCertificate 字段的內(nèi)容,即數(shù)字證書的數(shù)據(jù):
版本,version,v3。對(duì)應(yīng)的就是 X.509 V3 標(biāo)準(zhǔn)。
序列號(hào) ,serialNumber,0x248...,證書頒發(fā)者唯一序列號(hào)。
簽名算法ID ,Signature Algorithm。這里指的是使用 SHA-256 進(jìn)行摘要,RSA 進(jìn)行加密的簽名算法。
證書頒發(fā)者 ,issuer,就是頒發(fā)該證書的 CA 的信息。里面攜帶后該 CA 的唯一名稱(DN,Distinguished Name),比如國(guó)家為 US(美國(guó)),組織機(jī)構(gòu)為 GeoTrust Inc.,名稱為 GeoTrust SSL CA - G3。后面我們需要從證書鏈找到該 CA 證書,去認(rèn)證當(dāng)前證書。
有效期 ,validity,證書的起始時(shí)間和終止時(shí)間。可以得出該證書到 17 年 12 月 30 日后過(guò)期。
對(duì)象名稱 ,subject,里面就是該證書的名稱等主要信息了。比如國(guó)家為 CN(中國(guó)),組織為 NetEase (Hangzhou) Network Co., Ltd。名稱為
*.163.com,也是該證書適用的域名。對(duì)象公鑰信息 ,subjectPublicKeyInfo。因?yàn)檫@是服務(wù)端證書,這個(gè)公鑰后面將用于主密鑰的交換過(guò)程,從中可以了解到這個(gè)公鑰采用 RSA 加密,公鑰內(nèi)容為 3082010a0282010100ce9da6fb3a940ae04a939b85a1a961...
-
擴(kuò)展部分 ,一些擴(kuò)展信息。比如對(duì)象的別名。這個(gè)如果是 CDN 的服務(wù)器證書,那么別名將會(huì)非常多。是所有使用該 CDN 的網(wǎng)站的域名。比如之前抓到的 CDNetworks 的 CDN 證書,它的擴(kuò)展字段 subjectAltName 有這些信息:
Certificate: 308220a630821f8ea00302010202100b8a2d407d6121375c... signedCertificate version: v3 (2) serialNumber: 0x0b8a2d407d6121375cba2fac96354a41 signature (sha256WithRSAEncryption) issuer: rdnSequence (0) validity subject: rdnSequence (0) rdnSequence: 5 items RDNSequence item: 1 item (id-at-countryName=US) RDNSequence item: 1 item (id-at-stateOrProvinceName=California) RDNSequence item: 1 item (id-at-localityName=Campbell) RDNSequence item: 1 item (id-at-organizationName=CDNetworks Inc.) RDNSequence item: 1 item (id-at-commonName=support13.cdnetworks.net) subjectPublicKeyInfo extensions: 10 items Extension (id-ce-authorityKeyIdentifier) Extension (id-ce-subjectKeyIdentifier) Extension (id-ce-subjectAltName) Extension Id: 2.5.29.17 (id-ce-subjectAltName) GeneralNames: 314 items GeneralName: dNSName (2) dNSName: support13.cdnetworks.net GeneralName: dNSName (2) dNSName: china.ray-ban.com GeneralName: dNSName (2) dNSName: static1.read.ru GeneralName: dNSName (2) dNSName: ps4.cache.square-enix.co.jp ... Extension (id-ce-keyUsage) Extension (id-ce-extKeyUsage) Extension (id-ce-cRLDistributionPoints) Extension (id-ce-certificatePolicies) Extension (id-pe-authorityInfoAccessSyntax) Extension (id-ce-basicConstraints) Extension (iso.3.6.1.4.1.11129.2.4.2) algorithmIdentifier (sha256WithRSAEncryption) Padding: 0 encrypted: 68728010fd9e4b7e3bdbe5e47ec680330b6851e1ea4dc737...可以了解到該 CDN 為 314 個(gè)域名提供服務(wù)。
然后是證書頒發(fā)機(jī)構(gòu)的簽名信息:
- 簽名算法,algorithmIdentifier。這里得出使用的還是 SHA-256 摘要加 RSA 加密的簽名算法。這個(gè)就是認(rèn)證該證書的 CA 證書使用的簽名算法。
- 簽名信息 ,encrypted,這個(gè)信息的內(nèi)容,CA 證書對(duì) SHA-256 對(duì)上面的數(shù)據(jù)部分進(jìn)行摘要后,使用 RSA 的私鑰加密獲得。后面會(huì)用在該證書的認(rèn)證過(guò)程,取出 CA 證書的公鑰,解密簽名信息,用同樣的算法獲取數(shù)據(jù)摘要,對(duì)比一下是否相同。
從上面的 issuer 可以了解到,認(rèn)證該服務(wù)器證書的 CA 證書為 GeoTrust SSL CA - G3 ,我們從 Certificates 找到對(duì)應(yīng)的中級(jí)證書的內(nèi)容如下(中級(jí)證書可以有好幾級(jí),我們這兒只有一級(jí)):
Certificate: 3082044f30820337a0030201020203023a6f300d06092a86...
signedCertificate
version: v3 (2)
serialNumber: 146031
signature (sha256WithRSAEncryption)
issuer: rdnSequence (0)
rdnSequence: 3 items
RDNSequence item: 1 item (id-at-countryName=US)
RDNSequence item: 1 item (id-at-organizationName=GeoTrust Inc.)
RDNSequence item: 1 item (id-at-commonName=GeoTrust Global CA)
validity
notBefore: utcTime (0)
utcTime: 13-11-05 21:36:50 (UTC)
notAfter: utcTime (0)
utcTime: 22-05-20 21:36:50 (UTC)
subject: rdnSequence (0)
rdnSequence: 3 items
RDNSequence item: 1 item (id-at-countryName=US)
RDNSequence item: 1 item (id-at-organizationName=GeoTrust Inc.)
RDNSequence item: 1 item (id-at-commonName=GeoTrust SSL CA - G3)
subjectPublicKeyInfo
algorithm (rsaEncryption)
Padding: 0
subjectPublicKey: 3082010a0282010100e3be7e0a86a3cf6b6d3d2ba197ad49...
extensions: 8 items
Extension (id-ce-authorityKeyIdentifier)
Extension (id-ce-subjectKeyIdentifier)
Extension (id-ce-basicConstraints)
Extension (id-ce-keyUsage)
Extension (id-ce-cRLDistributionPoints)
Extension (id-pe-authorityInfoAccessSyntax)
Extension (id-ce-certificatePolicies)
Extension (id-ce-subjectAltName)
algorithmIdentifier (sha256WithRSAEncryption)
Padding: 0
encrypted: a0d4f72cfb740b7f64f1cd436a9f62531c027c9890a2ee4f...
可以得到中級(jí)證書名為 GeoTrust SSL CA - G3 ,證書組織為 GeoTrust Inc. 。
認(rèn)證該 CA 證書的證書呢?還是看 issue 字段,認(rèn)證證書名為 GeoTrust Global CA ,組織同樣是 GeoTrust Inc. 。
其實(shí)這個(gè)就是根 CA 證書。在這個(gè)請(qǐng)求中沒(méi)有找到,但在瀏覽器或者操作系統(tǒng)可以找到。一般的瀏覽器和系統(tǒng)都會(huì)內(nèi)置該 CA 證書。所以根證書是受瀏覽器或者操作系統(tǒng)信任的,無(wú)需其他證書做擔(dān)保。
如果想要自己的系統(tǒng)再信任某些非通用的權(quán)威機(jī)構(gòu)的根 CA 證書,那么就去安裝它。
比如我的 Windows 系統(tǒng)就安裝了 GeoTrust Global CA 證書:

像我們平時(shí)使用 Charles 抓 HTTPS 就是這個(gè)原理,把 Charles 的 CA 證書安裝在手機(jī)中,成為受信任的根 CA 證書。
基本原理就是,Charles 代理作為 SSL 隧道,并沒(méi)有透明傳輸,而是作為一個(gè)中間人,攔截了 SSL 握手信息,修改里面的 CA 證書。仿冒手機(jī)端和真實(shí)服務(wù)端建立連接獲取主密鑰,然后又仿冒服務(wù)端和手機(jī)客戶端建立 SSL 連接,修改服務(wù)端證書的 CA 和數(shù)字簽名,這樣 Charles 就可以解析到加密的 HTTP 內(nèi)容了。
修改后的服務(wù)端證書如下,可以看到 issuer 被替換成了 Charles 的證書。
Certificate: 30821ff230821edaa0030201020206015e7406ab1c300d06...
signedCertificate
version: v3 (2)
serialNumber: 1505185147676
signature (sha256WithRSAEncryption)
issuer: rdnSequence (0)
rdnSequence: 6 items
RDNSequence item: 1 item (id-at-commonName=Charles Proxy Custom Root Certificate)
RDNSequence item: 1 item (id-at-organizationalUnitName=http://charlesproxy.com/ssl)
RDNSequence item: 1 item (id-at-organizationName=XK72 Ltd)
RDNSequence item: 1 item (id-at-localityName=Auckland)
RDNSequence item: 1 item (id-at-stateOrProvinceName=Auckland)
RDNSequence item: 1 item (id-at-countryName=NZ)
validity
subject: rdnSequence (0)
rdnSequence: 5 items
RDNSequence item: 1 item (id-at-countryName=US)
RDNSequence item: 1 item (id-at-stateOrProvinceName=California)
RDNSequence item: 1 item (id-at-localityName=Campbell)
RDNSequence item: 1 item (id-at-organizationName=CDNetworks Inc.)
RDNSequence item: 1 item (id-at-commonName=support13.cdnetworks.net)
subjectPublicKeyInfo
extensions: 7 items
algorithmIdentifier (sha256WithRSAEncryption)
Padding: 0
encrypted: da491fc58682c7b85751db9def0b366d58cf09755ab8ef7d...
到這個(gè)階段,我們有了一個(gè)小想法,是不是可以自己搞個(gè)根 CA ,然后推廣到各個(gè)設(shè)備上使用?
理論上可以。但是推廣成本太高,市場(chǎng)上被大多數(shù)系統(tǒng)認(rèn)可的就這幾家機(jī)構(gòu):
- Symantec(VeriSign/GeoTrust)
- Comodo
- GoDaddy
像 BAT 這樣的大廠也需要買它們的證書。像我這次抓的是網(wǎng)易的包,使用的也是 GeoTrust 。不過(guò)也有例外,我們的 12306 比較任性,就沒(méi)有購(gòu)買這些機(jī)構(gòu)的證書,所以上這個(gè)網(wǎng)站在 Chrome 等瀏覽器經(jīng)常會(huì)彈出不受信任等等。
Server Key Exchange
密鑰交換階段,這個(gè)步驟是可選步驟,對(duì) Certificate 階段的補(bǔ)充,只有在這幾個(gè)場(chǎng)景存在:
- 協(xié)商采用了 RSA 加密,但是服務(wù)端證書沒(méi)有提供 RSA 公鑰。
- 協(xié)商采用了 DH(EC Diffie-Hellman) 加密,但是服務(wù)端證書沒(méi)有提供 DH 參數(shù)。
- 協(xié)商采用 fortezza_kea 加密,但是服務(wù)端證書沒(méi)有提供參數(shù)。
我們滿足了哪一個(gè)場(chǎng)景?
可以知道我們前面協(xié)商了使用 EC Diffie-Hellman 算法,而且沒(méi)帶參數(shù),所以這個(gè)包就是服務(wù)端帶過(guò)來(lái)的用來(lái)協(xié)商 DH 密鑰參數(shù)的。
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 333
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 329
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: secp256r1 (0x0017)
Pubkey Length: 65
Pubkey: 04a10ad7a23135095205caf7ca8e4c838728e877dbcb23c3...
Signature Hash Algorithm: 0x0601
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: RSA (1)
Signature Length: 256
Signature: 8c7c51f60574144e9e1385a534e12f85911e8dc7cd40dc04...
這個(gè)包把 DH 算法需要的公鑰給傳遞過(guò)來(lái)了,即 Pubkey: 04a10ad7a23135095205caf7ca8e4c838728e877dbcb23c3...
同樣這個(gè)包也攜帶了數(shù)字簽名 Signature: 8c7c51f60574144e9e1385a534e12f85911e8dc7cd40dc04... ,用服務(wù)端證書帶過(guò)來(lái)的公鑰驗(yàn)證一下完整性和來(lái)源。
Server Hello Done
通知客戶端,版本和加密套件協(xié)商結(jié)束。
TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 4
Handshake Protocol: Server Hello Done
Handshake Type: Server Hello Done (14)
Length: 0
這個(gè) Server Hello Done,就像 TCP 協(xié)議的 ACK 確認(rèn)包一樣,這里服務(wù)端也給了個(gè)確認(rèn)的信息,通知客戶端已經(jīng)做好進(jìn)入下一個(gè)階段的準(zhǔn)備。
通過(guò) Wireshark 抓包發(fā)現(xiàn)了一個(gè)現(xiàn)象,就是 Server Key Exchange 和 Server Hello Done 被放到了同一個(gè) SSL 記錄協(xié)議中,這是因?yàn)?SSL 記錄協(xié)議具有組合功能??蛻舳耸盏竭@樣的包后,會(huì)處理成兩個(gè)單獨(dú)的協(xié)議包,這又是 SSL 記錄協(xié)議的分組功能。
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done
這樣做的好處,可以減少發(fā) TCP 包的次數(shù),減少 SSL 握手的時(shí)間。
2.3.4. 階段三
如果在一些安全級(jí)別高的場(chǎng)景,服務(wù)端也會(huì)要求客戶端上報(bào)證書,會(huì)有 Certificate Request 的 SSL 握手報(bào)文。這樣的情況下,接下來(lái)會(huì)有完整的客戶端證書上報(bào)服務(wù)端的流程。整個(gè)流程和階段二類似。
2.3.5. 階段四
因?yàn)槲覀冞@里只需要驗(yàn)證服務(wù)端的證書,所以直接進(jìn)入階段四,開始最后的握手。這個(gè)階段的主要目的,就是生產(chǎn)加密密鑰,并進(jìn)行安全傳輸。
Client Key Exchange
這里,客戶端不直接生成加密密鑰,而是通過(guò)之前客戶端和服務(wù)端生成的隨機(jī)數(shù)又再生成一個(gè)隨機(jī)數(shù),使用前面協(xié)商好的用 EC Diffie-Hellman 算法進(jìn)行加密傳輸給服務(wù)端。這個(gè)值又被稱為 “premaster secret“。
TLSv1.2 Record Layer: Handshake Protocol: Client Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 70
Handshake Protocol: Client Key Exchange
Handshake Type: Client Key Exchange (16)
Length: 66
EC Diffie-Hellman Client Params
Pubkey Length: 65
Pubkey: 0433cfbd121d0fa5299819604a15237fc9359845a2a9dffe...
服務(wù)端收到這個(gè)報(bào)文后,會(huì)使用自己的私鑰解開這個(gè)隨機(jī)數(shù)。
在這個(gè)階段過(guò)后,服務(wù)端和客戶端都有三個(gè)隨機(jī)數(shù):客戶端隨機(jī)數(shù)、服務(wù)端隨機(jī)數(shù)和預(yù)備主密鑰。
在服務(wù)端收到了 Client Key Exchange 消息后,兩端都按照相應(yīng)的算法生成了主密鑰,加密密鑰交換完成。
交換完了,因?yàn)橹髅荑€是兩個(gè)端按照約定好的算法產(chǎn)生的,如何保證這個(gè)主密鑰是正確的?
這時(shí)候會(huì)進(jìn)入下一個(gè)階段??蛻舳撕头?wù)端會(huì)對(duì)握手信息使用 SHA 做個(gè)摘要,用 AES 加密算法和主密鑰加密,傳遞給對(duì)方驗(yàn)證。這種方式也稱為消息認(rèn)證。就是下面的過(guò)程:
Change Cipher Spec(Client)
客戶端通知服務(wù)端,后續(xù)的報(bào)文將會(huì)被加密。
Encrypted Handshake Message(Client)
這里就是客戶端的 Client Finished 消息。
也是整個(gè) SSL 過(guò)程中,發(fā)送給服務(wù)端的第一個(gè)加密消息。
服務(wù)端接收后,服務(wù)端用同樣的方式計(jì)算出已交互的握手消息的摘要,與用主密鑰解密后的消息進(jìn)行對(duì)比,一致的話,說(shuō)明兩端生成的主密鑰一致,完成了密鑰交換。
Change Cipher Spec(Server)
服務(wù)端通知客戶端,后續(xù)的報(bào)文將會(huì)被加密。
Encrypted Handshake Message(Server)
這里就是服務(wù)端的 Server Finish 消息。
和上面的客戶端的 Encrypted Handshake Message 一樣,是服務(wù)端發(fā)出的第一條加密信息。
客戶端按照協(xié)商好的主密鑰解密并驗(yàn)證正確后,SSL 握手階段完成。
2.3.6. 小結(jié)
整個(gè) SSL 握手主要是要完成這幾個(gè)目標(biāo):
- 對(duì)服務(wù)端身份的驗(yàn)證,或者客戶端
- 協(xié)商好對(duì)稱加密算法和對(duì)稱加密密鑰
對(duì)應(yīng) Java API 為 SSLSocket :
sslSocket.startHandshake();
這次請(qǐng)求的整個(gè)過(guò)程耗時(shí)大約為 380ms??梢钥闯?,SSL 握手是很消耗請(qǐng)求時(shí)間的。所以對(duì)握手進(jìn)行優(yōu)化,比如使用 Session ID 或者 Session Ticket。這個(gè)類似于 HTTP 協(xié)議的 Session 和 Cookie 的使用。
2.4. 數(shù)據(jù)傳輸
經(jīng)過(guò)了 SSL 握手后,服務(wù)端的身份認(rèn)證成功,協(xié)商出了加密算法為 AES,密鑰為 xxxxx(客戶端和服務(wù)端拿三個(gè)隨機(jī)值用相同算法計(jì)算出來(lái)的,并沒(méi)有明文傳輸)。一切準(zhǔn)備就緒。
SSL 握手成功,已經(jīng)可以對(duì)接下來(lái)的數(shù)據(jù)加密了,接下來(lái)各種應(yīng)用層協(xié)議都可以加密傳輸。
2.4.1. Application Data
應(yīng)用數(shù)據(jù)傳輸消息。因?yàn)檫@里是 HTTPS,所以可以對(duì) HTTP 應(yīng)用協(xié)議數(shù)據(jù)加密然后傳輸了。
Secure Sockets Layer
TLSv1.2 Record Layer: Application Data Protocol: http
Content Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 1072
Encrypted Application Data: 6d9b3c9089271630c33506fe28cd6a61fed1f4bd2808f537...
從這里,不知道密鑰是無(wú)法知道這里傳輸?shù)氖鞘裁磾?shù)據(jù),連傳輸?shù)氖鞘裁磪f(xié)議的內(nèi)容都不知道。
所以之前創(chuàng)建 SSL 隧道,讓代理服務(wù)器盲傳 HTTPS 數(shù)據(jù),就得通過(guò) CONNECT 方法告訴代理服務(wù)器要連哪臺(tái)主機(jī),哪個(gè)端口號(hào),要不然代理服務(wù)器也是一臉懵逼。
所以 SSL 協(xié)議是很獨(dú)立的,這里是對(duì) HTTP 進(jìn)行了加密,也可以對(duì)其他協(xié)議進(jìn)行加密。它就像是 TCP 和應(yīng)用層協(xié)議的中間層,為上層協(xié)議提供了加密的數(shù)據(jù)傳輸。
2.4.2. Encryted Alert
SSL 警告消息,因?yàn)槭羌用艿膬?nèi)容,所以單從 Wireshark 看不出警報(bào)的內(nèi)容。
Secure Sockets Layer
TLSv1.2 Record Layer: Encrypted Alert
Content Type: Alert (21)
Version: TLS 1.2 (0x0303)
Length: 48
Alert Message: Encrypted Alert
但因?yàn)榫瘓?bào)消息經(jīng)常只是客戶端用來(lái)提示服務(wù)端 SSL 傳輸結(jié)束,對(duì)照抓包到的內(nèi)容確實(shí)如此。所以這里只是 SSL 傳輸結(jié)束的一個(gè)信號(hào)。
發(fā)出了 Encryted Alert 后客戶端數(shù)據(jù)傳輸完畢,準(zhǔn)備進(jìn)入四次揮手?jǐn)嚅_ TCP 連接。
2.5. 四次揮手
客戶端發(fā)送完 HTTP 的數(shù)據(jù),也正確地獲取到服務(wù)端的響應(yīng)。完成了 HTTPS 的請(qǐng)求工作后,接下來(lái)要關(guān)閉 TCP 連接。關(guān)閉 TCP 連接一共分成四步,也可以成為四次揮手。對(duì)應(yīng)包如下:
| No. | Time | Source | Destionation | Protocol | Length | Info |
|---|---|---|---|---|---|---|
| 688 | 7.607349 | 172.17.32.211 | 172.17.32.19 | TCP | 66 | 35973 → 8888 [FIN, ACK] Seq=1657 Ack=3213 Win=96512 Len=0 TSval=15986483 TSecr=59355534 |
| 689 | 7.607363 | 172.17.32.19 | 172.17.32.211 | TCP | 66 | 8888 → 35973 [ACK] Seq=3213 Ack=1658 Win=65024 Len=0 TSval=59355763 TSecr=15986483 |
| 690 | 7.620494 | 172.17.32.19 | 172.17.32.211 | TCP | 66 | 8888 → 35973 [FIN, ACK] Seq=3213 Ack=1658 Win=65024 Len=0 TSval=59355764 TSecr=15986483 |
| 697 | 7.661600 | 172.17.32.211 | 172.17.32.19 | TCP | 66 | 35973 → 8888 [ACK] Seq=1658 Ack=3214 Win=96512 Len=0 TSval=15986494 TSecr=59355764 |
可以看到,這四個(gè)包都只有 66 個(gè)字節(jié)。因?yàn)椴粠?shù)據(jù),是純粹的 TCP 首部。
2.5.1. Round 1
客戶端 A 發(fā)出 FIN + ACK 包,通知服務(wù)端數(shù)據(jù)傳輸結(jié)束,可以關(guān)閉連接了。同樣這是一個(gè) ACK 確認(rèn)包,指出希望的下一個(gè)包的序號(hào)為 3213。
這個(gè)階段后,客戶端進(jìn)入 FIN_WAIT_1 狀態(tài),不再發(fā)送數(shù)據(jù)給服務(wù)端,但是如果服務(wù)端還在傳數(shù)據(jù)過(guò)來(lái),客戶端的 ACK 確認(rèn)報(bào)文還會(huì)有。
服務(wù)端進(jìn)入 CLOSE_WAIT 狀態(tài)。這個(gè)狀態(tài)再發(fā)出 ACK 確認(rèn)包后解除。
2.5.2. Round 2
接收端 B 收到第一次揮手的包后,會(huì)先給一個(gè) ACK 確認(rèn)包,為第二次揮手。
這里有個(gè)疑問(wèn),既然收到了 A 的結(jié)束信息,為什么不馬上結(jié)束呢?因?yàn)?A 完成數(shù)據(jù)傳輸,但是 B 可能還有數(shù)據(jù)沒(méi)有傳完,所以比三次握手會(huì)多一個(gè)步驟。
收到客戶端的 FIN 包后,已經(jīng)知道客戶端結(jié)束數(shù)據(jù)傳輸了,所以服務(wù)端后面數(shù)據(jù)傳輸結(jié)束,就可以直接通知客戶端可以結(jié)束了。
客戶端收到 ACK 確認(rèn)包后,進(jìn)入 FIN_WAIT_2 狀態(tài)。
2.5.3. Round 3
如果服務(wù)端數(shù)據(jù)還沒(méi)有傳完,會(huì)繼續(xù)傳給客戶端。
等服務(wù)端的數(shù)據(jù)完全傳完后,會(huì)再發(fā)一個(gè) FIN + ACK 包,通知客戶端數(shù)據(jù)傳完了。
這個(gè)階段后,服務(wù)端進(jìn)入 LAST_ACK 狀態(tài),即等待客戶端最后一個(gè) ACK 報(bào)文。
2.5.4. Round 4
發(fā)送端 A 收到 B 發(fā)出的 FIN + ACK 后,進(jìn)入 TIME_WAIT 狀態(tài)。服務(wù)端收到 FIN 后進(jìn)入 CLOSED 狀態(tài)關(guān)閉連接。
經(jīng)過(guò) 2MSL 時(shí)間,沒(méi)有問(wèn)題后會(huì)關(guān)閉連接。也進(jìn)入 CLOSED 狀態(tài)。
為什么有個(gè) TIME_WAIT ?
原因是有可能服務(wù)端一直沒(méi)有收到 FIN + ACK,有可能觸發(fā)超時(shí)重傳,又發(fā)了一個(gè) FIN 給客戶端,客戶端要重新發(fā)送最后一個(gè)包。
2.5.5. 小結(jié)
TCP 四次揮手的時(shí)序圖如下:
