TLS完全指南(一):TLS和安全通信
我曾經以為互聯網安全總是處在社會和法律控制的範圍里,但是在學習 TLS 的 過程中才意識到真實的互聯網四處漏洞簡直就是是一個Sin City。而TLS相關知 識就像美女包包里的避孕套和槍一樣——我們希望永遠也用不上,但是當不得不用 的時候,我們希望在手邊。
加密技術
TLS 依賴兩種加密技術:
- 對稱加密(symmetric encryption)
- 非對稱加密(asymmetric encryption)
對稱加密
對稱加密的一方(比如小紅)用秘鑰 K 給文本 M 加密;另一方(比如小明)用 同一個秘鑰解密:
小紅 : C = E(M, K)小明 : M = D(C, K)
這有一個問題:當一方生成了秘鑰 K 之後得把 K 分享給另一方。但是穿越 Sin City 的道路危險中途很可能有人竊聽到 K,竊聽者就可以假扮雙方中的任何一 方與另一方通信。這叫中間人攻擊mim。
非對稱加密
非對稱加密利用成對的兩個秘鑰:K1 和 K2。小紅用其中一個加密文本,小明可 以用另一個解密文本。比如,小紅用 K1 加密,小明用 K2 解密:
小紅 : C = E(M, K1)小明 : M = D(C, K2)
這樣一來,雙方中的一方(比如小紅)可以生成 K1和K2,然後把其中一個秘鑰 (比如K1)私藏,稱為私鑰;另一個(比如K2)公開,稱為公鑰。另一 方(比如小明)得到公鑰之後,雙方就可以通信。
然並卵,中間人還是可能截獲公鑰 K2,然後自己弄一對秘鑰(κ1, κ2),然後 告訴小明說 κ2 是小紅的公鑰。這樣中間人每次可以用截獲的 K2 解密小紅髮給 小明的文本(甚至可能修改文本),再用 κ1 加密了發出去;小明用 κ2 解密接 收。
這裡有一個用OpenSSL工具生成密鑰對的例子。
數字簽名和CA
為了幫小明確定得到的公鑰確實是小紅的 K2,而不是中間人偽造的 κ2,牛人們 發明了*數字簽名(digital signature)*技術。
數字簽名的做法是:
- 小紅把自己的公鑰和ID(身份證號碼,或者域名)合為身份證申請(certificate signing request,CSR),
- 小紅把CSR發給一個德高望重的人(被稱為 certificate authority,CA),比如小亮,
- 小亮用自己的私鑰加密小紅的 CSR,得到的密文被稱為數字簽名(digital signature),
- 小亮把 signature 和 CSR 的明文合在一起稱為 CA簽署的身份證(CA signed certificate,CRT),發給小紅,
小紅:CSR = 小紅公鑰+小紅域名 signature = E(CSR, 小亮的私鑰) CRT = CSR + signature
這裡有一個用OpenSSL生成CSR的例子。 簽署 CSR的例子在這裡。
每當其他人(比如小明)找小紅聊天(建立HTTPS連接)的時候,小紅出示自己的小亮簽署的身份證。 拿到這個身份證的人,只要他是相信小亮的——在自己機器上安裝了小亮的身份證,就可以
- 從小亮的身份證中的小亮的CSR里提取小亮的公鑰;
- 然後用小亮的公鑰解密小紅的身份證中小亮的signature,得到一個小紅的CSR";
- 如果這個CSR"和小紅身份證中的CSR明文一致,則說明「這個小紅的身份證是小亮確認過並且簽名的」。
小明:小亮的公鑰 = 小亮的CRT.CSR.小亮的公鑰 CSR" = D(CRT.signature, 小亮的公鑰) if CSR" == CRT.CSR then OK
由此過程可以看出來:隨便誰都可以當CA——只要願意公開自己的公鑰,即可用自 己的私鑰去加密別人的認證。那我們要是信錯了 CA,被他擺一道怎麼辦?答案 是:沒辦法。我們選擇信任社會,要相信如果 CA 說謊,萬一被識破,就沒有人 再相信他了。現實中,很多操作系統(Windows、Mac OS X)和瀏覽器(Chrome、 Firefox、IE)會內置一些靠譜的 CA 的身份證。但是有沒有 CA 冒天下之大不 韙說謊呢?據傳說有一個自稱 CNNIC 的機構說過謊。
這個過程可以用下圖描述:
請注意,這只是一個示意圖,並不為了精準描述HTTPS協議的握手和通信過程。 圖中省略了一些重要的細節,比如握手之後的實際通信都是採用 對稱加密技術來實現的。
信任鏈
小亮如果擔心沒有人信任自己是個好 CA(就像沒人信CNNIC一樣),可以找一個 大家都信的 CA,比如老王,用老王的私鑰在小亮的身份證上簽名:
小亮:CSR = 小亮的公鑰+小亮域名 signature = E(CSR, 老王的私鑰) CRT = CSR + signature
如果瀏覽器或者操作系統里安裝了老王的公鑰則可以驗證「小亮的身份證是老王 確認並且簽名過的」。
這樣,小亮在簽署小紅的身份證的時候,可以在小紅身份證後面附上自己的身份 證。這樣小紅的身份證就有「兩頁」了。
當小明和小紅通信的時候:
- 小明會先要求小紅出示自己的身份證;
- 小明雖然不信任小亮,但是信任老王,所以小明可以用老王的身份證里的老 王的公鑰來驗證小紅身份證附帶的小亮的身份證,於是就可以信任小亮了;
- 然後小明用小亮身份證里的公鑰驗證小紅的身份證。
要是怕小明連自己也也不信任,老王可以再找一個小明信任的人來簽名確認自己 的身份證。這個過程可以不斷遞推,從而形成了一條信任鏈(trust of chain)chain。
根身份證和自簽名
信任鏈總會有個頂端,被稱為根身份證(root CA)。那麼根身份證是誰簽名 的呢?答案是:自己簽名。實際上,我們每個人都可以自己簽名認證自己的身份 證,得到自簽名的身份證(self-signed certificate)。具體過程是:
- 生成一對秘鑰:公鑰 K2 和私鑰 K1,
- 創建自己的 CSR,
- 用自己的秘鑰加密CSR得到signature,然後把CSR明文和signature一起發布。
任何人只要信任我們自簽名的身份證 CRT,也就可以用 CRT.CSR.K2 作為公鑰加 密要傳遞給我們的文本。我們可以用自己的私鑰 K1 來解密文本。
如果老王就是根CA了,那麼上述各位的身份證的信任鏈如下:
小紅:CSR = 小紅公鑰+小紅域名 signature = E(CSR, 小亮的私鑰) CRT = CSR + signature小亮:CSR = 小亮的公鑰+小亮域名 signature = E(小亮的CSR, 老王的私鑰) CRT = 小亮的CSR + signature老王:CSR = 老王的公鑰+老王的域名 signature = E(老王的CSR, 老王自己的私鑰) CRT = 老王的CSR + signature
這裡有一個用OpenSSL工具自簽署身份證的例子, 以及一個用自己創建的根身份證簽署其他身份證的例子。
雙方TLS認證
上述例子解釋了通信的一方如何驗證另一方的身份。這種情況的一個常見應用是: 我們通過瀏覽器訪問銀行的網頁。這裡的關鍵是,我們要能驗證銀行的身份證, 然後才敢於在網頁里輸入賬號和密碼。瀏覽器驗證銀行的身份證的過程如下:
- 在瀏覽器和銀行的HTTPS服務建立安全連接的過程中,銀行的HTTPS服務會把 它的身份證發給瀏覽器showcerts;
- 瀏覽器使用內置的CA的身份證來驗證銀行的身份證。
瀏覽器驗證了銀行的HTTPS服務的身份之後,就輪到銀行驗證瀏覽器的用戶的身份了:
- 瀏覽器展示銀行HTTPS服務發來的登陸頁面;
- 用戶在這個頁面里輸入賬號和密碼,銀行的HTTPS服務由此驗證用戶的身份。
在這個過程中,銀行HTTPS伺服器的身份是通過TLS身份證來驗證的。而我們(用 戶)的身份是通過我們輸入的賬號和密碼來驗證的。
有時通信的雙方都是程序(而不是人)。此時,讓一方輸入賬號和密碼,不如讓 雙方都通過TLS身份證來互相驗證方便。尤其是在很多分散式系統里,有多種類 型的程序互相通信,而不只是兩方通信。
比如在 Kubernetes 機群里,不光操作機群的客戶端程序 kubectl 要能驗證 Kubernetes master node(具體的說是 apiserver)的身份,才能放心地把包括 敏感信息(比如資料庫密碼)的計算作業提交給 apiserver。類似的, apiserver也要能驗證 kubectl 的身份,以確認提交作業的是公司的合法僱員, 而不是外賊sign。
為此,通信各方都需要有各自的身份證。一個公司可以自簽名一個CA身份證,並 且用它來給每個僱員以及每個程序簽署身份證。這樣,只要每台電腦上都預先安 裝好公司自己的CA身份證,就可以用這個身份證驗證每個僱員和程序的身份了。 這是目前很多公司的常用做法。
雙向認證和通信的過程請參見下圖
加密和解密的性能
因為TLS模式下所有傳輸的數據都是加密的,大家會關注加密和解密的性能。客 觀的說,非對稱加密技術的加密和解密比較慢,相對來說,對稱加密技術的加密 解密過程更快。所以實際的連接和握手過程中,通信雙方會協商一個對稱加密秘 鑰,之後的數據通信過程中的加密都是利用對稱加密技術來實現的。
具體的做法是:握手的時候,雙方各自生成一個隨機數,並且以非對稱加密的方式 分享給對方。然後每一方都把自己的隨機數和對方的隨機數拼起來,就是接下來 通信時候使用的對稱加密方法的秘鑰了。
下一步
上面介紹的概念在實際操作中往往是靠開源工具 openssl 實現的。 下一篇介紹如何使用 openssl。
參考文獻
mim Man-in-the-middle attack
chain Chain of trust - Wikipedia
sign CoreOS
推薦閱讀: