前言
很高興遇見你~
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,那麼
- 傳送方對資料進行加密:123+5=128
- 接收方對資料進行解密:128-5 =123
即使黑客知道具體的演算法就是資料+動態整數,但是他不知道具體的動態整數是多少,也就無法解出原始的資料內容。這個動態整數稱之為金鑰。
下面介紹HTTPS中用到的加密演算法。
對稱演算法
對稱演算法比較簡單:加密和解密資料使用相同的金鑰 。如下圖:
對稱演算法的優點就是效率很高,可以對長資料進行加解密。但對稱演算法也存在缺點。
第一是雙方使用相同的金鑰,無法辨別資料到底是由伺服器加密還是客戶端加密,也就是無法區分一個訊息是由伺服器發出還是由客戶端發出。解決這個問題方法也很簡單:雙方加密使用不同的金鑰 。
第二,通訊雙方難以確保拿到安全的金鑰 。因為第一步總是需要通過網路通訊來商量金鑰,那可不可以使用固定的金鑰?前面講過,黑客也是一個客戶,那麼他也可以拿到金鑰,這個演算法就失去意義了。
解決這個問題的方法是:使用非對稱演算法
非對稱演算法
對稱演算法是加密解密使用相同的金鑰,而非對稱演算法是加密與解密使用不同的金鑰 。如下圖:
- 非對稱加密有兩把金鑰:公鑰和私鑰
- 公鑰可公開給所有人,私鑰必須自己保密,不給任何人拿到
- 客戶端可以使用伺服器的公鑰加密資料,而這份密文,只有伺服器的私鑰才能解開
- 反過來,使用私鑰加密的資料,也只有公鑰可以解開
非對稱演算法很好地解決了對稱演算法存在的問題:無法安全交換金鑰 。伺服器的公鑰可以公開給所有的使用者,當客戶端首次訪問伺服器,伺服器便把公鑰返回即可。
但是對於非對稱演算法有一個很嚴重的缺點:效能極差 。所以我們可以將對稱與非對稱演算法結合起來,解決上述問題。
對稱+非對稱
對稱演算法存在的問題是無法安全地互換金鑰;因此第一步我們可以使用非對稱演算法來交換金鑰,後續使用對稱演算法來進行通訊。如下圖:
- 當客戶訪問伺服器時,伺服器返回一個公鑰;
- 客戶端拿到公鑰之後,對客戶端金鑰使用公鑰進行加密之後傳送給服務端;
- 服務端拿到客戶端金鑰之後對服務端金鑰進行加密傳送給客戶端;
這樣就完成了雙方金鑰的交換,後續可以使用金鑰進行高效率通訊。
到此我們的網路傳輸依舊不是安全的,因為,我們無法保證第一步伺服器返回的公鑰不會被黑客篡改。假如黑客把伺服器返回的公鑰轉換成自己的公鑰,後續他就可以對客戶端的的所有訊息使用自己的私鑰解密。而問題的本質在於:我們無法辨別返回的資料是否是真的由伺服器返回的 。這個問題的解決方法就是:使用數字證書來證明資訊傳送方的身份 。
數字證書
經過前面加密演算法的討論,對稱+非對稱演算法已經可以解決大部分的網路安全問題。但第一步伺服器返回的公鑰仍舊有被黑客篡改的風險,因為我們無法確保通訊對方的身份。數字證書的引入,就是為了解決這個問題。
證書概述
數字證書是由公認的證書機構頒發給伺服器的一個用於驗證身份的數字認證。
數字證書可以用身份證來進行類比:
身份證是我們自身身份資訊的一個認證,頒發的機構是我們全國人民認可的公安局。
同理,伺服器的數字證書也是伺服器身份的一個認證,頒發的機構是網際網路中普遍認可的證書機構。
伺服器的證書中,包含有伺服器資訊例如公鑰等、證書籤名、證書機構資訊等。客戶端拿到伺服器的證書,進行證書驗證後,就可以準確得到伺服器的公鑰,利用這個公鑰,就可以實現上述的演算法加密了。
總之,數字證書的作用就是證明資料的來源,安全獲取到伺服器的公鑰進行加密通訊 。
證書驗證
客戶端如何驗證伺服器的證書呢?首先得看看證書是怎麼做出來的:
- 伺服器向證書機構申請證書,同時提供自己的域名、地址、公鑰等資訊;
- 證書機構對伺服器的資訊使用hash演算法得出一份128位的摘要,並對這份摘要使用自己的私鑰進行非對稱加密得到證書數字簽名。
- 證書機構把伺服器資訊(明文)+數字簽名+證書機構資訊(包含證書機構公鑰)傳送給伺服器
- 客戶端請求伺服器時,伺服器把證書返回給客戶端
客戶端驗證證書的重點就是:比較摘要 :
- 客戶端拿到證書,得到伺服器資訊、數字簽名、證書機構資訊
- 客戶端對伺服器資訊進行hash演算法計算得出一份摘要S1
- 客戶端使用證書機構的公鑰對數字簽名進行解密得到一份摘要S2
- 對比S1和S2即可辨別此證書是否來自伺服器且沒經過篡改
經過上面的證書驗證流程,客戶端就可以成功拿到伺服器的公鑰,進行下一步的加密流程。至於為什麼通過比較摘要即可知道證書安全,下面進行討論。
證書鏈
客戶端驗證證書的流程很簡單:使用證書機構公鑰解開證書的數字簽名後進行比對即可。但這裡有一個問題:如何保證證書機構的公鑰可信 ?假如黑客使用自己的私鑰加密,同時把證書機構的公鑰修改成自己的公鑰,那豈不是非常危險?
網際網路中的主機物件非常多,但證書機構卻不多。計算機產商,會在系統中安裝一些根證書機構的資訊,其中就包含了這些機構的公鑰。這些公鑰是在一定程度上是絕對安全的,是可以信任的。客戶端可以使用這些公鑰對數字簽名進行解密。安全問題,終於得到了完美的解決。
系統中預裝的證書機構是有限的,但世界上每時每刻申請數字證書卻非常多,他們“忙不過來”,因此有了二級證書機構。二級證書機構由根證書機構簽發,二級證書機構再去給伺服器簽發證書。那麼此時如何進行證書驗證呢?還是一樣的道理:
- 利用根證書機構給二級證書機構簽發的時候同樣是一份數字證書,其中包含了二級證書機構資訊、數字簽名、根證書機構資訊
- 伺服器的數字證書中包含了二級證書機構的數字證書
- 客戶端使用根證書機構的公鑰對二級證書機構的數字簽名進行解密得到摘要再進行比對,得到二級證書機構的公鑰
- 使用二級證書機構的公鑰對伺服器證書進行驗證
同理,三級、四級證書機構驗證都類同。在瀏覽器中,我們可以檢視網站的證書鏈:
可以看到這是一個包含了兩級證書機構的證書鏈,最頂層的證書機構,即是根證書機構。
hash演算法
我們會發現,證書並不是直接對伺服器資訊進行加密,而是利用hash演算法得到伺服器資訊的摘要,再對摘要進行加密。那這裡可能會有這些問題:
- 直接對資訊進行加密不可以嗎?為什麼多此一舉?
- 只對摘要進行加密,那麼原文內容不是洩露了嗎?
hash演算法最常用的就是MD5,他可以把一段資料轉化成一個128位的長度的摘要,不同的資料,會得到不同的摘要。
摘要的長度更短,使用非對稱加密的效率更高。因此,證書中對摘要而不是直接對資訊進行加密可以提高網路效率。而伺服器資訊本身並不是敏感資訊,不怕被黑客擷取監聽,所以可以使用明文傳輸。
hash演算法不僅為了提高效率,更重要的是可以辨別資訊是否遭受了篡改。
假如在證書中我們直接對伺服器資訊進行私鑰加密,黑客擷取到我們的資料後,他雖然看不懂,但是他可以直接對密文進行篡改。最後接收方解密之後得到的就是一分錯誤的資訊。
如果資訊是一個文字,我們可以很明顯地辨別出來;但如果是一個數字編號,那麼很難知道是否遭受了篡改。舉個例子:
- 伺服器傳送貨物編號123,對123進行加密之後得到098
- 黑客擷取後無法解密,將098修改成048之後傳送給客戶端
- 客戶端解密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安全連線建立流程就非常簡單了。基本就是把上面的流程走了一遍。先來看一張總體圖:
-
客戶端請求伺服器建立安全連線,附加客戶端支援的SSL與TLS版本、支援的加密演算法版本、隨機數。
加密演算法與安全協議版本有很多,但服務不一定支援最新版本的協議預演算法。所以客戶端把所以支援的版本傳送給伺服器,讓伺服器去選擇。
隨機數非常重要,前面講hash演算法的時候講到,隨機數是一個動態因子,讓hash演算法更加安全。同時,隨機數也參與了對稱金鑰的生成。
-
伺服器響應請求,附加選擇的協議版本、加密演算法版本、伺服器隨機數。
伺服器從客戶端支援的協議版本中,選擇一套自己最喜歡的。
為了辨別訊息是由哪一方加密併發出的,需要準備兩個對稱金鑰。因此伺服器也需要產生一個隨機數。
-
伺服器向客戶端傳送證書
伺服器向客戶端傳送自己證書,其中就包含了伺服器的公鑰。
-
伺服器傳送hello done表示hello階段結束
-
客戶端驗證證書,拿到伺服器公鑰;利用兩個隨機數,生成pre-master secret,並使用伺服器的公鑰加密傳送給伺服器。
證書驗證步驟參考上面的證書小節;
pre-master secret是一個非常重要的東西,雙方利用pre-master secret生成master-secret,利用前面的兩個隨機數生成兩個對稱加密金鑰和兩個HMAC金鑰,兩對金鑰分別用於客戶端加密和伺服器加密。
-
客戶端傳送changeCipherSpec提示伺服器此後使用pre-master secret產生的金鑰加密通訊
-
客戶端傳送FIN報文,表示結束
-
伺服器也傳送changeCipherSpec報文
-
伺服器也傳送FIN報文,表示結束
-
雙方可以開始安全通訊了
至此,對於HTTPS的加密流程,已經比較清晰了。
Android中運用
無論是HTTP還是HTTPS,事實上開源網路框架都已經為我們完成了這些粗活累活,例如okHttp。正常情況下,發起HTTP和HTTPS請求並沒有任何異同。但有時候會出現一些特殊的問題,就需要我們自己動手解決:
- 系統過於老舊,沒有安裝根證書。缺乏根證書的公鑰,那麼無法驗證伺服器證書是否安全。
- 自簽名證書。自己的app訪問自己的伺服器,有時候為了節約經費,可以自己給自己的伺服器簽名。
- 證書資訊缺乏關鍵資訊,如頒發證書的機構。
對於上面的問題,我們可以自己重寫證書驗證流程,或者在okHttp中新增信任的伺服器公鑰,可以解決上面的問題。
最後
HTTPS要解決的就是計算機網路中的安全問題,不同問題的解決方法要清楚:
- 防止訊息監聽:加密
- 防止訊息篡改:hash演算法
- 驗證身份:數字證書
HTTPS就是利用這些方法,為通訊雙方建立安全連線,從而來實現安全通訊。
如果文章對你有幫助,還希望可以留下您的點贊鼓勵一下作者~
全文到此,原創不易,覺得有幫助可以點贊收藏評論轉發。
有任何想法歡迎評論區交流指正。
如需轉載請評論區或私信告知。另外歡迎光臨筆者的個人部落格:傳送門