OpenSSL 建立私有 CA 三部曲:
使用 OpenSSL 建立私有 CA:1 根證照
使用 OpenSSL 建立私有 CA:2 中間證照
使用 OpenSSL 建立私有 CA:3 使用者證照
本文將在前文《使用 OpenSSL 建立私有 CA:1 根證照》的基礎上介紹如何為私有 CA 建立中間證照。
說明:本系列文章的演示環境為 Ubuntu 18.04,OpenSSL 的版本為 1.1.0g。
為什麼要建立中間證照?
使用 CA 的根證照是可以直接簽發使用者證照的,那麼為什麼還還要建立中間證照呢?
大概有兩個原因:
- 安全性
- 建立中間證照頒發機構(CA)
先說安全性,通過簽發中間證照,再用中間證照籤發使用者證照的方式,可以最大程度的減少更證照的使用頻率,甚至可以離線儲存根證照。如果發現中間證照被破壞,還可以吊銷該中間證照並頒發新的中間證照。
再來說說中間證照頒發機構(CA),一箇中間證照頒發機構(CA)是一個實體,它具有根 CA 頒發的中間證照,並且可以代表根 CA 頒發使用者證照,從而形成一條被信任的證照鏈。
這裡有必要介紹下證照產業的基本玩法:首先根證照的所有者(大的證照廠商)會和 OS 廠商合作,讓 OS 等環境預設信任自己的根證照。然後根證照的所有者會簽發中間證照賣給小一些的證照廠商。這些小的證照廠商再用中間證照建立最終證照賣給普通消費者。
舉個例子,像我們常用的 Windows 作業系統預設就信任了很多廠商的根證照,比如 VeriSign 的證照,並且這些證照還會隨著 Windows 系統的更新而更新:
上圖中,VeriSign 是根證照所有者,你安裝 windows 作業系統時,VeriSign 的根證照就被安裝到信任列表中了。Symantec Class 3 SHA256 Code Signing CA 則是 Symantec 公司從 VeriSign 公司購買的中間證照。GrapeCity inc. 則是 GrapeCity 公司從 Symantec 公司購買的最終證照用來對產品進行簽名。當然,有時候根證照廠商比如 VeriSign 等也會直接向終端使用者銷售證照。
為了模擬中間證照頒發機構(CA),本文將使用前文中建立的根證照建立一個名稱為 power 的中間證照。
準備中間 CA 的配置檔案
建立檔案配置檔案 powerca/powerca.cnf,編輯其內容如下:
# OpenSSL root CA configuration file. # v1 [ ca ] # `man ca` default_ca = CA_default [ CA_default ] # Directory and file locations. dir = /home/nick/projects/myca/powerca certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts database = $dir/db/index serial = $dir/db/serial RANDFILE = $dir/private/random # The root key and root certificate. private_key = $dir/private/powerca.key.pem certificate = $dir/certs/powerca.cert.pem # For certificate revocation lists. crlnumber = $dir/db/crlnumber crl = $dir/crl/powerca.crl.pem crl_extensions = crl_ext default_crl_days = 30 # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha256 name_opt = ca_default cert_opt = ca_default default_days = 3750 copy_extensions = copy preserve = no policy = policy_loose [ policy_loose ] # Allow the intermediate CA to sign a more diverse range of certificates. # See the POLICY FORMAT section of the `ca` man page. countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] # Options for the `req` tool (`man req`). # Optionally, specify some defaults. prompt = no input_password = 123456 default_bits = 2048 distinguished_name = req_distinguished_name string_mask = utf8only # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha256 # Extension to add when the -x509 option is used. # make sure use x509_extensions, do not use req_extensions. x509_extensions = v3_ca # use the req_extensions not work. #req_extensions = v3_ca [ req_distinguished_name ] # See <https://en.wikipedia.org/wiki/Certificate_signing_request>. countryName = CN stateOrProvinceName = ShaanXi localityName = Xian organizationName = NickLi Ltd organizationalUnitName = NickLi Ltd CA commonName = NickLi Power CA emailAddress = ljfpower@163.com [ v3_ca ] # Extensions for a typical CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ v3_intermediate_ca ] # Extensions for a typical intermediate CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ usr_cert ] # Extensions for client certificates (`man x509v3_config`). basicConstraints = CA:FALSE nsCertType = client, email nsComment = "OpenSSL Generated Client Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth, emailProtection [ server_cert ] # Extensions for server certificates (`man x509v3_config`). basicConstraints = CA:FALSE nsCertType = server nsComment = "OpenSSL Generated Server Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth [ crl_ext ] # Extension for CRLs (`man x509v3_config`). authorityKeyIdentifier=keyid:always [ ocsp ] # Extension for OCSP signing certificates (`man ocsp`). basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, digitalSignature extendedKeyUsage = critical, OCSPSigning
這個配置檔案的內容和 rootca/rootca.cnf 有很多相同之處,下面是一些比較重要的不同之處:
[ CA_default ] dir = /home/nick/projects/myca/powerca private_key = $dir/private/powerca.key.pem certificate = $dir/certs/powerca.cert.pem crl = $dir/crl/powerca.crl.pem policy = policy_loose [ req_distinguished_name ] commonName = NickLi Power CA
CA_default 中的變化主要是告訴 OpenSSL 相關檔案的路徑,而中間證照的 commonName 必須不同於根證照。
準備目錄和檔案
與 rootca 目錄相同,我們需要在 powerca 目錄下建立如下的目錄和檔案:
powerca/certs/ powerca/db/ powerca/private/ powerca/crl/ powerca/csr/ powerca/newcerts/ powerca/db/index powerca/db/serial powerca/db/crlnumber
其中的 powerca/private 目錄需同樣要 700 的許可權,我們使用下面的指令碼來建立這些目錄和檔案:
#!/bin/bash # create dir certs db private crl csr newcerts under powerca dir. if [ ! -d powerca/certs ]; then mkdir -p powerca/certs fi if [ ! -d powerca/db ]; then mkdir -p powerca/db touch powerca/db/index openssl rand -hex 16 > powerca/db/serial echo 1001 > powerca/db/crlnumber fi if [ ! -d powerca/private ]; then mkdir -p powerca/private chmod 700 powerca/private fi if [ ! -d powerca/crl ]; then mkdir -p powerca/crl fi if [ ! -d powerca/newcerts ]; then mkdir -p powerca/newcerts fi if [ ! -d powerca/csr ]; then mkdir -p powerca/csr fi
把上面的程式碼儲存到 myca/powerhelper.sh 檔案中,然後 cd 到 myca 目錄下執行:
$ ./powerhelper.sh
此時當前目錄為 myca,powerca 下的子目錄和檔案都已經建立成功。
建立中間證照的祕鑰
進入 powerca 目錄:
$ cd powerca
執行下面的命令重建私鑰:
$ openssl genrsa -aes256 -out powerca/private/powerca.key.pem 4096
這裡筆者設定的密碼為:123456,記住這個密碼,後面還會用到。然後為了確保安全,把祕鑰的許可權設定為 400:
$ chmod 400 private/powerca.key.pem
此時當前目錄為 myca/powerca。
建立 Certificate Signing Requests(csr)
要建立中間證照,需要使用帶有 v3_intermediate_ca 副檔名的根 CA 來簽署中間 CSR,其中 v3_intermediate_ca 擴充套件的配置資訊就在 powerca/powerca.cnf 中:
[ v3_intermediate_ca ] # Extensions for a typical intermediate CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign
現在直接建立 csr 就行了:
$ openssl req -new \ -config powerca.cnf \ -sha256 \ -key private/powerca.key.pem \ -out csr/powerca.csr.pem
下面的命令可以檢查生成的 csr:
$ openssl req -text -noout -in csr/powerca.csr.pem
注意,csr 中包含了 CA 的基本資訊,和公鑰資訊。
建立中間證照
下面是整個過程中最為關鍵的地方!通過 Root CA 的資訊和使用者的 csr 為使用者生成證照。
建立中間證照需要用到 rootca/rootca.cnf 中的配置資訊,所以先進入 myca 目錄:
# 從 powerca 目錄回到 myca 目錄 $ cd .. $ openssl ca -config rootca/rootca.cnf \ -extensions v3_intermediate_ca \ -days 3650 -notext -md sha256 \ -in powerca/csr/powerca.csr.pem \ -out powerca/certs/powerca.cert.pem
在互動式的提示中輸入私鑰的密碼 123456,並同意其它的確認提示,就完成了根證照的生成操作:
rootca/db/index 檔案是 OpenSSL CA 工具儲存證照的資料庫,請不要手動修改這個檔案(除非你清楚的知道自己在幹什麼)。此時它應該包含了根證照和中間證照的資訊:
證照生成後我們把它的許可權修改為 444:
$ chmod 444 powerca/certs/powerca.cert.pem
驗證中間證照
通過下面的命令驗證中間證照:
$ openssl x509 -noout -text -in powerca/certs/powerca.cert.pem
在中間證照中,Subject 的 Common Name 為 "NickLi Power CA",而 Issuer 的 Common Name 為 "NickLi Root CA"。
還可以通過下面的命令來檢查中間證照的狀態:
$ openssl verify -CAfile rootca/certs/rootca.cert.pem powerca/certs/powerca.cert.pem
建立證照鏈檔案
當 web 瀏覽器等應用程式試圖驗證中間 CA 頒發的證照時,它還必須根據根證照驗證中間證照。這就需要構建完整的證照信任鏈供應用程式驗證。所謂的證照鏈,簡單的說就是把根證照和中間證照按照順序放置在同一個證照檔案中。重點是:中間證照在上面,根證照在下面。比如為我們的中間證照建立證照鏈:
$ cat powerca/certs/powerca.cert.pem \ rootca/certs/rootca.cert.pem > powerca/certs/powerca-chain.cert.pem $ chmod 444 powerca/certs/powerca-chain.cert.pem
注意:在區域網環境中,我們一般會把生成的這個證照鏈安裝到使用者的機器上。
pem 格式的證照、證照鏈檔案適用的場景比較多,但是在 windows 系統中一般使用 p12 格式,所以我們還需要建立一個 p12 格式的證照鏈:
$ openssl pkcs12 -export \ -name "powerca chain" \ -inkey powerca/private/powerca.key.pem \ -in powerca/certs/powerca.cert.pem \ -certfile powerca/certs/powerca-chain.cert.pem \ -out powerca/certs/powerca-chain.cert.p12
這個過程中需要輸入私鑰 powerca/private/powerca.key.pem 的密碼(這裡是 123456),並且為新證照設定的密碼。
總結
現在我們已經有了私有 CA 的根證照並且生成了中間證照和證照鏈,在接下來的《使用 OpenSSL 建立私有 CA:3 使用者證照》一文中我們將詳細的介紹如何使用中間證照建立使用者證照,以及如何把證照部署到 web 站點和客戶端。
參考:
OpenSSL Certificate Authority
《openssl-cookbook》