26丨信任始於握手:TLS1.2連接過程解析


說明《透視HTTP協議》是 羅劍鋒 (奇虎360技術專家)在極客時間開的一門專欄課,筆者記錄一下學習筆記,僅供參考。

經過前幾講的介紹,你應該已經熟悉了對稱加密與非對稱加密、數字簽名與證書等密碼學知識。

       有了這些知識“打底”,現在我們就可以正式開始研究 HTTPS 和 TLS 協議了。

HTTPS 建立連接

       當你在瀏覽器地址欄里鍵入“https”開頭的 URI,再按下回車,會發生什么呢?

       回憶一下第 8 講的內容,你應該知道,瀏覽器首先要從 URI 里提取出協議名和域名。因為協議名是“https”,所以瀏覽器就知道了端口號是默認的 443,它再用 DNS 解析域名,得到目標的 IP 地址,然后就可以使用三次握手與網站建立 TCP 連接了。

       在 HTTP 協議里,建立連接后,瀏覽器會立即發送請求報文。但現在是 HTTPS 協議,它需要再用另外一個“握手”過程,在 TCP 上建立安全連接,之后才是收發 HTTP 報文。

       這個“握手”過程與 TCP 有些類似,是 HTTPS 和 TLS 協議里最重要、最核心的部分,懂了它,你就可以自豪地說自己“掌握了 HTTPS”。

TLS 協議的組成

       在講 TLS 握手之前,我先簡單介紹一下 TLS 協議的組成。

       TLS 包含幾個子協議,你也可以理解為它是由幾個不同職責的模塊組成,比較常用的有記錄協議、警報協議、握手協議、變更密碼規范協議等。

              記錄協議(Record Protocol)規定了 TLS 收發數據的基本單位:記錄(record)。它有點像是 TCP 里的 segment,所有的其他子協議都需要通過記錄協議發出。但多個記錄數據可以在一個 TCP 包里一次性發出,也並不需要像 TCP 那樣返回 ACK。

       警報協議(Alert Protocol)的職責是向對方發出警報信息,有點像是 HTTP 協議里的狀態碼。比如,protocol_version 就是不支持舊版本,bad_certificate 就是證書有問題,收到警報后另一方可以選擇繼續,也可以立即終止連接。

       握手協議(Handshake Protocol)是 TLS 里最復雜的子協議,要比 TCP 的 SYN/ACK 復雜的多,瀏覽器和服務器會在握手過程中協商 TLS 版本號、隨機數、密碼套件等信息,然后交換證書和密鑰參數,最終雙方協商得到會話密鑰,用於后續的混合加密系統。

       最后一個是變更密碼規范協議(Change Cipher Spec Protocol),它非常簡單,就是一個“通知”,告訴對方,后續的數據都將使用加密保護。那么反過來,在它之前,數據都是明文的。

       下面的這張圖簡要地描述了 TLS 的握手過程,其中每一個“框”都是一個記錄,多個記錄組合成一個 TCP 包發送。所以,最多經過兩次消息往返(4 個消息)就可以完成握手,然后就可以在安全的通信環境里發送 HTTP 報文,實現 HTTPS 協議。

 

抓包的准備工作

       這次我們在實驗環境里測試 TLS 握手的 URI 是“/26-1”,看了上面的圖你就可以知道,TLS 握手的前幾個消息都是明文的,能夠在 Wireshark 里直接看。但只要出現了“Change Cipher Spec”,后面的數據就都是密文了,看到的也就會是亂碼,不知道究竟是什么東西。

       為了更好地分析 TLS 握手過程,你可以再對系統和 Wireshark 做一下設置,讓瀏覽器導出握手過程中的秘密信息,這樣 Wireshark 就可以把密文解密,還原出明文。

       首先,你需要在 Windows 的設置里新增一個系統變量“SSLKEYLOGFILE”,設置瀏覽器日志文件的路徑,比如“D:\http_study\www\temp\sslkey.log”(具體的設置過程就不詳細說了,可以在設置里搜索“系統變量”)。

 

 

 

然后在 Wireshark 里設置“Protocols-TLS”(較早版本的 Wireshark 里是“SSL”),在“(Pre)-Master-Secret log filename”里填上剛才的日志文件。

 

 

 

       設置好之后,過濾器選擇“tcp port 443”,就可以抓到實驗環境里的所有 HTTPS 數據了。

       如果你覺得麻煩也沒關系,GitHub 上有抓好的包和相應的日志,用 Wireshark 直接打開就行。

ECDHE 握手過程

       剛才你看到的是握手過程的簡要圖,我又畫了一個詳細圖,對應 Wireshark 的抓包,下面我就用這個圖來仔細剖析 TLS 的握手過程。

 

       在 TCP 建立連接之后,瀏覽器會首先發一個“Client Hello”消息,也就是跟服務器“打招呼”。里面有客戶端的版本號、支持的密碼套件,還有一個隨機數(Client Random),用於后續生成會話密鑰。

 

Handshake Protocol: Client Hello

 

    Version: TLS 1.2 (0x0303)

 

    Random: 1cbf803321fd2623408dfe…

 

    Cipher Suites (17 suites)

 

        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

 

        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)

復制代碼

       這個的意思就是:“我這邊有這些這些信息,你看看哪些是能用的,關鍵的隨機數可得留着。”

       作為“禮尚往來”,服務器收到“Client Hello”后,會返回一個“Server Hello”消息。把版本號對一下,也給出一個隨機數(Server Random),然后從客戶端的列表里選一個作為本次通信使用的密碼套件,在這里它選擇了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”。

 

Handshake Protocol: Server Hello

 

    Version: TLS 1.2 (0x0303)

 

    Random: 0e6320f21bae50842e96…

 

    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)

復制代碼

       這個的意思就是:“版本號對上了,可以加密,你的密碼套件挺多,我選一個最合適的吧,用橢圓曲線加 RSA、AES、SHA384。我也給你一個隨機數,你也得留着。”

       然后,服務器為了證明自己的身份,就把證書也發給了客戶端(Server Certificate)。

接下來是一個關鍵的操作,因為服務器選擇了 ECDHE 算法,所以它會在證書后發送“Server Key Exchange”消息,里面是橢圓曲線的公鑰(Server Params),用來實現密鑰交換算法,再加上自己的私鑰簽名認證。

 

Handshake Protocol: Server Key Exchange

 

    EC Diffie-Hellman Server Params

 

        Curve Type: named_curve (0x03)

 

        Named Curve: x25519 (0x001d)

 

        Pubkey: 3b39deaf00217894e...

 

        Signature Algorithm: rsa_pkcs1_sha512 (0x0601)

 

        Signature: 37141adac38ea4...

復制代碼

       這相當於說:“剛才我選的密碼套件有點復雜,所以再給你個算法的參數,和剛才的隨機數一樣有用,別丟了。為了防止別人冒充,我又蓋了個章。”

       之后是“Server Hello Done”消息,服務器說:“我的信息就是這些,打招呼完畢。”

這樣第一個消息往返就結束了(兩個 TCP 包),結果是客戶端和服務器通過明文共享了       三個信息:Client Random、Server Random 和 Server Params

    客戶端這時也拿到了服務器的證書,那這個證書是不是真實有效的呢?

這就要用到第 25 講里的知識了,開始走證書鏈逐級驗證,確認證書的真實性,再用證書公鑰驗證簽名,就確認了服務器的身份:“剛才跟我打招呼的不是騙子,可以接着往下走。”

       然后,客戶端按照密碼套件的要求,也生成一個橢圓曲線的公鑰(Client Params),用“Client Key Exchange”消息發給服務器。

 

Handshake Protocol: Client Key Exchange

 

    EC Diffie-Hellman Client Params

 

        Pubkey: 8c674d0e08dc27b5eaa…

復制代碼

       現在客戶端和服務器手里都拿到了密鑰交換算法的兩個參數(Client Params、Server Params),就用 ECDHE 算法一陣算,算出了一個新的東西,叫“Pre-Master”,其實也是一個隨機數。

       至於具體的計算原理和過程,因為太復雜就不細說了,但算法可以保證即使黑客截獲了之前的參數,也是絕對算不出這個隨機數的。

       現在客戶端和服務器手里有了三個隨機數:Client Random、Server Random 和 Pre-Master。用這三個作為原始材料,就可以生成用於加密會 話的主密鑰,叫“Master Secret”。而黑客因為拿不到“Pre-Master”,所以也就得不到主密鑰。

       為什么非得這么麻煩,非要三個隨機數呢?

       這就必須說 TLS 的設計者考慮得非常周到了,他們不信任客戶端或服務器偽隨機數的可靠性,為了保證真正的“完全隨機”“不可預測”,把三個不可靠的隨機數混合起來,那么“隨機”的程度就非常高了,足夠讓黑客難以猜測。

       你一定很想知道“Master Secret”究竟是怎么算出來的吧,貼一下 RFC 里的公式:

 

master_secret = PRF(pre_master_secret, "master secret",

 

                    ClientHello.random + ServerHello.random)

復制代碼

       這里的“PRF”就是偽隨機數函數,它基於密碼套件里的最后一個參數,比如這次的 SHA384,通過摘要算法來再一次強化“Master Secret”的隨機性。

       主密鑰有 48 字節,但它也不是最終用於通信的會話密鑰,還會再用 PRF 擴展出更多的密鑰,比如客戶端發送用的會話密鑰(client_write_key)、服務器發送用的會話密鑰(server_write_key)等等,避免只用一個密鑰帶來的安全隱患。

       有了主密鑰和派生的會話密鑰,握手就快結束了。客戶端發一個“Change Cipher Spec”,然后再發一個“Finished”消息,把之前所有發送的數據做個摘要,再加密一下,讓服務器做個驗證。

       意思就是告訴服務器:“后面都改用對稱算法加密通信了啊,用的就是打招呼時說的 AES,加密對不對還得你測一下。”

       服務器也是同樣的操作,發“Change Cipher Spec”和“Finished”消息,雙方都驗證加密解密 OK,握手正式結束,后面就收發被加密的 HTTP 請求和響應了。

RSA 握手過程

       整個握手過程可真是夠復雜的,但你可能會問了,好像這個過程和其他地方看到的不一樣呢?

       剛才說的其實是如今主流的 TLS 握手過程,這與傳統的握手有兩點不同。

第一個,使用 ECDHE 實現密鑰交換,而不是 RSA,所以會在服務器端發出“Server Key Exchange”消息。

       第二個,因為使用了 ECDHE,客戶端可以不用等到服務器發回“Finished”確認握手完畢,立即就發出 HTTP 報文,省去了一個消息往返的時間浪費。這個叫“TLS False Start”,意思就是“搶跑”,和“TCP Fast Open”有點像,都是不等連接完全建立就提前發應用數據,提高傳輸的效率。

       實驗環境在 440 端口(https://www.chrono.com:440/26-1)實現了傳統的 RSA 密鑰交換,沒有“False Start”,你可以課后自己抓包看一下,這里我也畫了個圖。

 

       大體的流程沒有變,只是“Pre-Master”不再需要用算法生成,而是客戶端直接生成隨機數,然后用服務器的公鑰加密,通過“Client Key Exchange”消息發給服務器。服務器再用私鑰解密,這樣雙方也實現了共享三個隨機數,就可以生成主密鑰。

雙向認證

       到這里 TLS 握手就基本講完了。

       不過上面說的是“單向認證”握手過程,只認證了服務器的身份,而沒有認證客戶端的身份。這是因為通常單向認證通過后已經建立了安全通信,用賬號、密碼等簡單的手段就能夠確認用戶的真實身份。

       但為了防止賬號、密碼被盜,有的時候(比如網上銀行)還會使用 U 盾給用戶頒發客戶端證書,實現“雙向認證”,這樣會更加安全。

       雙向認證的流程也沒有太多變化,只是在“Server Hello Done”之后,“Client Key Exchange”之前,客戶端要發送“Client Certificate”消息,服務器收到后也把證書鏈走一遍,驗證客戶端的身份。

小結

今天我們學習了 HTTPS/TLS 的握手,內容比較多、比較難,不過記住下面四點就可以。

HTTPS 協議會先與服務器執行 TCP 握手,然后執行 TLS 握手,才能建立安全連接;

握手的目標是安全地交換對稱密鑰,需要三個隨機數,第三個隨機數“Pre-Master”必須加密傳輸,絕對不能讓黑客破解;

“Hello”消息交換隨機數,“Key Exchange”消息交換“Pre-Master”;

“Change Cipher Spec”之前傳輸的都是明文,之后都是對稱密鑰加密的密文。

課下作業

為什么公鑰能夠建立信任鏈,用對稱加密算法里的對稱密鑰行不行呢?

假設有一個三級的證書體系(Root CA=> 一級 CA=> 二級 CA),你能詳細解釋一下證書信任鏈的驗證過程嗎?


免責聲明!

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



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