前言
很高興遇見你~
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就是利用這些方法,為通信雙方建立安全連接,從而來實現安全通信。
如果文章對你有幫助,還希望可以留下您的點贊鼓勵一下作者~
全文到此,原創不易,覺得有幫助可以點贊收藏評論轉發。
有任何想法歡迎評論區交流指正。
如需轉載請評論區或私信告知。另外歡迎光臨筆者的個人博客:傳送門