HTTPS(SSL/TLS)的加密機制雖然是個大家都應瞭解的基本問題,但網上的很多HTTPS入門文章總會忽略一些內容或表述不清,我學習它的時候也廢了挺大功夫。
對稱加密、非對稱加密、數字簽名、數字證書等等,在學習過程中,除了瞭解“它是什麼”,你是否有想過“為什麼是它”?我認為理解了後者才真正理解了HTTPS的加密機制。
本文以問題的形式逐步展開,一步步解開HTTPS的面紗,希望能幫助你徹底搞懂HTTPS。特別是對於瞭解過HTTPS卻在有些地方有所卡殼的人,希望本文能幫助你理清思路。
為什麼需要加密?
因為http的內容是明文傳輸的,明文資料會經過中間代理伺服器、路由器、wifi熱點、通訊服務運營商等多個物理節點,如果資訊在傳輸過程中被劫持,傳輸的內容就完全暴露了,他還可以篡改傳輸的資訊且不被雙方察覺,這就是中間人攻擊
。所以我們才需要對資訊進行加密。最直接的加密方式就是對稱加密
。
什麼是對稱加密?
就是有一個金鑰,它可以對一段內容加密,加密後只能用它才能解密看到原本的內容,和我們日常生活中用的鑰匙作用差不多。
用對稱加密可行嗎?
如果通訊雙方都各自持有同一個金鑰,且沒有別人知道,這兩方的通訊安全當然是可以被保證的(除非金鑰被破解)。
然而最大的問題就是這個金鑰怎麼讓傳輸的雙方知曉,同時不被別人知道。如果由伺服器生成一個金鑰並傳輸給瀏覽器,那這個傳輸過程中金鑰被別人劫持弄到手了怎麼辦?之後他就能用金鑰解開雙方傳輸的任何內容了,所以這麼做當然不行。
換種思路?試想一下,如果瀏覽器內部就預存了網站A的金鑰,且可以確保除了瀏覽器和網站A,不會有任何外人知道該金鑰,那理論上用對稱加密是可以的,這樣瀏覽器只要預存好世界上所有HTTPS網站的金鑰就行啦!這麼做顯然不現實。
怎麼辦?所以我們就需要神奇的非對稱加密
什麼是非對稱加密?
有兩把金鑰,通常一把叫做公鑰、一把叫做私鑰,用公鑰加密的內容必須用私鑰才能解開,同樣,私鑰加密的內容只有公鑰能解開。
用非對稱加密可行嗎?
鑑於非對稱加密的機制,我們可能會有這種思路:伺服器先把公鑰直接明文傳輸給瀏覽器,之後瀏覽器向伺服器傳資料前都先用這個公鑰加密好再傳,這條資料的安全似乎可以保障了!因為只有伺服器有相應的私鑰能解開這條資料。
然而由伺服器到瀏覽器的這條路怎麼保障安全?如果伺服器用它的的私鑰加密資料傳給瀏覽器,那麼瀏覽器用公鑰可以解密它,而這個公鑰是一開始通過明文傳輸給瀏覽器的,這個公鑰被誰劫持到的話,他也能用該公鑰解密伺服器傳來的資訊了。所以目前似乎只能保證由瀏覽器向伺服器傳輸資料時的安全性(其實仍有漏洞,下文會說),那利用這點你能想到什麼解決方案嗎?
改良的非對稱加密方案,似乎可以?
我們已經理解通過一組公鑰私鑰,已經可以保證單個方向傳輸的安全性,那用兩組公鑰私鑰,是不是就能保證雙向傳輸都安全了?請看下面的過程:
- 某網站擁有用於非對稱加密的公鑰A、私鑰A’;瀏覽器擁有用於非對稱加密的公鑰B、私鑰B’。
- 瀏覽器像網站伺服器請求,伺服器把公鑰A明文給傳輸瀏覽器。
- 瀏覽器把公鑰B明文傳輸給伺服器。
- 之後瀏覽器向伺服器傳輸的所有東西都用公鑰A加密,伺服器收到後用私鑰A’解密。由於只有伺服器擁有這個私鑰A’可以解密,所以能保證這條資料的安全。
- 伺服器向瀏覽器傳輸的所有東西都用公鑰B加密,瀏覽器收到後用私鑰B’解密。同上也可以保證這條資料的安全。
的確可以!拋開這裡面仍有的漏洞不談(下文會講),HTTPS的加密卻沒使用這種方案,為什麼?最主要的原因是非對稱加密演算法非常耗時,特別是加密解密一些較大資料的時候有些力不從心,而對稱加密快很多,看來必須得用對稱加密,那我們能不能運用非對稱加密的特性解決前面提到的對稱加密的問題?
非對稱加密+對稱加密?
既然非對稱加密耗時,非對稱加密+對稱加密結合可以嗎?而且得儘量減少非對稱加密的次數。當然是可以的,而且非對稱加密、解密各只需用一次即可。
請看一下這個過程:
- 某網站擁有用於非對稱加密的公鑰A、私鑰A’。
- 瀏覽器像網站伺服器傳送請求,伺服器把公鑰A明文給傳輸瀏覽器。
- 瀏覽器隨機生成一個用於對稱加密的金鑰X,用公鑰A加密後傳給伺服器。
- 伺服器拿到後用私鑰A’解密得到金鑰X。
- 這樣雙方就都擁有金鑰X了,且別人無法知道它。之後雙方所有資料都用金鑰X加密解密。
完美!HTTPS基本就是採用了這種方案。完美?還是有漏洞的。
中間人攻擊
中間人的確無法得到瀏覽器生成的金鑰B,這個金鑰本身被公鑰A加密了,只有伺服器才有私鑰A’解開拿到它呀!然而中間人卻完全不需要拿到金鑰A’就能幹壞事了。請看:
- 某網站擁有用於非對稱加密的公鑰A、私鑰A’。
- 瀏覽器向網站伺服器請求,伺服器把公鑰A明文給傳輸瀏覽器。
- 中間人劫持到公鑰A,儲存下來,把資料包中的公鑰A替換成自己偽造的公鑰B(它當然也擁有公鑰B對應的私鑰B’)。
- 瀏覽器隨機生成一個用於對稱加密的金鑰X,用公鑰B(瀏覽器不知道公鑰被替換了)加密後傳給伺服器。
- 中間人劫持後用私鑰B’解密得到金鑰X,再用公鑰A加密後傳給伺服器。
- 伺服器拿到後用私鑰A’解密得到金鑰X。
這樣在雙方都不會發現異常的情況下,中間人得到了金鑰B。根本原因是瀏覽器無法確認自己收到的公鑰是不是網站自己的。那麼下一步就是解決下面這個問題:
如何證明瀏覽器收到的公鑰一定是該網站的公鑰?
現實生活中,如果想證明某身份證號一定是小明的,怎麼辦?看身份證。這裡政府機構起到了“公信”的作用,身份證是由它頒發的,它本身的權威可以對一個人的身份資訊作出證明。網際網路中能不能搞這麼個公信機構呢?給網站頒發一個“身份證”?
數字證書
網站在使用HTTPS前,需要向“CA機構”申請頒發一份數字證書,數字證書裡有證書持有者、證書持有者的公鑰等資訊,伺服器把證書傳輸給瀏覽器,瀏覽器從證書裡取公鑰就行了,證書就如身份證一樣,可以證明“該公鑰對應該網站”。然而這裡又有一個顯而易見的問題了,證書本身的傳輸過程中,如何防止被篡改?即如何證明證書本身的真實性?身份證有一些防偽技術,數字證書怎麼防偽呢?解決這個問題我們就基本接近勝利了!
如何放防止數字證書被篡改?
我們把證書內容生成一份“簽名”,比對證書內容和簽名是否一致就能察覺是否被篡改。這種技術就叫數字簽名
:
數字簽名
這部分內容建議看下圖並結合後面的文字理解,圖中左側是數字簽名的製作過程,右側是驗證過程(原圖出處找不到了,可以看出來這圖已經被轉載了無數次了。。。)
數字簽名的製作過程:
- CA擁有非對稱加密的私鑰和公鑰。
- CA對證書明文資訊進行hash。
- 對hash後的值用私鑰加密,得到數字簽名。
明文和數字簽名共同組成了數字證書,這樣一份數字證書就可以頒發給網站了。
那瀏覽器拿到伺服器傳來的數字證書後,如何驗證它是不是真的?(有沒有被篡改、掉包)
瀏覽器驗證過程:
- 拿到證書,得到明文T,數字簽名S。
- 用CA機構的公鑰對S解密(由於是瀏覽器信任的機構,所以瀏覽器保有它的公鑰。詳情見下文),得到S’。
- 用證書裡說明的hash演算法對明文T進行hash得到T’。
- 比較S’是否等於T’,等於則表明證書可信。
為什麼這樣可以證明證書可信呢?我們來仔細想一下。
中間人有可能篡改該證書嗎?
假設中間人篡改了證書的原文,由於他沒有CA機構的私鑰,所以無法得到此時加密後簽名,無法相應地篡改簽名。瀏覽器收到該證書後會發現原文和簽名解密後的值不一致,則說明證書已被篡改,證書不可信,從而終止向伺服器傳輸資訊,防止資訊洩露給中間人。
既然不可能篡改,那整個證書被掉包呢?
中間人有可能把證書掉包嗎?
假設有另一個網站B也拿到了CA機構認證的證書,它想搞垮網站A,想劫持網站A的資訊。於是它成為中間人攔截到了A傳給瀏覽器的證書,然後替換成自己的證書,傳給瀏覽器,之後瀏覽器就會錯誤地拿到B的證書裡的公鑰了,會導致上文提到的漏洞。
其實這並不會發生,因為證書裡包含了網站A的資訊,包括域名,瀏覽器把證書裡的域名與自己請求的域名比對一下就知道有沒有被掉包了。
為什麼製作數字簽名時需要hash一次?
我初學HTTPS的時候就有這個問題,似乎以上過程中hash有點多餘,把hash過程去掉也能保證證書沒有被篡改。
最顯然的是效能問題,前面我們已經說了非對稱加密效率較差,證書資訊一般較長,比較耗時。而hash後得到的是固定長度的資訊(比如用md5演算法hash後可以得到固定的128位的值),這樣加密解密就會快很多。
當然還有安全上的原因,這部分內容相對深一些,感興趣的可以看這篇解答:crypto.stackexchange.com/a/12780
怎麼證明CA機構的公鑰是可信的?
你們可能會發現上文中說到CA機構的公鑰,我幾乎一筆帶過,“瀏覽器保有它的公鑰”,這是個什麼保有法?怎麼證明這個公鑰是否可信?
讓我們回想一下數字證書到底是幹啥的?沒錯,為了證明某公鑰是可信的,即“該公鑰是否對應該網站/機構等”,那這個CA機構的公鑰是不是也可以用數字證書來證明?沒錯,作業系統、瀏覽器本身會預裝一些它們信任的根證書,如果其中有該CA機構的根證書,那就可以拿到它對應的可信公鑰了。
實際上證書之間的認證也可以不止一層,可以A信任B,B信任C,以此類推,我們把它叫做信任鏈
或數字證書鏈
,也就是一連串的數字證書,由根證書為起點,透過層層信任,使終端實體證書的持有者可以獲得轉授的信任,以證明身份。
另外,不知你們是否遇到過網站訪問不了、提示要安裝證書的情況?這裡安裝的就是跟證書。說明瀏覽器不認給這個網站頒發證書的機構,那麼沒有該機構的根證書,你就得手動下載安裝(風險自己承擔XD)。安裝該機構的根證書後,你就有了它的公鑰,就可以用它驗證伺服器發來的證書是否可信了。
HTTPS必須在每次請求中都要先在SSL/TLS層進行握手傳輸金鑰嗎?
這也是我當時的困惑之一,顯然每次請求都經歷一次金鑰傳輸過程非常耗時,那怎麼達到只傳輸一次呢?用session就行。
伺服器會為每個瀏覽器(或客戶端軟體)維護一個session ID,在TSL握手階段傳給瀏覽器,瀏覽器生成好金鑰傳給伺服器後,伺服器會把該金鑰存到相應的session ID下,之後瀏覽器每次請求都會攜帶session ID,伺服器會根據session ID找到相應的金鑰並進行解密加密操作,這樣就不必要每次重新制作、傳輸金鑰了!
總結
可以看下這張圖,梳理一下整個流程(SSL、TSL握手有一些區別,不同版本間也有區別,不過大致過程就是這樣):
(出處:www.extremetech.com)
至此,我們已自下而上地打通了HTTPS加密的整個脈絡以及核心知識點,不知你是否真正搞懂了HTTPS呢?
找幾個時間,多看、多想、多理解幾次就會越來越清晰的!
那麼,下面的問題你是否已經可以解答了呢?
- 為什麼要用對稱加密+非對稱加密?
- 為什麼不能只用非對稱加密?
- 為什麼需要數字證書?
- 為什麼需要數字簽名?
…
當然,由於篇幅所限和能力所限,一些更深入的內容沒有覆蓋到。但我認為一般對於前後端工程師來說,瞭解到這步就夠了,有興趣的可以再深入研究~如有疏漏之處,歡迎指出。
這篇文章也是對我學習時的一些總結和我的一些理解,一共花了快一天才寫完。如果你喜歡這篇文章,歡迎點贊分享~感謝!