如何使用自簽名 CA 和證書來保護我的在公網上的內容

做爲一個喜歡折騰的人,我的搞了不少東西放在本身的服務器上,可是爲了方便,可以在世界各地隨時隨地的打開查看和使用,我將服務器暴露到了公網中,固然了有些在公有云上的原本就暴露出來的。html

那麼這裏就有一個問題,我如何保護個人信息只能我來查看呢?git

  • 最簡單的方法就是經過 HTTP Basic Auth + HTTPS。記住必定要上 https,不然你的密碼也是會泄漏的。爲何說簡單呢?由於只須要在 Nginx 或 Traefik 上配置下就能夠了。可是這個方案有一個很是麻煩的問題,就是過一段時間以後就要輸入用戶名和密碼。時間短了,到無所謂,時間一長就會以爲很煩。
  • 構建一套 token 驗證體系,不論是使用 oauth 也好仍是 jwt 也好,都是能夠的。安全性也是能夠保證的,並且設置好 token 的時間長度,也能保證避免頻繁的輸入密碼。可是這有一個問題就是實現起來太過於複雜,都快遇上公司的一套系統了。並且還要有各類登陸頁面,想一想都煩。
  • 與上面相似,不過驗證方式使用 Two Auth,也就是基於時間的 6 位數。可是依舊比較複雜。

我想了許久,有沒有一種不須要輸入密碼,就能夠驗證安全的呢?由於是我一我的使用的,因此我根本不須要多用戶系統,也就是說驗證方式只須要一個密碼就能夠了。這我忽然想起了以前在寫 gRPC 的時候有一個雙向驗證的參數,也能夠驗證客戶端能夠不能夠。當時以爲只是他們基於 h2 改的協議,結果我一查發現這原來就包含在 https 裏面,準確說是 SSL 規範裏面。(怪本身當初上計算機網絡的時候沒好好學這部分,居然連這個都不知道)github

那麼至此,思路就很清晰了,給個人全部我的服務都添加 https 客戶端校驗。只要個人證書夠安全,個人頁面就是安全的(反正都是我我的的東西,直接拿着 U 盤處處拷貝,手機 Pad 用數據線發送,我就不信這樣誰還能盜走個人證書,傲嬌臉)算法

關於 SSL 證書的一些知識

  • 生成證書咱們主要採用 openssl 具體的安裝教程我就不講解了,有興趣的小夥伴自行查閱,主要有下面幾個步驟:
    • openssl genrsa:生成 Private Key,用於生成請求文件使用,這裏用 .key 後綴。
    • openssl req:依賴上面生成的 Key 去生成 CSR,也就是證書請求文件。使用 .csr 後綴。這期間要填寫一些信息,前面的幾個大寫字母是縮寫,後面在命令行使用的時候會用到。
      • C(Country) 國家
      • ST(State/Province) 州或者省
      • L(Locality) 地區,國內寫區便可
      • O(Organization) 組織
      • OU(Organization) 組織單位
      • CN(Common Name) 通用名,這個是很是重要的,影響了證書的顯示名稱和 HTTPS 的域名。
    • openssl x509:根據 x509 規範,利用 CA 的證書和私鑰將 CSR 文件加密成真正可使用到的證書。使用 .crt 後綴
  • SSL 證書必需要採用 sha-2 加密算法。2015 年 12 月 31 日前,CA 機構還會頒發 SHA-1 簽名的證書,可是以後只會簽發 SHA-2 簽名的證書了。Chrome 也會對 SHA-1 簽名的證書提示不安全。在 openssl 中用的是 -sha-256 參數。
  • CRTPEM 的關係,你們能夠簡單的認爲 PEM 是將證書 base64 以後的文件,而 CRT 是既能 base64 也能 binary 的一種文件格式。可是一般 openssl 產出的是 base64 的文件,你能夠經過 -outform 參數控制產出的類型。

CA 的生成

有了 CA 咱們才能去給其餘的證書籤名,生成 CA 的過程很簡單api

建立根鑰

這個祕鑰很是重要,任何得到了這個祕鑰的人在知道密碼的狀況下均可以生成證書。因此請當心保存瀏覽器

openssl genrsa -des3 -out root.key 4096
複製代碼
  • -des3 標明瞭私鑰的加密方式,也就是帶有密碼。建議添加密碼保護,這樣即便私鑰被竊取了,依舊沒法對其餘證書籤名。你也能夠更換其餘的加密方式,具體的請自行 help。
  • 4096 表示祕鑰的長度。

建立自簽名證書

由於是 CA 證書,因此無法讓別人去簽名,只能自簽名。這裏能夠認爲是生成 CSR 和簽名兩部合成一步走。安全

openssl req -x509 -sha256 -new -key root.key -sha256 -days 1024 -out root.crt
複製代碼

服務端證書生成

生成證書私鑰

openssl genrsa -out your-domain.com.key 2048
複製代碼

和 CA 證書不一樣,這個私鑰通常不須要加密,長度也能夠短一些。服務器

生成證書請求文件

openssl req -new -key your-domain.com.key -out your-domain.com.csr
複製代碼

這期間要填入一些信息,注意 CN 的名字必定要是你的域名。網絡

使用 CA 對 CSR 簽名

在 Chrome 58 以前,Chrome 會根據 CN 來檢查訪問的域名是否是和證書的域名一致,可是在 Chrome 58 以後,改成使用 SAN(Subject Alternative Name) 而不是 CN 檢查域名的一致性。dom

而 SAN 屬於 x509 擴展裏面的內容,因此咱們須要經過 -extfile 參數來指定存放擴展內容的文件。

因此咱們須要額外建立一個 your-domain.com.ext 文件用來保存 SAN 信息,經過指定多個 DNS 從而能夠實現多域名證書。

subjectAltName = @alt_names

[alt_names]
DNS.1 = your-domain.com
DNS.2 = *.your-domain.com
DNS.3 = *.api.your-domain.com
複製代碼

以此類推,若是域名較少,還能夠用另一種簡寫方案。

subjectAltName = DNS: your-domain.com, DNS: *.your-domain.com
複製代碼

關於語法的更多內容請查看官方文檔。在有了 ext 文件以後就直接能夠開始簽名了。

openssl x509 -req -sha256 -in your-domain.com.csr -CA root.crt -CAkey root.key -CAcreateserial -out your-domain.com.crt -days 365 -extfile your-domain.com.ext
複製代碼

CAcreateserial 這個參數是比較有意思的,意思是若是證書沒有 serial number 就建立一個,由於咱們是簽名,因此確定會建立一個。序列號在這裏的做用就是惟一標識一個證書,當有兩個證書的時候,只有給這兩個證書籤名的 CA 和序列號都同樣的狀況下,咱們才認爲這兩個證書是一致的。除了自定生成,還能夠經過 -set_serial 手動指定一個序列號。

當使用 -CAcreateserial 參數以後,會自動建立一個和 CA 文件名相同的,可是後綴是 .srl 的文件。這裏存儲了上一次生成的序列號,每次調用的時候都會讀取並 +1 。也就是說每一次生成的證書的序列號都比上一次的加了一。

如今,只須要將 your-domain.com.crtyour-domain.com.key 放到服務端就可使用了。別忘了將 CA 添加系統當中,要否則瀏覽器訪問會出現問題。

客戶端證書生成

服務端有了以後,就須要生成客戶端的證書,步驟和服務端基本一致,可是不須要 SAN 信息了。

openssl genrsa -out client.key 2048
# 這裏也能夠採用非交互形式,方便製做成命令行工具
openssl req -new \
	-key client.key \
	-subj "/C=CN/ST=Zhejiang/O=X/CN=*.your-domain.com" \ # 這裏的縮寫就是文章一開始所說的那些縮寫
	-out client.csr
openssl x509 -req -in client.csr -CA root.crt -CAkey root.key -out client.crt -days 365
複製代碼

只不過客戶端驗證須要的是 PKCS#12 格式,這種格式是將證書和私鑰打包在了一塊兒。由於系統須要知道一個證書的私鑰和公鑰,而證書只包含公鑰和簽名,不包含私鑰,因此須要這種格式的溫江將私鑰和公鑰都包含進來。

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
複製代碼

這期間會提示你輸入密碼,用於安裝的時候使用。也就是說不是什麼人均可以安裝客戶端證書的,要有密碼才行,這無疑又增長了必定的安全性。固然了,我試過不輸入密碼,可是好像有點問題,有興趣的同窗能夠本身嘗試下。

客戶端校驗證書的使用

這裏以 Node.js 舉例。使用 https 模塊,在建立的時候和普通的建立方式基本一致,可是須要額外指定 requestCertca 參數來開啓客戶端校驗。

https.createServer({
	key: fs.readFileSync('your-domain.com.key'),
	cert: fs.readFileSync('your-domain.com.crt'),
	requestCert: true,
	ca: [fs.readFileSync('root.crt')], // 校驗客戶端證書的 CA
}, (req, resp) => {
	// blahblah
})
複製代碼

這樣只要客戶端沒有安裝合法的證書,那麼整個請求就是失敗的。並且根本不會進入請求處理的回調函數中,這也意味着顯示的錯誤是瀏覽器的默認錯誤。那麼這對用戶來說其實不太友好。

那麼咱們能夠經過在參數中添加 rejectUnauthorized: false 來關閉這個功能,也就是說無論客戶端證書校驗是正確仍是失敗,均可以進入正常的回調流程。此時咱們只須要經過 req.client.authorized 來判斷這個請求是否經過了客戶端證書的校驗,能夠給予用戶更詳盡的錯誤提示。

另外咱們還能夠經過 resp.connection.getPeerCertificate() 獲取客戶端證書的信息,甚至能夠根據不一樣的信息選擇給予不一樣的用戶權限。

這裏有一個 DEMO: www.xgheaven.net.cn:3443,你們打開以後應該會看到一個證書校驗失敗的提示。這裏要說下,我這裏的 DEMO 沒有使用自簽名的服務端證書,只是使用了自簽名的 CA 去檢查客戶端證書。由於用本身簽名的服務端證書的話,瀏覽器會提示不安全,由於用戶麼有安裝自簽名的 CA。

能夠點擊下載客戶端證書按鈕,安裝客戶端證書。由於客戶端證書是有密碼保護的,請輸入頁面上提示的密碼。

再次刷新,若是是 Mac 系統,會提示你要使用哪一個客戶端證書登陸,此時就說明安裝成功了。

點擊確認,可能還要輸入一個系統密碼容許 Chrome 訪問 Keychain,一勞永逸的話在輸入密碼以後選擇 Always Allow,今後就不須要再輸入密碼了。

按照道理,你就能夠看到這個頁面了。

結語

有了這個功能,我就能夠將個人全部內容全盤私有化並且還能直接暴露在公網中。配合以前畢設搞的微服務化,簡直不要美滋滋。若是以前是使用帳號密碼登陸的,也能夠接入這個方案。就是將登陸頁面替換成證書校驗就能夠了。

Refs

相關文章
相關標籤/搜索