HTTPS 工作原理,之前也看過一些,但是對整體的一個完整流程和部分細節,還是處于一個模糊狀態,之前也有一些疑問:“證書是怎么驗證的?”、“TLS 握手過程是怎么樣的?”、“對稱密鑰如何計算?”、“計算預主密鑰隨機數用了幾個?” 等等,基于這些疑問,也花了一些時間才逐步了解的,基于自己的理解,做了一個 HTTPS 的系列文章,希望能幫助到有此疑問的讀者朋友。
本文為系列的第一篇,帶著一些問題逐步了解對稱加密、非對稱加密、數字證書、密鑰協商等這些概念分別是什么、能做什么,一層一層揭開其神秘面紗。
使用 HTTP 潛在的問題
在 HTTP 中數據之間的網絡傳輸是明文的,很容易被中間人竊取、攻擊,對數據進行偽造再發往服務器端,服務端接收到數據也無法判斷數據的來源是否準確。
如果說為什么要使用 HTTPS?直白點就是 “HTTP 不安全”,無法準確的保證數據的機密性、真實性、完整性。
什么是 HTTPS 協議
HTTPS 不是一種全新的協議,它是建立在 SSL/TLS 傳輸層安全協議之上的一種 HTTP 協議,相當于 HTTPS = HTTP + SSL/TLS,可保護用戶計算機與網站服務器之間數據傳輸的完整性、機密性。
從 OSI 模型圖上看主要是在應用層和傳輸層直接多了一個 SSL/TLS 協議。
這里最主要的部分 SSL/TLS 就是我們學習 HTTPS 的關鍵部分,SSL/TLS 做為一種安全的加密協議,其在不安全的基礎設施之上為我們提供了安全的通信通道。
SSL/TLS 這個名字有時也會讓人迷,現在我們所說的 SSL/TLS 一般特指 TLS 協議,不妨看下它的發展歷史。
SSL/TLS 發展歷史
SSL 是 secure socket layer 的簡稱,中文為安全套接字層。最早由網景(Netscape)公司開發,該協議的第一個版本從未發布過。自 1994 年 11 月開始發布第二個版本,SSL 2 在開發上基本上沒有與 Netscape 公司以外的安全專家商討,這個版本被認為存在嚴重缺陷,這個版本最終也以失敗告終。在 SSL2 失敗后,Netscape 專注于 SSL 3 進行了完全重新的協議設計,于 1995 年發布,SSL 3 版的協議被沿用至今,只不過后來被改了名字 TLS 1.0,也許很多人并不知道。
1996 年 5 月,TLS 工作組成立,開始將 SSL 從 Netscape 公司遷移至 IETF,由于 Netscape 與 Microsoft 在 Web 統治權的爭執,整個遷移工作也經歷了一個漫長的過程,在 1999 年 1 月 IETF 組織將 SSL 進行了標準化 TLS 1.0 問世,前身就是 SSL 3。
TLS 是 transport layer security 的簡稱,中文為傳輸層安全協議。在 2006 年 4 月 TSL 1.1 版本發布,修復了一些關鍵的安全問題,添加對 CBC 攻擊的保護(隱式 IV 被替換為顯示 IV,更改分組密碼模式中的填充錯誤)。
在 2008 年 8 月 TLS 1.2 版本發布,主要包括:增加 SHA-2 密碼散列函數、AEAD 加密算法、TLS 擴展定義和 AES 密碼組合。
2018 年 8 月 TLS 1.3 版本發布,對安全的加強、性能的提升也做了很多改變,例如,在安全上將 MD5、SHA-1 這些不安全或過時的算法移除,僅保留了少數算法 ECDHA、SHA-2 等。性能上在 TLS 握手過程中由之前的 2-RTT 握手改進為 1-RTT 握手并初步支持 0-RTT。
選擇合適加密算法
我們談到 https 都知道它之所以安全是因為傳輸中對數據做了加密,首先了解下它選擇了哪種加密方式來實現的。
對稱加密
對稱加密是一種共享密鑰的算法,客戶端與服務端共用一把密鑰,對數據做加密傳輸,如果密鑰只有通信雙方持有,不保證泄漏,那就可以保證安全。
現實世界中顯然不是這樣的,例如瀏覽器同服務器交互,服務器把共享密鑰傳輸給瀏覽器,這個密鑰在傳輸過程中怎么保證不被截取、篡改?
非對稱加密
進一步提升安全系數,出現了 “非對稱加密” 又稱為 “公鑰加密”,該算法擁有兩個不對稱的密鑰,它的特性是使用公鑰加密只有對應的私鑰可解密,反之,私鑰加密也只有對應的公鑰才可解密。注意,私鑰僅自己可見,對外暴露的是公鑰。
非對稱加密的安全性比對稱加密要高,但是它需要更多的計算,不適用于數據量大的場景,通信速度沒有了保證也不行的,TLS 加密算法并沒有完全采用這種加密算法。
混合加密
所謂 “取長補短”,TLS 在加密算法上結合了非對稱加密和對稱加密,我們這里稱之為 “混合加密” 算法,使用非對稱加密進行身份驗證和共享密鑰的協商,只用一次即可,后續的通信中使用對稱密鑰進行數據的傳輸。
除此之外,客戶端和服務端交換公鑰的過程,依然存在被竊聽,經典的例子還是中間人攻擊,因為公鑰在傳輸的過程是可見的,中間人可以對客戶端扮演服務端的角色或者對服務端扮演客戶端的角色,依然可以對數據進行篡改,但是服務端無法判別來源是否可靠,問題仍然存在。
舉一個例子:
- 服務器使用非對稱加密算法生成一對公私鑰,我們稱為公鑰 A、私鑰 A,解決密鑰交換問題。
- 這里還存在一個中間人,它也生成了一對公私鑰,我們稱為公鑰 B、私鑰 B。
- 瀏覽器向服務器發起請求,服務器返回自己的公鑰 A,傳輸中被中間人截取(問題來了),將服務器的公鑰 A 替換為中間人的公鑰 B 發往瀏覽器。
- 瀏覽器獲取到公鑰 B,并不知道這個是中間人的,它生成一個隨機數再用公鑰 B 加密,得到對稱加密所需要的 “會話密鑰”。
- 瀏覽器將生成的 “會話密鑰” 發送給服務器,中間人截取之后使用自己測私鑰 B 解密,得到 “會話密鑰”,再用服務器公鑰 A 加密發送到服務器。
- 服務器在收到信息后,用自己的私鑰 A 解密,得到 “會話密鑰”,但服務器也不知道此時已被中間人截取了。
這也不行那該怎么辦?在這里使用 “混合加密” 從安全、性能上得到了一個平衡,使用非對稱加密交換對稱加密密鑰,已實現了我們需要的機密性。
現在我們要解決下一個疑問:如何保證瀏覽器拿到的公鑰是可信的?
數字證書解決信任問題
例如,現實世界里,我們去銀行辦事,到柜臺前你說我是張三,要辦理業務,銀行工作人員首先需要你出示證件,得證明你是真的張三,能證明自己的就是 “身份證” 了,由權威機構(現實世界里的公安局)頒發的大家都認可的證件。
網絡世界的 “公安局”
那么網絡世界里的公安局,就是我們常說的 CA,Certificate Authority,證書認證機構,我們也需要為網站申請數字證書。
證書是一個包含版本、序列號、簽名算法、頒發者、有效期、公鑰等的數字證書文件。我們的網站在使用 HTTPS 之前都會預先向 CA 機構申請一份數字證書,安裝到自己的服務器上,之后瀏覽器發起請求,服務器就可以把這個數字證書返回到瀏覽器,這個過程中怎么保證數字證書不被修改呢?
公安局在頒發我們的身份證時有一定的防偽技術,同樣 CA 在簽發證書時也會對證書進行數字簽名,保證證書的完整性。
image.png
摘要算法
摘要算法是一種單向的加密算法,也稱為 “散列算法”,在加密數據時不需要提供密鑰,加密之后的數據也不能進行逆向推算。
它能實現對一個大文件加密之后映射為一個小文件,好比一篇文章提取一段摘要,但如果原文發生改變,哪怕是增加或刪除一個標點符號再次加密后的結果也會發生完全不同的變化,目前一些常用的摘要算法(MD5、SHA-1)被認為存在安全性問題,在 TLS 1.3 版本已經移除了,現在推薦的是 SHA-2,例如 SHA256。
CA 機構對明文數據會做一個摘要算法,生成一段不可逆向解密的 Hash value,這段 Hash value 不能明文傳輸,避免中間人在修改證書后把摘要算法也修改了。
數字簽名
數字簽名,這個名字在現實世界也是如此,例如我給你一個證明,要證明是我給你的,最有效的辦法就是簽名、按手印,這個是沒辦法偽造的。
CA 也有一對自己的公私鑰,結合上面摘要算法生成的 hash value,使用 CA 私鑰加上這段 hash value 來生成數字簽名,這個只有對應的公鑰才可解密。
數字證書
CA 將數字簽名和我們申請的信息(服務器名稱、公鑰、主機名、權威機構的名稱、信息等)整合到一塊,生成數字證書,頒發給服務器。
下面是對 www.nodejs.red 這個域名截取的一張圖。
有了數字證書,客戶端和服務端在交互時就可使用非對稱密鑰來協商用于數據加密的對稱加密密鑰了。
協商對稱加密密鑰
證書驗證
我們在瀏覽器打開一個 HTTPS 協議的網址發起請求,在建立 TCP 鏈接之后,會發起 TLS 的握手協議,之后服務器會返回一系列消息,其中就包括證書消息。
證書的驗證存在一個證書信任鏈問題,我們向 CA 申請的證書,通常是由中間證書機構頒發的。例如,www.nodejs.red 這個域名你會看到它的證書簽發者是 “R3”,它是 Let's Encrypt 在 2020 年 11 月 20 日推出的一個免費證書,通過 R3 我們可以找到它的簽發者是 “ISRG Root X1”,而 “ISRG Root X1” 沒有了上級的簽發者,現在會認為它是根證書。
下圖展示的是 www.nodejs.red 這個域名網站的證書鏈關系。
在我們的操作系統中會預先安裝一些權威機構的證書,瀏覽器信任的是根證書,如果根證書在本地,就用根證書 “ISRG Root X1” 公鑰去驗證 “ISRG Root X1” 這個中間證書機構是否可信,如果校驗通過,再用 “ISRG Root X1” 去驗證最終的實體證書 “www.nodejs.red” 是否可信任,如果通過就認為證書 “www.nodejs.red” 是可信的。
證書驗證基本上都是這種模式,最終要找到本地安裝的根證書,在反向的逐級驗證,確認網站的簽發者是可信的。如下圖所示。
如果服務器返回的證書驗證通過,瀏覽器就可獲取到數字證書的明文、簽名信息,做以下操作:
- 用 CA 機構的公鑰(CA 機構的公鑰是不需要傳輸的,操作系統提供的根證書里會存在)去解密簽名,得到摘要算法計算出的 hash value,我們暫定名稱為 hashCode1。
- 用證書里指定的摘要算法對明文數據做加密,得到 hashCode2。
- 如果明文數據未被篡改,hashCode2 應該等于 hashCode1。
- 現在證書是可信的,就可拿到服務器的公鑰。
如果證書信息被篡改,沒有證書私鑰是不能改簽名的,客戶端收到證書之后對原文信息做個簽名一比對就知道是否被篡改。
另一個問題,假設:“我們的證書被黑客用合法證書調包呢?”,證書的域名等信息是不能被篡改的,就算黑客調包換成了自己的合法證書,因為域名信息不一樣,瀏覽器請求的時候一對比也可發現問題。
沒有絕對的安全,如果黑客把自己的根證書安裝在了你的計算機上,那么它就可以簽發任意域名的虛假證書了,因此,遇到一些不可信的文件還是不要亂安裝的好,保證根證書的安全。
計算加密密鑰
上面瀏覽器向服務器發起請求,服務器返回證書,這個過程雙方會交換兩個參數,分別是客戶端的隨機數、服務端的隨機數,用于生成主密鑰,但是主密鑰的生成還依賴一個預主密鑰。
不同的密鑰交換算法,生成預主密鑰的方法也不同。一種密鑰交換算法是 RSA,它的密鑰交換過程很簡單,由客戶端生成預主密鑰,為 46 字節的隨機數,使用服務器的公鑰加密,經過密鑰交換消息發送到服務端,服務端再用私鑰就可解密出這個預主密鑰。
基于 RSA 的密鑰交換算法被認為存在嚴重的漏洞威脅,任何能夠接觸到私鑰的人(例如,由于政治、賄賂、強行進入等)都可恢復預主密鑰,進而構建相同的主密鑰,最終密鑰泄漏就可解密之前記錄的所有流量了。這種密鑰交換算法正在被支持前向保密的其它算法替代,例如,ECDHE 算法在密鑰交換時,每個鏈接使用的主密鑰相互獨立,如果出現問題也只是影響到當前會話,不能用于追溯解密任何其它的流量。
ECDHE 是臨時橢圓曲線密鑰交換算法,客戶端和服務器會分別交換兩個信息 Server Params、Client Params,在每次的鏈接中,都會生成一對新的臨時公私鑰。基于 ECDHE 算法客戶端和服務端可分別計算出預主密鑰(premaster secret)
這時客戶端和服務端就分別擁有 Client Random、Server Random、Premaster Secret 三個隨機數。
主密鑰在 TLS v1.2 是通過一個偽隨機函數 master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) 計算出來的。
但主密鑰并不是最終的會話密鑰,最終的會話密鑰使用 PRF 偽隨機函數傳入主密鑰、客戶端隨機數、服務端隨機數生成。
- key_block = PRF(master_secret, "key expansion", server_random + client_random)
這個最終的會話密鑰包括:對稱加密密鑰(symmetric key)、消息認證碼密鑰(mac key)、初始化項量(iv key,只在必要時生成)
上面這些都是在 TLS 的握手協議中完成的,當握手完成之后,客戶端/服務端建立一個安全通信隧道,就可以發送應用程序數據了。
HTTPS 完整過程圖示
協商對稱加密密鑰,這里面主要就是 TLS 的握手協議,這個過程很復雜,還有很多內容本篇最后沒有詳細的講解,下圖為筆者畫的一個握手交互圖,在下一篇文章中會通過 Wireshark 工具來抓取網絡數據包做分析,做一個實戰講解,更深刻的理解 HTTPS 的原理。
原文鏈接:https://mp.weixin.qq.com/s?__biz=MzU3NTg5MjU1Mw==&mid=2247484501&idx=1&sn=744e6c655876a219ed0853a1f2b7aeaa&chksm=fd1d7973ca6af065de03fd2eeca288bedd738e2fea7c88632a315663c330f67b06db8a92c2b6&mpshare=1&s