天天看點

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

網際網路應用的網絡通信一般都是通過 HTTP,但 HTTP 是明文傳輸的,容易洩漏資訊,是以大多數應用都會更新為 HTTPS。

HTTP 底層是用 TCP 傳輸的,HTTPS 就是在 TCP 和 HTTP 之間加了一層加密和認證的協定,這一層叫做 SSL/TLS。

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

為什麼叫這個名字呢?

因為最早的時候是 SSL 協定,但是後來發現了漏洞,就改為 TLS 協定了,而且 TLS 協定也在不斷的更新,從 1.1、1.2 到了現在的 TLS 1.3。

不管叫 SSL 還是叫 TLS,都是指的這一層。

這一層就實作了加密、身份認證,還有防篡改的功能。

加密很容易了解,就是通過一種加密算法對内容進行處理,生成密文,然後另一端通過解密算法把密文處理成原始内容。

但隻有加解密算法還不行,得加入一些随機性,讓每次都不一樣,是以會有密鑰的存在,每次先随機生成密鑰,然後通信過程就用這個密鑰配合加密算法來進行加解密。

這個密鑰是随機生成的,是以隻能一端生成以後告訴另一端。

那麼問題來了,怎麼把這個密鑰安全的告訴另一端呢?

這就得用到一種特殊的加密算法 --- 非對稱加密了。

這種加密算法特殊在有兩個密鑰,用一個密鑰加密的資料隻能另一個密鑰解密,那麼把一個密鑰暴露出去,一個密鑰留下,這樣用留下的密鑰加密的資料,别人都能解密,但是用暴露出去的密鑰加密的資料,隻有自己能解密。

這個暴露出去的密鑰就叫做公鑰,留下的密鑰叫做私鑰。

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

這樣當别人想給你傳遞一些資訊的時候,就通過你暴露出去的公鑰對資訊加密,别人都解密不了,隻有你能解密。這就保證了資訊傳遞的安全性。

就比如說上面提到的密鑰傳遞問題,就可以通過這種公私鑰的非對稱加密機制來解決。

有的同學可能會問,既然基于公私鑰的非對稱加密機制能保證安全,為啥還要再傳遞别的密鑰呢?

因為這種非對稱加密太慢了,傳遞幾次資訊還行,頻繁的用這種方式加解密資料的話效率太低了。是以一般隻用這種方式來傳遞會話密鑰,保證安全,然後後面用傳遞的會話密鑰來進行資料加解密。

前面提到 TLS 層主要是實作了加密、身份認證、防篡改的功能。

加密是用對稱加密的方式,用到的密鑰通過基于公私鑰的非對稱加密機制來傳遞。

那身份認證怎麼做呢?

其實也是通過公私鑰的機制,剛才提到了公鑰加密的内容隻能私鑰解密,這保證了資訊的安全傳遞。

那反過來,私鑰加密的資料,如果用公鑰能解開,這不就證明了資訊是你傳遞的麼?因為私鑰隻有你有。

是以,私鑰的加密又叫做簽名,可以用來做身份的認證。

那用私鑰加密什麼呢?

一般是對傳輸的資訊做一次 hash,生成資料指紋,然後用私鑰加密這個資料指紋,也就是對它進行簽名。

這樣資料傳遞到另一端,用公鑰把資料指紋取出來,再對内容做一次 hash,生成一份資料指紋,兩者對比一下,如果一樣,就說明沒有被篡改。

這就是 TLS 層的第三個功能,防篡改,也就是保證資料的完整性。

至此,HTTPS 給 HTTP 額外添加的加密、身份認證、防篡改功能的實作原理我們都了解了。

但不知道同學們有沒有發現這其中有個漏洞,非對稱加密的算法是公開的,你可以生成公私鑰,别人也可以,那怎麼保證我拿到的公鑰是你的呢?

萬一我拿到的公鑰是别人的,那我用它加密的資料,不就被别人截去了麼?

想解決這個問題就涉及到一個新的概念,數字證書了:

數字證書

現在的問題是怎麼驗證公鑰是某個人的。

如果我有信得過的人,他說這個公鑰是那個人的,我就相信。基于這樣的信任來驗證可以麼?

也就是說我信任的人有自己的公私鑰,他用私鑰對這段資訊簽名,我收到資訊後用他的公鑰來解密,發現能解密出其中的資訊,說明這是被他簽名過的,我就相信我收到的公鑰是可靠的。

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

這樣是可以的,但怎麼保證我收到的信任的人的公鑰是真的呢?

這就無限循環起來了。

現實中肯定不會這樣無限循環的,解決方式是作業系統内置了一批信得過的機構的公鑰,經過這些機構簽名的,就一定是對方的公鑰。

這種信得過的機構叫做 CA(Certification Authority),電子認證機構,經過 CA 認證的公鑰和相關資訊,就叫做數字證書。

作業系統内置了所有可信的 CA 的證書,也就是 CA 的公鑰和相關資訊,叫做根證書。

在 mac 下可以在鑰匙串裡看到系統内置的所有根證書:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?
什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

當你打開一個 https 的網站的時候,會把網站的證書下載下傳下來,看看是不是經過這些系統内置過的 CA 根證書所信任的證書,如果是,代表收到的網站的公鑰是可信的。

證書都是頒給某個域名的,也就是證明證書裡的公鑰是這個域名的。

比如打開 baidu.com ,檢視它的證書:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?
什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

你會看到這樣的三級證書,根證書是系統内置的 CA 的證書,它信任了一個中間的證書,然後這個中間證書信任了 baidu.com 的證書。(信任就是指用自己的私鑰做了簽名)

這個 CA頒發的根證書是内置在系統裡的,受信任的,是以也就也就信任了他信任的中間證書,進而信任了中間證書信任的 baidu.com 的證書,這是一條信任鍊。

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

你打開 taobao.com 也會看到是這樣的三級證書鍊:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

為什麼都是三級呢?

因為這樣萬一中級證書不能信任了,還可以讓根證書再找一個中級證書,因為信任根證書,也自然信任這個新的中級證書,但如果根證書直接信任某個網站的證書,萬一根證書被攻破不能信任了,那就找不到可以信任的了。

是以,三級證書會更安全一些。

也就是這樣的一條信任鍊:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

也就是說想給網站更新 HTTPS,得找 CA 申請個證書才行。

一般的雲計算平台都提供了代理申請的服務,不過一年的費用挺貴的,比如阿裡雲:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

如果我隻是想測試下 HTTPS,要花這麼大成本去 CA 申請個證書麼?

那倒不用,我們可以自己建立一個 CA 根證書,然後用它給自己頒發證書,這叫自簽名證書:

自簽名證書

當測試的時候,可以用 openssl 這個庫自己建立一個 CA 根證書。

第一步用 genrsa 指令生成私鑰:

openssl genrsa -out ca-key.pem -des 1024      

過程中要輸入密碼,這個是保護私鑰用的。

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

然後用 req 指令建立證書簽名請求,輸入域名和相關資訊:

openssl req -new -key ca-key.pem -out ca-csr.pem      

這個過程中要輸入一些資訊,最重要的是域名資訊,因為證書就是為了證明公鑰是這個域名的:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

然後用 x509 指令生成根證書:

openssl x509 -req -in ca-csr.pem -signkey ca-key.pem -out      

至此,根證書建立完了,産生了 ca-key.pem、ca-csr.pem、ca-cert.pem 三個檔案,分别是私鑰、證書簽名請求、根證書。

然後用這個根證書建立網站的證書。

也是同樣的三步:

用 genrsa 生成私鑰:

openssl genrsa -out server-key.pem 1024      

然後用 req 指令建立證書簽名請求。

openssl req -new -key server-key.pem -out server-csr.pem      

最重要的也是域名資訊:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

最後一步生成證書,但是這裡要指定前面生成的 QA 的根證書:

openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out      

這樣就産生了 server-key.pem、server-csr.pem、server-cert.pem 三個檔案,分别是網站的私鑰、證書簽名請求、證書。

用私鑰和證書就可以建立 https 服務了,我們使用 nodejs 來建立:

const https = require('https');
const fs = require('fs');

let options = {
  key: fs.readFileSync('./server-key.pem'),
  cert: fs.readFileSync('./server-cert.pem')
};

https.createServer(options,function(req,res){
    res.write('hello https');
    res.end();

}).listen(3000);      

然後浏覽器通路,就可以看到 https 服務傳回的内容:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

chrome 會标記為不安全,我們點開看一下證書:

會看到隻有一級 www.ggg.com 的證書,還沒有被信任。

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

這是因為簽發他的根證書沒有導入鑰匙串,我們導入一下:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

導入 ca-cert.pem,就可以在鑰匙串中找到 guangguangguang.com 的根證書,已經标記了是自簽名證書:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

再通路網站,就會看到二級的結構了:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

但是還沒有被信任,我們信任一下自簽名的根證書:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?
什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

再去網站看一下,就可以看到證書受信任了,因為頒發他的根證書受信任了:

什麼是 HTTPS 的證書信任鍊?自己給自己發行不行?

不過網站依然會标記為不安全,這是 chrome 的政策,不支援自簽名證書。

一般我們還是會向 CA 機構申請一個證書的,但是測試的時候可以自行建立一個 CA 根證書,然後給自己頒發網站的證書。

總結

HTTPS 就是在 TCP 和 HTTP 之間加了一個 SSL 或者叫 TLS 層,實作了加密、身份認證、防篡改的功能。

為了增加随機性,每次都要生成密鑰來做加解密,傳輸這個密鑰需要用到非對稱加密的公私鑰機制。

公鑰加密的内容隻有私鑰能解開,防止被竊取。

私鑰隻有一個人有,是以加密的内容可以用作身份認證,也就是簽名。

對内容做 hash,然後私鑰簽名,就能做到完整性校驗,防止被篡改。

但是如何保證拿到的公鑰一定是對方的,這是個複雜的問題。

現在的方案是系統内置了一些 CA 的根證書,然後這些 CA 證書頒發了一些網站的證書,如果通路網站拿到的證書是這些 CA 機構頒發的,那就是受信任的。

繼續閱讀