細說 CA 和證書

贊 回覆發表於2016-05-01

CA,Catificate Authority,它的作用就是提供證照(即伺服器證照,由域名、公司資訊、序列號和簽名資訊組成)加強服務端和客戶端之間資訊互動的安全性,以及證照運維相關服務。任何個體/組織都可以扮演 CA 的角色,只不過難以得到客戶端的信任,能夠受瀏覽器預設信任的 CA 大廠商有很多,其中 TOP5 是 Symantec、Comodo、Godaddy、GolbalSign 和 Digicert。

可以通過兩個維度來分類,一個是商業角度,一個是業務角度。

 

 單域名多域名泛域名多泛域名
DV 支援 不支援
OV 支援
EV 支援 不支援
舉例 www.barretlee.com www.barretlee.com
www.xiaohuzige.com
www.barret.cc
*.barretlee.com *.barretlee.com
*.xiaohuzige.com
*.barret.cc

需要強調的是,不論是 DV、OV 還是 EV 證照,其加密效果都是一樣的! 它們的區別在於:

  • DV(Domain Validation),面向個體使用者,安全體系相對較弱,驗證方式就是向 whois 資訊中的郵箱傳送郵件,按照郵件內容進行驗證即可通過;
  • OV(Organization Validation),面向企業使用者,證照在 DV 證照驗證的基礎上,還需要公司的授權,CA 通過撥打資訊庫中公司的電話來確認;
  • EV(Extended Validation),開啟 Github 的網頁,你會看到 URL 位址列展示了註冊公司的資訊,這會讓使用者產生更大的信任,這類證照的申請除了以上兩個確認外,還需要公司提供金融機構的開戶許可證,要求十分嚴格。

OV 和 EV 證照相當昂貴,使用方可以為這些頒發出來的證照買保險,一旦 CA 提供的證照出現問題,一張證照的賠償金可以達到 100w 刀以上。

前文 HTTPS證照生成原理和部署細節 提到如果本地生成公/私鑰對和對應未簽證的證照,如果使用的證照沒有簽證,或者未在瀏覽器受信的 CA 簽證,你會看到下圖的問題:

net:ERR_CERT_AUTHORITY_INVALID

上圖出現的錯誤是 net:ERR_CERT_AUTHORITY_INVALID,我們生成證照和公/私鑰對的流程都是正確的,但是瀏覽器不認這張證照,並且提示證照授權不通過;如果通過其他與 Common Name 不同的域名去訪問,如我註冊的時候使用的 localhost,但是訪問的時候用的 127.0.0.1,還會報出這樣的錯誤:

net:ERR_CERT_COMMON_NAME_INVALID

錯誤碼為 net:ERR_CERT_COMMON_NAME_INVALID,意思是 Common Name 不匹配,具體校驗流程可以在瀏覽器的 DevTools 中看到:

DevTools

從上面幾張圖,可以大致瞭解 CA 和證照會做哪些事情,證照由域名、公司資訊、序列號和簽名資訊組成,當我們通過 HTTPS 訪問頁面時,瀏覽器會主動驗證證照資訊是否匹配,也會驗證證照是否有效。

CA 有權給所有的域名簽發證照,如它可以私自給我的網站簽發一張 www.barretlee.com的證照,並且可以拿著新證照攔截網頁流量(當然,前提是這個 CA 是瀏覽器認證的權威 CA),那我的網站可能就很不安全了,對擁新證照的人來說,我的網站等同於在 HTTP 下進行通訊。

CA 供應商很多,提供服務的側重點可能也存在一些差異,比如很多 CA 都沒有提供證照吊銷的服務,這一點對於安全性要求很高的企業來說是完全不能接受的,那麼對 CA 供應商的評估需要注意寫什麼呢?

1. 內建根

所謂內建根,就是 CA 的根證照內建到各種通用的系統/瀏覽器中,只有根證照的相容性夠強,它所能覆蓋的瀏覽器才會越多。

2. 安全體系

兩個指標可以判斷 CA 供應商是否靠譜,一是看價格,價格高自然有它的理由,必然提供了全套的安全保障體系;二是看黑歷史,該 CA 供應商有沒有爆出過什麼漏洞,比如之前的 DigiNotar,被伊朗入侵,簽發了 500 多張未授權的證照,結果直接被各系統/瀏覽器將其根拉入黑名單,毫無疑問公司直接倒閉。

3. 核心功能和擴充套件功能

這就需要從業務上考慮了,不同的規模的企業、不同的業務對證照的要求不一樣,比如證照是否會考慮無 SNI 支援的瀏覽器問題,是否支援在 reissue 的時候新增域名,是否支援 CAA,是否支援短週期證照等等。

4. 價格

企業完全沒必要購買 Github 那樣的 EV 證照,太昂貴,而且一般的企業也未必能夠申請到這樣的證照。供應商很大,價格可以好好評估下,不一定要最貴,最適合的就行。

OpenSSL 是一個免費開源的庫,它提供了構建數字證照的命令列工具,其中一些可以用來自建 Root CA。

很多網站都希望使用者知道他們建立的網路通道是安全的,所以會想 CA 機構購買證照來驗證 domain,所以我們也可以在很多 HTTPS 的網頁位址列看到一把小綠鎖。

然而在一些情況下,我們沒必要去 CA 機構購買證照,比如在內網的測試環境中,為了驗證 HTTPS 下的一些問題,我們不需要部署昂貴的證照,這個時候自建 Root CA,給自己頒發證照就顯得很有價值了。

本節內容較多,主要是程式碼演示生成證照和驗證的過程,可以跳過看下一節,直接看 這裡

  • git clone https://github.com/barretlee/autocreate-ca.git
  • 依次執行 install-rootCA.shinstall-intermediateCA.sh 和 install-websiteConfig.sh

首先找到一個放置證照的資料夾,比如 /root/ca 下,下方的測試也在改目錄下,如果你要更換其他目錄,記得替換下文中的目錄地址。

扮演 CA 角色,就意味著要管理大量的 pair 對,而原始的一對 pair 對叫做 root pair,它包含了 root key(ca.key.pen)和 root certificate(ca.cert.pem)。通常情況下,root CA 不會直接為伺服器或者客戶端簽證,它們會先為自己生成幾個中間 CA(intermediate CAs),這幾個中間 CA 作為 root CA 的代表為伺服器和客戶端簽證。

注意:一定要在絕對安全的環境下建立 root pair,可以斷開網路、拔掉網線和網路卡,當然,如果是測試玩一玩就不用這麼認真了。

設定資料夾結構,並且配置好 openssl 設定:

# cd /root/ca
# mkdir certs crl newcerts private
# chmod 700 private
# touch index.txt
# echo 1000 > serial
# wget -O /root/ca/openssl.cnf \ 
    https://raw.githubusercontent.com/barretlee/autocreate-ca/master/cnf/root-ca

建立 root key,密碼可為空,設定許可權為只可讀:

# cd /root/ca
# openssl genrsa -aes256 -out private/ca.key.pem 4096

Enter pass phrase for ca.key.pem: secretpassword
Verifying - Enter pass phrase for ca.key.pem: secretpassword

# chmod 400 private/ca.key.pem

建立 root cert,許可權設定為可讀:

# cd /root/ca
# openssl req -config openssl.cnf \
      -key private/ca.key.pem \
      -new -x509 -days 7300 -sha256 -extensions v3_ca \
      -out certs/ca.cert.pem

Enter pass phrase for ca.key.pem: secretpassword
You are about to be asked to enter information that will be incorporated
into your certificate request.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name []:Zhejiang
Locality Name []:
Organization Name []:Barret Lee
Organizational Unit Name []:Barret Lee Certificate Authority
Common Name []:Barret Lee Root CA
Email Address []:

# chmod 444 certs/ca.cert.pem

驗證證照:

# openssl x509 -noout -text -in certs/ca.cert.pem

正確的輸出應該是這樣的:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            87:e8:c0:a0:4b:e2:12:5d
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=Zhejiang, O=Barret Lee, OU=Barret Lee Certificate Authority, CN=Barret Lee Root CA
        Validity
            Not Before: Apr 23 05:46:36 2016 GMT
            Not After : Apr 18 05:46:36 2036 GMT
        Subject: C=CN, ST=Zhejiang, O=Barret Lee, OU=Barret Lee Certificate Authority, CN=Barret Lee Root CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (4096 bit)
                Modulus (4096 bit):
                    // ...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                E5:2D:B8:2B:DC:88:FE:CE:DA:93:D8:6F:2E:74:04:D2:39:E7:C8:03
            X509v3 Authority Key Identifier:
                keyid:E5:2D:B8:2B:DC:88:FE:CE:DA:93:D8:6F:2E:74:04:D2:39:E7:C8:03

            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
    Signature Algorithm: sha256WithRSAEncryption
        // ...

包含:

  • 數字簽名(Signature Algorithm)
  • 有效時間(Validity)
  • 主體(Issuer)
  • 公鑰(Public Key)
  • X509v3 擴充套件,openssl config 中配置了 v3_ca,所以會生成此項

目前我們已經擁有了 Root Pair,事實上已經可以用於證照的發放了,但是由於根證照很乾淨,特別容易被汙染,所以我們需要建立中間 pair 作為 root pair 的代理,生成過程同上,只是細節略微不一樣。

生成目錄結構和 openssl 的配置,這裡的配置是針對 intermediate pair 的:

# mkdir /root/ca/intermediate
# cd /root/ca/intermediate
# mkdir certs crl csr newcerts private
# chmod 700 private
# touch index.txt
# echo 1000 > serial
# echo 1000 > /root/ca/intermediate/crlnumber
# wget -O /root/ca/openssl.cnf \
    https://raw.githubusercontent.com/barretlee/autocreate-ca/master/cnf/intermediate-ca

建立 intermediate key,密碼可為空,設定許可權為只可讀:

# cd /root/ca
# openssl genrsa -aes256 \
      -out intermediate/private/intermediate.key.pem 4096

Enter pass phrase for intermediate.key.pem: secretpassword
Verifying - Enter pass phrase for intermediate.key.pem: secretpassword

# chmod 400 intermediate/private/intermediate.key.pem

建立 intermediate cert,設定許可權為只可讀,這裡需要特別注意的一點是 Common Name 不要與 root pair 的一樣 :

# cd /root/ca
# openssl req -config intermediate/openssl.cnf -new -sha256 \
      -key intermediate/private/intermediate.key.pem \
      -out intermediate/csr/intermediate.csr.pem

Enter pass phrase for intermediate.key.pem: secretpassword
You are about to be asked to enter information that will be incorporated
into your certificate request.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name []:Zhejiang
Locality Name []:
Organization Name []:Barret Lee
Organizational Unit Name []:Barret Lee Certificate Authority
Common Name []:Barret Lee Intermediate CA
Email Address []:

使用 v3_intermediate_ca 擴充套件簽名,密碼可為空,中間 pair 的有效時間一定要為 root pair 的子集:

# cd /root/ca
# openssl ca -config openssl.cnf -extensions v3_intermediate_ca \
      -days 3650 -notext -md sha256 \
      -in intermediate/csr/intermediate.csr.pem \
      -out intermediate/certs/intermediate.cert.pem

Enter pass phrase for ca.key.pem: secretpassword
Sign the certificate? [y/n]: y

# chmod 444 intermediate/certs/intermediate.cert.pem

此時 root 的 index.txt 中將會多出這麼一條記錄:

V 260421055318Z   1000  unknown .../CN=Barret Lee Intermediate CA

驗證中間 pair 的正確性:

# openssl x509 -noout -text \
      -in intermediate/certs/intermediate.cert.pem
# openssl verify -CAfile certs/ca.cert.pem \
      intermediate/certs/intermediate.cert.pem

intermediate.cert.pem: OK

瀏覽器在驗證中間證照的時候,同時也會去驗證它的上一級證照是否靠譜,建立證照鏈,將 root cert 和 intermediate cert 合併到一起,可以讓瀏覽器一併驗證:

# cat intermediate/certs/intermediate.cert.pem \
      certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem
# chmod 444 intermediate/certs/ca-chain.cert.pem

終於到了這一步,生成我們伺服器上需要部署的內容,上面已經解釋了為啥需要建立中間證照。root pair 和 intermediate pair 使用的都是 4096 位的加密方式,一般情況下伺服器/客戶端證照的過期時間為一年,所以可以安全地使用 2048 位的加密方式。

# cd /root/ca
# openssl genrsa -aes256 \
      -out intermediate/private/www.barretlee.com.key.pem 2048
# chmod 400 intermediate/private/www.barretlee.com.key.pem

建立 www.barretlee.com 的證照:

# cd /root/ca
# openssl req -config intermediate/openssl.cnf \
      -key intermediate/private/www.barretlee.com.key.pem \
      -new -sha256 -out intermediate/csr/www.barretlee.com.csr.pem

Enter pass phrase for www.barretlee.com.key.pem: secretpassword
You are about to be asked to enter information that will be incorporated
into your certificate request.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name []:Zhejiang
Locality Name []:Hangzhou
Organization Name []:Barret Lee
Organizational Unit Name []:Barret Lee's Personal Website
Common Name []:www.barretlee.com
Email Address []:barret.china@gmail.com

使用 intermediate pair 簽證上面證照:

# cd /root/ca
# openssl ca -config intermediate/openssl.cnf \
      -extensions server_cert -days 375 -notext -md sha256 \
      -in intermediate/csr/www.barretlee.com.csr.pem \
      -out intermediate/certs/www.barretlee.com.cert.pem
# chmod 444 intermediate/certs/www.barretlee.com.cert.pem

可以看到 /root/ca/intermediate/index.txt 中多了一條記錄:

V 170503055941Z   1000  unknown .../emailAddress=barret.china@gmail.com

驗證證照:

# openssl x509 -noout -text \
      -in intermediate/certs/www.barretlee.com.cert.pem
# openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \
      intermediate/certs/www.barretlee.com.cert.pem

www.barretlee.com.cert.pem: OK

此時我們已經拿到了幾個用於部署的檔案:

  • ca-chain.cert.pem
  • www.barretlee.com.key.pem
  • www.barretlee.com.cert.pem

雙擊 /root/ca/intermediate/certs/ca-chain.cert.pem 將證照安裝到系統中,目的是讓本機信任這個 CA,將其當作一個權威 CA,安裝 root pem 或者 intermediate chain pem 都是可以的,它們都具備驗證能力。如果不執行這一步,瀏覽器依然會提示net:ERR_CERT_AUTHORITY_INVALID

上面申請測試證照時,我設定的 Common Name 為 www.barretlee.com,由於不線上上機器測試,可以將其新增到 hosts:

127.0.0.1 www.barretlee.com

執行下方測試程式碼:

// https-server.js
var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('/root/ca/intermediate/private/www.barretlee.com.key.pem'),
  cert: fs.readFileSync('/root/ca/intermediate/certs/www.barretlee.com.cert.pem'),
  passphrase: 'passoword' // 如果生成證照的時候設定了密碼,請新增改引數和密碼
};

https.createServer(options, function(req, res) {
  res.writeHead(200);
  res.end('hello world');
}).listen(8000, function(){
  console.log('Open URL: https://www.barretlee.com:8000');
});

可以看到這樣的效果:

小綠鎖出來了

檢視證照的詳細資訊:

證照的詳細資訊

回到最初的問題:

然而在一些情況下,我們沒必要去 CA 機構購買證照,比如在內網的測試環境中,為了驗證 HTTPS 下的一些問題,我們不需要部署昂貴的證照,這個時候自建 Root CA,給自己頒發證照就顯得很有價值了。

一般公司內網的電腦都會強制安裝一些安全證照,此時就可以把我們自建自簽名的證照匯入/引導安裝到使用者的電腦中啦~

很多公司由於業務眾多,域名也是相當多的,為了方便運維,會讓很多域名指向同樣的 ip,然後統一將流量/請求分發到後端,此時就會面臨一個問題:由於 TLS/SSL 在 HTTP 層之下,客戶端和伺服器握手的時候還拿不到 origin 欄位,所以伺服器不知道這個請求是從哪個域名過來的,而伺服器這邊每個域名都對應著一個證照,伺服器就不知道該返回哪個證照啦。

SNI 就是用來解決這個問題的,官方解釋是

SNI(Server Name Indication)是為了解決一個伺服器使用多個域名和證照的SSL/TLS擴充套件。一句話簡述它的工作原理就是,在連線到伺服器建立SSL連結之前先傳送要訪問站點的域名(Hostname),這樣伺服器根據這個域名返回一個合適的證照。

然後有將近 25% 的瀏覽器不支援該欄位的擴充套件,這個問題有兩個通用解決方案:

  • 使用 VIP 伺服器,每個域名對應一個 VIP,然後 VIP 與統一接入服務對接,通過 ip 來分發證照,不過運維成本很高,可能也需要大量的 VIP 伺服器
  • 採用多泛域名,將多個泛域名證照打包進一個證照,可以看看淘寶頁面的證照taobao cert它的缺點是每次新增域名都需要更新證照。

1. 證照選擇

證照有多張加密方式,不同的加密方式對 CPU 計算的損耗不同,安全級別也不同。TLS 在進行第一次握手的時候,客戶端會向伺服器端 say hello,這個時候會告訴伺服器,它支援哪些演算法,此時伺服器可以將最適合的證照發給客戶端。

2. 證照的吊銷

CA 證照的吊銷存在兩種機制,一種是線上檢查,client 端向 CA 機構傳送請求檢查 server 公鑰的靠譜性;第二種是 client 端儲存一份 CA 提供的證照吊銷列表,定期更新。前者要求查詢伺服器具備良好效能,後者要求每次更新提供下次更新的時間,一般時差在幾天。安全性要求高的網站建議採用第一種方案。

大部分 CA 並不會提供吊銷機制(CRL/OCSP),靠譜的方案是為根證照提供中間證照,一旦中間證照的私鑰洩漏或者證照過期,可以直接吊銷中間證照並給使用者頒發新的證照。中間證照的簽證原理於上上條提到的原理一樣,中間證照還可以產生下一級中間證照,多級證照可以減少根證照的管理負擔。

很多 CA 的 OCSP Server 在國外,線上驗證時間比較長,如果可以聯絡 CA 供應商將 Server 轉移到國內,效率可以提升 10 倍左右。

3. PKI 體系

比較主流的兩種方案是 HPKP 和 Certificate Transparency:

  • HPKP 就是使用者第一次訪問的時候記下 sign 資訊,以後不匹配則拒絕訪問,這存在很大的隱患,比如 Server 更新了證照,或者使用者第一次訪問的時候就被人給黑了
  • Certificate Transparency 意思就是讓 CA 供應商透明化 CA 服務日誌,防止 CA 供應商偷偷簽證

看了不少文章,對 CA 和證照相關的知識做了一些總結,可能不全面,也可能存在表述錯誤或者知識性錯誤,歡迎拍磚!

相關文章