wireshark 抓HTTPS 的包 HTTPS = TLS + HTTP TLSv1.2 協議


今天我抓了個 HTTPS 的包

 

之前寫過一篇講 HTTPS 的思想的文章。

破玩意 | 用 HTTPS 傳紙條

后來又寫了篇用更凝練的語言總體描述了 HTTPS 的主干。

叮咚 | HTTPS 的分支和主干

想必通過這兩篇文章,HTTPS 為什么要這么設計,以及它是用來解決什么問題的,大家已經心中有數了。

那接下來就是細節了。

由於之前已經把思想講過了,本篇就通過抓包的方式,專注於 HTTPS 的過程,我會像個無情的流水賬一樣,給大家把一個 HTTPS 的包挖干凈。

先普及一個知識點,HTTPS = TLS + HTTP。

HTTP 我們很熟悉了,所以我們想知道的 HTTPS 的知識,本質上是想知道 TLS 協議的規范,以及為什么這樣設計。所以我們本文展開講解 TLSv1.2 協議的內容。

我們開始吧!

直接 postman 發起一個 HTTPS 的 POST 請求,這個 IP 是微博首頁。

wireshark 抓包,過濾出 tls 協議的包,看到如下結果。

一下就可以看到整個 HTTPS 握手的過程了。

這個抓包數據可以加我好友,朋友圈有下載鏈接。但其實你自己隨便訪問一個網站用 wireshark 抓一下也行。

學一個協議,最科學也是最方便的辦法,就是看官方文檔。

我們看 TLS 1.2 的官方文檔,RFC-5246,其中 section-7.3 為我們描繪了整個握手過程。

由於我們只是客戶端驗證服務端證書,而沒有服務端驗證客戶端證書的過程,所以我們的包里,是不包含 Server CertificateRequest(請求客戶端證書)、Client Certificate(客戶端證書)、CertificateVerify(客戶端證書有效性驗證)這三項的。

下面我們一個一個過程拆開來看。

 

ClientHello




 

當 client 連接到一個 server 時,第一個發送的包就是它。

具體包含以下內容:

client_version

客戶端希望的,也是客戶端能支持的最高的 TLS 版本號,這里是 TLS 1.2 版本。

random

由客戶端生成的隨機數,之后會用到,我們稱之為隨機數 1。

ee8880e816ac14ca5b69bde656c188f37a08bcf2052a550b7867b041f6c1ab48

session_id

用於復用 TLS 連接,防止資源的浪費。但這個要服務端支持才行。

cipher_suites

客戶端支持的密碼學套件,按客戶端偏好排序,如果服務端沒有可支持的,那就回應錯誤(returns a handshake failure alert)並關閉連接。

本次一共發了 18 個密碼學套件

我們拿其中一個密碼學套件舉例

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

我們知道,HTTPS 的原理就是用非對稱加密的方式來交換秘鑰,用對稱加密的方式來通信,然后里面夾雜着哈希算法用於驗證簽名等。

所以這個密碼學套件就包含了這三個部分。

ECDHE_RSA 指的是秘鑰協商算法AES_128_GCM 是最終通信的對稱加密算法SHA256 是哈希算法

以此來確定整個握手過程所需要的算法都用什么。

compression_methods

壓縮算法

extensions

擴展字段

由此看出,本次 clientHello 最重要的信息就倆,一個隨機數,一組支持的密碼學套件。

接着往下看

 

ServerHello




 

服務端給客戶端發送的包,響應 ClientHello。

 具體包含以下內容:

server_version

TLS 的版本,具體是服務端支持的最高版本以及客戶端支持的最低版本。

random

服務端生成的隨機數,且生成規則不能依賴於客戶端的隨機數,我們稱為隨機數 2。

3ad03af5b8a5ebfe7902a250406b2e99d2667e37e524e0e5c333c0e0b9a637e8

session_id

服務端返回的會話 ID。

如果客戶端剛剛發過來的 session_id 服務端已經有了緩存,並且同意復用連接,則返回一個和客戶端剛剛發來的相同的 session_id。

也可以發送一個新的 session_id,以便客戶端下次將其攜帶並且復用。

也可以回復一個空值,表示不緩存 session_id,因此也不會復用。

cipher_suite

選擇的加密套件。

剛剛客戶端傳來 18 個加密套件,服務端選擇了一個回應,此處回應的是。

0xc02f

表示

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

這個剛剛解釋過了,表示用 ECDHE_RSA 作為秘鑰交換算法,用 AES_128 作為通信時的對稱加密算法,用 SHA256 作為哈希算法。 

compression_method

選擇的壓縮算法,同上

extensions

擴展字段,同上

由此看出,本次 serverHello 最重要的信息也是倆,和上面的 clientHello 一樣,也是一個隨機數,還有一個從客戶端發來的一組密碼學套件中選擇的一個。

至此,服務端和客戶端都擁有了隨機數 1 和隨機數 2,並且選定了共同確定的密碼學套件。

雙方互相 hello 的過程就此結束了。

接着往下看

 

Server Certificate




 

服務端發完上面的 ServerHello 后立即發這個包,這個包非常簡單。

只有一個 Certificates 結構體,就是我們常說的證書。

而后綴還加了個 s,因此翻譯成證書鏈,是由一組證書組成的,最上面的是服務端本身的證書。

我們把這個服務端的證書的關鍵信息都展開看一下:

這個證書在默認情況下都是 X.509 格式的,除非明確協商說明。

別懷疑,RFC-5246 中就是這樣寫的。

這種格式的結構定義在 RFC-1422 的 3.3 節中有說明。

別廢話了,展開講一下吧。

version

證書版本號,v3

serial number

證書序列號,這個每個頒發機構是唯一的,此處為:

0x0de81066db219caef5ecb01ba273cad1

signature

簽名算法,僅僅是一個算法喲。

此處是 1.2.840.113549.1.1.11

表示 sha256WithRSAEncryption

這就表示用 sha256 這個哈希算法對證書進行哈希生成摘要,然后再用 RSA 這個非對稱加密算法,用 CA 的私鑰加密剛剛生成的摘要,形成數字簽名。

issuer name

頒發者信息,我們展開看一下

RDNSequence item: item (id-at-countryName=US) RDNSequence item: item (id-at-organizationName=DigiCert Inc) RDNSequence item: item (id-at-organizationalUnitName=www.digicert.com) RDNSequence item: item (id-at-commonName=GeoTrust CN RSA CA G1)

 

validity period

證書有效期,比如本案例中的

notBefore: utcTime (0)     utcTime: 20-06-09 00:00:00 (UTC) notAfter: utcTime (0)     utcTime: 22-05-15 12:00:00 (UTC)

 

subject name

證書持有者信息

rdnSequence: items     RDNSequence item: item (id-at-countryName=CN)         RelativeDistinguishedName item (id-at-countryName=CN)             Id: 2.5.4.6 (id-at-countryName)             CountryName: CN     RDNSequence item: item (id-at-stateOrProvinceName=Beijing)         RelativeDistinguishedName item (id-at-stateOrProvinceName=Beijing)             Id: 2.5.4.8 (id-at-stateOrProvinceName)             DirectoryString: printableString (1)     RDNSequence item: item (id-at-organizationName=Sina.com Technology(China)Co.,ltd)         RelativeDistinguishedName item (id-at-organizationName=Sina.com Technology(China)Co.,ltd)             Id: 2.5.4.10 (id-at-organizationName)             DirectoryString: printableString (1)     RDNSequence item: item (id-at-organizationalUnitName=Sina.com Technology(China)Co.,ltd)         RelativeDistinguishedName item (id-at-organizationalUnitName=Sina.com Technology(China)Co.,ltd)             Id: 2.5.4.11 (id-at-organizationalUnitName)             DirectoryString: printableString (1)     RDNSequence item: item (id-at-commonName=weibo.cn)         RelativeDistinguishedName item (id-at-commonName=weibo.cn)             Id: 2.5.4.3 (id-at-commonName)             DirectoryString: printableString (1)                 printableString: weibo.cn 

太亂了,簡化下就是

countryName=CN
stateOrProvinceName=Beijing
organizationName=Sina.com Technology(China)Co.,ltd
organizationalUnitName=Sina.com Technology(China)Co.,ltd
commonName=weibo.cn

顧名思義,就是微博網站這個頒發機構的信息

 

subject public key

證書的公鑰信息,本案例中是:

3082010a0282010100c4c84ff479214c5875037500cfc453d676cec0e64c7ab5f14e0284d8b49b6f23ec70f853d38eb60dc91a6fa826d49d188fd20158c3aaa101b4b6a0c89d4df824fe755ff2cfd4f876bb2dcefe760d6f9ec5e9e2990cab4367949f27062857ca26f2303f07f6c6c953f382cb8a379ae7b28c6234983fd61739550dc6b502c4feb9c9991459265f61471b91e3b592ad3e21a276d14321f462c820477e2b34a7ea16da1f3ffa760d9065ceb5a98ffc3d19da519133d542f74dd70c4366d98d16c36c27e4384cf31130614a1398621c64c260ad91d0de32900e2ac2589029b35d21eacd078bea5cb0a9db4bc3b7ba644d0459c2e0489ae62215cc525c36784191d94b0203010001

除此之外,還剩下兩項,證書簽名算法(誒?這個剛剛不是已經傳過了么),以及證書的簽名值

從中可以讀出,簽名算法就是 sha256WithRSAEncryption,簽名值我提取出來,如下:

220f0a0d15fa3c3909bb1e9f4d1d78cb9a41983cc9e549a4f8781f483fc18c679421eb84875354e00d86877eb1e80fb691eb0133208a0bee641ca0a7585b0e85818e88557a50f3f6241eebbc9cf49be40dc21f1d82a0cf30de30643cf236b290e74b6dee9bfdb71dab5f03b5cfd965bc16e139bc66f37119fcfc73aaf4c50fda1111bd948f507f85dd239012be73c953234328e332091c2fa38c482b6b4fdba52a26a1cc557a9c95edacea1d7b62f8996c934a5b3d762dd4f3cb88d405b805b7f604c07bd518665940f34fcb9e54121ac724a1ea3a58f42c9556f25058b19afa8c233fdf881bfeff32186051ec104fa23d4024b16b672f8eb33e359c3f813aa1

至於它是怎么算出來的,之前也畫過一張圖,我就直接放過來了(注意,這里的給服務端,是指 CA 機構給服務端,然后服務端現在又給了客戶端)。

還記得剛剛的簽名算法么?sha256WithRSAEncryption

這個圖里的哈希摘要用的算法就是 sha256,而 CA 私鑰加密用的算法就是 RSA。

好了,全部證書相關的信息就講完了,同時也是 Server Certificate 這個環節的唯一信息。

證書一方面可以通過服務端給客戶端傳遞的包解析來看,另一方面,由於瀏覽器要解析這個證書信息做驗證,所以通常瀏覽器有更直觀的方式可以查看,就不用我們費心思了。

點開瀏覽器地址欄的小鎖頭。

看,和我們剛剛抓包分析出來的信息,一毛一樣。

我們繼續看下一個包。

我相信你已經不記得整個流程到哪里了,好心的我給你放之前的圖。

剛剛進行完兩個 hello,以及一個傳遞證書的包 Certificate,接下來就要進行協商對稱秘鑰的過程了。

這個過程,最簡單就是 RSA 算法,用服務端公鑰直接加密客戶端隨機生成的一個對稱加密秘鑰,發給服務端。

但現在基本上都用更為復雜的秘鑰交換算法,我們往下看。

 

Server Key Exchange




 

用於 premaster secret 生成的

之前說了,秘鑰交換算法是 ECDHE 算法,這里隱含着包含了好多信息。

首先選擇的橢圓曲線是 named_curve 類型,並指定了基點生成一個私鑰,這個我們抓包看不見根據私鑰和基點,計算出公鑰,然后把這個公鑰用服務端公鑰加密,發送給服務端,這個我們能看到,就是里面的 Pubkey ,值為

2ce174dbdb6f481b6ab9fd37446dca95b6ade3613afba03243d163360f63713b

至於 ECDHE 用到的橢圓曲線秘鑰交換算法的細節,這里就不展開講了,因為我也不會,就知道它最終是為了和服務端協商出來一個 premaster_secret 就好。

接着往下看。

 

Server CertificateRequest




 

請求客戶端證書,此案例中沒有,一般銀行等需要客戶端也加密的才有,比如 U 盾。

 

Server ServerHelloDone




 

標識着 serverHello 這個握手過程結束了。

 

Client Certificate




 

客戶端證書。本案例中沒有,也說明了上面服務端確實沒有發送 CertificateRequest

 

Client ClientKeyExchange




 

緊接着 ServerHelloDone 發送,用於協商出 premaster_secret,同之前的 ServerKeyExchange 配合使用的。

這回輪到客戶端給服務端一個用於 ECDHE 算法的公鑰了。

f04e0743377afb5e9bf0a84aec5c7257957b85daee98fc48fb8971a26b457077

而同時客戶端與這個公鑰配對的私鑰,我們也無法通過抓包看出來。

 

生成最終通信的對稱加密秘鑰
master_secret




 

這一步不是抓包的信息,而是客戶端和服務端此時都在自己端內所做的事情,非常關鍵。

就是計算出最終對稱加密用的秘鑰 master_secret,這也是整個花里胡哨的過程,最終且唯一的一個目的,並且兩端算出來的結果肯定是一樣的。

HTTPS 的目的,不就是雙方協商出一個共同的對稱加密秘鑰么,怕被中間人攔截到,所以做的證書呀,非對稱加密算法呀,秘鑰協商算法等復雜的規定。

那 master_secret 是怎么計算出來的呢?

還記不記得之前我們得到了三個隨機數:

隨機數 1(客戶端隨機數) :在 ClientHello 消息里,由客戶端生成的隨機數,是 ee8880e816ac14ca5b69bde656c188f37a08bcf2052a550b7867b041f6c1ab48隨機數 2(服務端隨機數) :在 ServerHello 消息里,由服務端生成的隨機數,是3ad03af5b8a5ebfe7902a250406b2e99d2667e37e524e0e5c333c0e0b9a637e8隨機數 3(pre_master) :通過秘鑰交換算法 ECDHE 計算出的,我們叫它 pre_master。

最終的對稱加密秘鑰 master_secret,就是根據這三個隨機數共同計算出來的。

一旦雙方協商出來了這個相同的對稱秘鑰,那就可以開始愉快地安全通信了,TLS 層的工作也就圓滿完成。

所以可想而知,接下來的工作,就都是收尾工作了,因為秘鑰已經協商好了。

 

Client CertificateVerify




 

驗證客戶端證書有效性,本案例中沒有。

 

Client ChangeCipherSpec




 

秘鑰改變通知,此時客戶端已經生成了 master_secret,之后的消息將都通過 master secret 來加密。

可以看到,就是個標識,沒有攜帶什么有用的信息。

 

Client Finish




 

這一步對應的是 Client Finish 消息,客戶端將前面的握手消息生成摘要再用協商好的秘鑰加密,這是客戶端發出的第一條加密消息。服務端接收后會用秘鑰解密,能解出來說明前面協商出來的秘鑰是一致的。

 

Server ChangeCipherSpec




 

也是秘鑰改變通知,此時服務端也已經生成了 master_secret 了,后面的通信都用此值加密。

 

Server Finish




 

同 Client Finish,服務器端發送握手結束通知,同時會帶上前面所發內容的簽名到客戶端,保證前面通信數據的正確性。

 

Application Data




 

之后就是真正加密的數據了。

 

 

總結

 

 

我們去掉客戶端證書這個部分,整個過程簡化來說一遍。1. client --> server ClientHello客戶端生成隨機數,並發送一組密碼學套件供服務端選
2. server--> client ServerHello服務端生成隨機數,並從上述密碼學套件組里選一個3. server--> client Certificate服務端發給客戶端證書
4. server--> client ServerKeyExchange服務端發給客戶端秘鑰交換算法所需的值
5. server--> client ServerHelloDone服務端 hello 階段結束
6. client --> server ClientKeyExchange客戶端發給服務端秘鑰交換算法所需的值7. client --> server ChangeCipherSpec客戶端告訴服務端,我已經知道秘鑰了,之后的消息我就都加密發送了。
8. client --> server Finish結束並驗證
7. server --> server ChangeCipherSpec服務端告訴客戶端,我已經知道秘鑰了,之后的消息我就都加密發送了。9. server--> client Finish

結束並驗證

如果不看客戶端證書,不看復雜的 premaster_secret 協商算法,不看壓縮算法這些細節,其實簡單說只有三大步驟。
首先第一步,客戶端對服務端說 hello,並且發一組密碼套件。第二步,服務端對客戶端說 hello,並且選擇一組密碼套件,同時把附帶公鑰的證書發給客戶端。第三步,客戶端驗證證書,並且把對稱加密的秘鑰用服務端的公鑰加密,發給服務端,服務端用私鑰解密出就是對稱加密秘鑰了。(當然實際情況沒這么簡單,比如我們本次抓包是用 ECDHE 秘鑰交換算法,這也是目前大部分網站的做法)。經此三步之后,客戶端與服務端就都擁有了相同的對稱加密秘鑰,進行簡單的收尾工作,也就是通知對方秘鑰已生成好的信息,之后就可以開始通信了。最后說一句,本案例的抓包數據可以加我好友,朋友圈有下載鏈接,當然你也可以自己用 wireshark 抓,隨便訪問一個網站幾乎都是 https 的。

參考資料:

RFC-5246 The Transport Layer Security (TLS) Protocol Version 1.2

RFC-1422 Privacy Enhancement for Internet Electronic Mail Part II: Certificate-Based Key Management

公眾號 - 低並發編程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM