清晰圖解深度分析 HTTPS 原理

前言

很高興遇見你~

Https 現在基本已經覆蓋所有的 http 請求了,作爲一個偉大的發明,保障了我們的通信安全。在 Android 中對於 HTTPS 其實感知不多,因爲這些內容都有成熟的框架幫我們完成了,例如 okHttp。我們發起一個 http 或 https 的請求幾乎感受不到區別。

但最近在研究 okHttp 的源碼的時候,發現很多的內容沒看懂,最後發現是 http 相關的網絡知識不紮實,再一次回過頭來,把 https 學了一遍。正如前面所說,得益於框架,我們幾乎不需要學習 https 背後到底發生了什麼,但是發生了相關的 bug 也就無法修復(面試要問 [狗頭])。所以,作爲一個 android 開發者,也還是很有必要學一下 https。

HTTPS 的目標就是解決網絡通信的安全問題。本文首先闡述網絡中存在的風險,然後再討論其涉及的加密方法、證書驗證,最後再同從請求的角度解析整個安全連接的流程。

網絡存在的風險

在沒有經過任何加密手段的 HTTP 通信中,面臨着三大危險:消息監聽、消息篡改、冒充身份

消息監聽

我們發送的消息需要經過很多的中間路由器,我們無法確保網絡中每一個節點都是安全的,所以我們發送的數據會被惡意的對象截取到。假如我們的消息沒有經過任何加密,那麼惡意用戶就可以監聽到我們通信的所有數據。如下圖:

解決的方法是:對通信數據進行加密。如下圖:

經過加密的數據,即時被黑客截取到,他也無法知道數據的內容。

消息篡改

第二個危險是消息篡改。我們發出的數據會經過危險的中間節點,黑客可以監聽我們的數據,也可以對我們的數據進行修改。如下圖:

解決篡改的方法是:利用 MD5 等 hash 算法手段來檢驗數據的完整性 。下面會詳解。

冒充身份

HTTP 並沒驗證身份的流程,我們無法保證我們接收到的數據是服務器響應的,服務器也無法鑑別請求的用戶是否是惡意用戶。如下圖:

解決的方法是:使用證書來檢驗對方的身份

HTTP 通信面臨的這些問題,讓我們的網絡通信變得極其不安全,HTTPS 就是在 HTTP 的基礎上來解決安全問題。

加密算法

加密算法依舊是 HTTPS 安全通信中的重頭戲。在理想的情況下,如若有一個加密算法使得僅有用戶和服務可以加密解密,那麼其實是不存在上面的安全問題的。但黑客本身,他也可以作爲一個客戶存在,普通客戶可以加密解密,那麼黑客也就可以做到。所以需要附加上動態因子來保證算法的安全。

這裏解釋一下什麼是動態因子算法(這個名字我自己起的,僅僅爲了幫助理解)

假如現在需要發送的數據是:123 算法是:數據 + 動態整數

現在通信雙方商量的動態因子是:5,那麼

  1. 發送方對數據進行加密:123+5=128
  2. 接收方對數據進行解密:128-5 =123

即使黑客知道具體的算法就是數據 + 動態整數,但是他不知道具體的動態整數是多少,也就無法解出原始的數據內容。這個動態整數稱之爲密鑰

下面介紹 HTTPS 中用到的加密算法。

對稱算法

對稱算法比較簡單:加密和解密數據使用相同的密鑰 。如下圖:

對稱算法的優點就是效率很高,可以對長數據進行加解密。但對稱算法也存在缺點。

第一是雙方使用相同的密鑰,無法辨別數據到底是由服務器加密還是客戶端加密,也就是無法區分一個消息是由服務器發出還是由客戶端發出。解決這個問題方法也很簡單:雙方加密使用不同的密鑰

第二,通信雙方難以確保拿到安全的密鑰 。因爲第一步總是需要通過網路通信來商量密鑰,那可不可以使用固定的密鑰?前面講過,黑客也是一個客戶,那麼他也可以拿到密鑰,這個算法就失去意義了。

解決這個問題的方法是:使用非對稱算法

非對稱算法

對稱算法是加密解密使用相同的密鑰,而非對稱算法是加密與解密使用不同的密鑰 。如下圖:

  1. 非對稱加密有兩把密鑰:公鑰和私鑰
  2. 公鑰可公開給所有人,私鑰必須自己保密,不給任何人拿到
  3. 客戶端可以使用服務器的公鑰加密數據,而這份密文,只有服務器的私鑰才能解開
  4. 反過來,使用私鑰加密的數據,也只有公鑰可以解開

非對稱算法很好地解決了對稱算法存在的問題:無法安全交換密鑰 。服務器的公鑰可以公開給所有的用戶,當客戶端首次訪問服務器,服務器便把公鑰返回即可。

但是對於非對稱算法有一個很嚴重的缺點:性能極差 。所以我們可以將對稱與非對稱算法結合起來,解決上述問題。

對稱 + 非對稱

對稱算法存在的問題是無法安全地互換密鑰;因此第一步我們可以使用非對稱算法來交換密鑰,後續使用對稱算法來進行通信。如下圖:

  1. 當客戶訪問服務器時,服務器返回一個公鑰;
  2. 客戶端拿到公鑰之後,對客戶端密鑰使用公鑰進行加密之後發送給服務端;
  3. 服務端拿到客戶端密鑰之後對服務端密鑰進行加密發送給客戶端;

這樣就完成了雙方密鑰的交換,後續可以使用密鑰進行高效率通信。

到此我們的網絡傳輸依舊不是安全的,因爲,我們無法保證第一步服務器返回的公鑰不會被黑客篡改。假如黑客把服務器返回的公鑰轉換成自己的公鑰,後續他就可以對客戶端的的所有消息使用自己的私鑰解密。而問題的本質在於:我們無法辨別返回的數據是否是真的由服務器返回的 。這個問題的解決方法就是:使用數字證書來證明信息發送方的身份

數字證書

經過前面加密算法的討論,對稱 + 非對稱算法已經可以解決大部分的網絡安全問題。但第一步服務器返回的公鑰仍舊有被黑客篡改的風險,因爲我們無法確保通信對方的身份。數字證書的引入,就是爲了解決這個問題。

證書概述

數字證書是由公認的證書機構頒發給服務器的一個用於驗證身份的數字認證

數字證書可以用身份證來進行類比:

身份證是我們自身身份信息的一個認證,頒發的機構是我們全國人民認可的公安局。 同理,服務器的數字證書也是服務器身份的一個認證,頒發的機構是互聯網中普遍認可的證書機構。

服務器的證書中,包含有服務器信息例如公鑰等、證書籤名、證書機構信息等。客戶端拿到服務器的證書,進行證書驗證後,就可以準確得到服務器的公鑰,利用這個公鑰,就可以實現上述的算法加密了。

總之,數字證書的作用就是證明數據的來源,安全獲取到服務器的公鑰進行加密通信

證書驗證

客戶端如何驗證服務器的證書呢?首先得看看證書是怎麼做出來的:

  1. 服務器向證書機構申請證書,同時提供自己的域名、地址、公鑰等信息;
  2. 證書機構對服務器的信息使用 hash 算法得出一份 128 位的摘要,並對這份摘要使用自己的私鑰進行非對稱加密得到證書數字簽名
  3. 證書機構把服務器信息(明文)+ 數字簽名 + 證書機構信息(包含證書機構公鑰)發送給服務器
  4. 客戶端請求服務器時,服務器把證書返回給客戶端

客戶端驗證證書的重點就是:比較摘要

  1. 客戶端拿到證書,得到服務器信息、數字簽名、證書機構信息
  2. 客戶端對服務器信息進行 hash 算法計算得出一份摘要 S1
  3. 客戶端使用證書機構的公鑰對數字簽名進行解密得到一份摘要 S2
  4. 對比 S1 和 S2 即可辨別此證書是否來自服務器且沒經過篡改

經過上面的證書驗證流程,客戶端就可以成功拿到服務器的公鑰,進行下一步的加密流程。至於爲什麼通過比較摘要即可知道證書安全,下面進行討論。

證書鏈

客戶端驗證證書的流程很簡單:使用證書機構公鑰解開證書的數字簽名後進行比對即可。但這裏有一個問題:如何保證證書機構的公鑰可信 ?假如黑客使用自己的私鑰加密,同時把證書機構的公鑰修改成自己的公鑰,那豈不是非常危險?

互聯網中的主機對象非常多,但證書機構卻不多。計算機產商,會在系統中安裝一些根證書機構的信息,其中就包含了這些機構的公鑰。這些公鑰是在一定程度上是絕對安全的,是可以信任的。客戶端可以使用這些公鑰對數字簽名進行解密。安全問題,終於得到了完美的解決。

系統中預裝的證書機構是有限的,但世界上每時每刻申請數字證書卻非常多,他們 “忙不過來”,因此有了二級證書機構。二級證書機構由根證書機構簽發,二級證書機構再去給服務器簽發證書。那麼此時如何進行證書驗證呢?還是一樣的道理:

  1. 利用根證書機構給二級證書機構簽發的時候同樣是一份數字證書,其中包含了二級證書機構信息、數字簽名、根證書機構信息
  2. 服務器的數字證書中包含了二級證書機構的數字證書
  3. 客戶端使用根證書機構的公鑰對二級證書機構的數字簽名進行解密得到摘要再進行比對,得到二級證書機構的公鑰
  4. 使用二級證書機構的公鑰對服務器證書進行驗證

同理,三級、四級證書機構驗證都類同。在瀏覽器中,我們可以查看網站的證書鏈:

可以看到這是一個包含了兩級證書機構的證書鏈,最頂層的證書機構,即是根證書機構。

hash 算法

我們會發現,證書並不是直接對服務器信息進行加密,而是利用 hash 算法得到服務器信息的摘要,再對摘要進行加密。那這裏可能會有這些問題:

  1. 直接對信息進行加密不可以嗎?爲什麼多此一舉?
  2. 只對摘要進行加密,那麼原文內容不是泄露了嗎?

hash 算法最常用的就是 MD5,他可以把一段數據轉化成一個 128 位的長度的摘要,不同的數據,會得到不同的摘要。

摘要的長度更短,使用非對稱加密的效率更高。因此,證書中對摘要而不是直接對信息進行加密可以提高網絡效率。而服務器信息本身並不是敏感信息,不怕被黑客截取監聽,所以可以使用明文傳輸。

hash 算法不僅爲了提高效率,更重要的是可以辨別信息是否遭受了篡改

假如在證書中我們直接對服務器信息進行私鑰加密,黑客截取到我們的數據後,他雖然看不懂,但是他可以直接對密文進行篡改。最後接收方解密之後得到的就是一分錯誤的信息。

如果信息是一個文本,我們可以很明顯地辨別出來;但如果是一個數字編號,那麼很難知道是否遭受了篡改。舉個例子:

  1. 服務器發送貨物編號 123,對 123 進行加密之後得到 098
  2. 黑客截取後無法解密,將 098 修改成 048 之後發送給客戶端
  3. 客戶端解密 048 之後得到 129,數據遭受了篡改;雖然黑客不知道我們發送什麼,但是可以讓我們的業務發生錯誤

此時如果對密文進行 hash 得到一份摘要,同時對摘要進行加密。客戶端拿到數據之後,對密文進行 hash 再加密,再與服務器發送過來的摘要進行比對即可知道數據是否發生了篡改。黑客不管是修改密文 or 摘要密文,最後都會導致最後兩者的摘要不等。

hash 算法的優化

MD5 算法是有缺點的,他會發生碰撞。例如一年只有 366 天,但中國有 13 億人口,肯定會有非常多的人生日相同。同理,摘要的長度只有 128 位,無法唯一表示所有的數據,存在一定的風險:兩份不同的數據得到相同的摘要。讓黑客變得有機可乘,所以需要引入一種優化方案:HMAC(消息認證碼)

HMAC 與 MD5 的差別在於,他並不是直接對數據進行 hash,他還需要一個隨機數來共同作用 hash,只要保證每次的隨機數不同,黑客拿不到隨機數,也就無法對 hash 算法進行破解;即使兩次的數據一樣,因爲隨機數不同,最終得出的摘要也不同;這更進一步保證了安全。

但是隨機數需要通信雙方進行協商擬定,所以在證書中無法使用 HMAC。但是在 HTTPS 安全通信中,則可以加入隨機數來實現 HMAC,提高安全性。

安全模型

這一小節主要講一下 HTTPS 爲我們建立的宏觀安全模型。

需要特別注意的是,HTTPS 並不是一個新的應用協議來取代 HTTP,而是在 HTTP 的基礎上,增加了網絡安全的內容。HTTPS 的全稱:Hyper Text Transfer Protocol over SecureSocket Layer,建立在安全 socket 層次上的超文本傳輸協議,可以認爲 HTTPS = HTTP+SSL。HTTPS 與 HTTP、TCP 的關係如下:

HTTPS 在 HTTP 和 TCP 之間建立了一個安全連接層 。SSL/TLS 層次和 TCP 很類似,雙方建立 TCP 連接之後,需要再建立安全連接。與 TCP 連接一樣,SSL 連接本質上,是對雙方安全信息的記錄,並不是一個真正意義上的連接。HTTP 通過安全連接,即可與目標主機進行安全的通信,不怕被監聽、篡改、冒充身份。

這裏的 SSL 與 TLS 指的都是安全協議。SSL 全名 Secure Sockets Layer,安全套接字層協議;TLS 全名 Transport Layer Security,安全傳輸層協議。TLS 從 SSL 發展而來,SSL 是早期的安全層協議;後期逐漸發現了其安全漏洞,發展出了 TLS。現在使用的最多的是 TLS1.2、TLS1.3 版本,如我們查看掘金的證書:

可以看到使用了 TLS1.2 版本。安全協議版本需要通信雙方進行協商,只有使用相同版本的協議,才能建立安全連接。

此外,建立安全連接是比較消耗性能的。如果每次請求都建立一次安全連接,那麼網絡的效率將會大打折扣。因此,在建立一次安全連接之後,服務器會存儲客戶端的安全相關信息,在一定時間內通信時無需再次建立安全連接,服務器會把先前的密鑰等信息發送給客戶端,直接使用此前已經記錄的安全信息即可。

安全連接建立流程

和 TCP 連接類同,安全連接也需要一個建立的流程。但是經過了前面 HTTPS 加密算法以及證書體系的學習,理解 HTTPS 安全連接建立流程就非常簡單了。基本就是把上面的流程走了一遍。先來看一張總體圖:

  1. 客戶端請求服務器建立安全連接,附加客戶端支持的 SSL 與 TLS 版本、支持的加密算法版本、隨機數

    加密算法與安全協議版本有很多,但服務不一定支持最新版本的協議預算法。所以客戶端把所以支持的版本發送給服務器,讓服務器去選擇。

    隨機數非常重要,前面講 hash 算法的時候講到,隨機數是一個動態因子,讓 hash 算法更加安全。同時,隨機數也參與了對稱密鑰的生成。

  2. 服務器響應請求,附加選擇的協議版本、加密算法版本、服務器隨機數

    服務器從客戶端支持的協議版本中,選擇一套自己最喜歡的。

    爲了辨別消息是由哪一方加密併發出的,需要準備兩個對稱密鑰。因此服務器也需要產生一個隨機數。

  3. 服務器向客戶端發送證書

    服務器向客戶端發送自己證書,其中就包含了服務器的公鑰。

  4. 服務器發送 hello done 表示 hello 階段結束

  5. 客戶端驗證證書,拿到服務器公鑰;利用兩個隨機數,生成 pre-master secret,並使用服務器的公鑰加密發送給服務器。

    證書驗證步驟參考上面的證書小節;

    pre-master secret 是一個非常重要的東西,雙方利用 pre-master secret 生成 master-secret,利用前面的兩個隨機數生成兩個對稱加密密鑰和兩個 HMAC 密鑰,兩對密鑰分別用於客戶端加密和服務器加密。

  6. 客戶端發送 changeCipherSpec 提示服務器此後使用 pre-master secret 產生的密鑰加密通信

  7. 客戶端發送 FIN 報文,表示結束

  8. 服務器也發送 changeCipherSpec 報文

  9. 服務器也發送 FIN 報文,表示結束

  10. 雙方可以開始安全通信了

至此,對於 HTTPS 的加密流程,已經比較清晰了。

Android 中運用

無論是 HTTP 還是 HTTPS,事實上開源網絡框架都已經爲我們完成了這些粗活累活,例如 okHttp。正常情況下,發起 HTTP 和 HTTPS 請求並沒有任何異同。但有時候會出現一些特殊的問題,就需要我們自己動手解決:

  1. 系統過於老舊,沒有安裝根證書。缺乏根證書的公鑰,那麼無法驗證服務器證書是否安全。
  2. 自簽名證書。自己的 app 訪問自己的服務器,有時候爲了節約經費,可以自己給自己的服務器簽名。
  3. 證書信息缺乏關鍵信息,如頒發證書的機構。

對於上面的問題,我們可以自己重寫證書驗證流程,或者在 okHttp 中添加信任的服務器公鑰,可以解決上面的問題。

最後

HTTPS 要解決的就是計算機網絡中的安全問題,不同問題的解決方法要清楚:

HTTPS 就是利用這些方法,爲通信雙方建立安全連接,從而來實現安全通信。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://juejin.cn/post/6934678746808975397