OpenSSL命令行:自建CA&操作CRL

零、序章
前情提要:OpenSSL命令行實例

注:本文均以RSA密鑰為例,整太多就復雜化了

來自官網(wǎng)的警告提示:
此命令最初用作如何在 CA 中執(zhí)行操作的示例。它的代碼沒有生產(chǎn)質量。它本身不應該被用作一個完整的CA,但是有些人至少在內部使用它來達到這個目的。執(zhí)行此操作時,應特別注意正確保護用于簽名證書的私鑰。建議將它們保存在安全的硬件存儲(如智能卡或HSM)中,并通過合適的引擎或加密提供程序訪問它們。
此命令命令實際上是單個用戶命令:不對各種文件執(zhí)行鎖定,并且嘗試在同一數(shù)據(jù)庫上運行多個?openssl ca?命令可能會產(chǎn)生不可預知的結果。

UP注:
正常情況下,我一般只會使用openssl來進行不需要安全保障的一些測試和測試數(shù)據(jù)生成,不會用于生產(chǎn)實際。需要安全保障的情況下,一般也不會使用簡單的命令行工具,而是使用真正的安全設備(如加密機)來保障安全。

本文編寫環(huán)境:OpenSSL 3.0
本文內容二次驗證環(huán)境:OpenSSL 1.1.1h

一、創(chuàng)建一些文件夾
命名最好用英文

我創(chuàng)建的主文件夾(CA工作目錄)名稱:myCA
CA文件夾:個人喜好,用于單獨存放根CA的證書、csr、私鑰
certs文件夾:已簽發(fā)證書的保存位置,主要是自己歸類。openssl似乎不會對這個文件夾進行操作。
newcerts文件夾:新簽發(fā)證書的保存位置,必定輸出到該位置。即便你指定了輸出路徑,這個文件夾還是會生成一份證書文件(此時輸出了兩本一樣的證書)。
index.txt文件:數(shù)據(jù)庫索引文件,即會記錄CA頒發(fā)的證書的信息
serial文件:頒發(fā)出來的證書的serial number(序列號)。創(chuàng)建時需要往里面輸入初始值“01”(也可以填入其他十六進制值表示)(印象中這個序列號長度是有上限的,所以不要隨便弄一個很長的數(shù)值進來),則頒發(fā)證書時會依據(jù)這個值開始自動遞增。


二、復制一份openssl.cnf文件到主文件夾內
openssl.cnf通常是在OpenSSL安裝目錄的bin/cnf文件夾里面


三、編輯openssl.cnf文件

修改圖中所示的[CA_default]里的內容

dir:主文件夾路徑,我這里用“點”表示“當前文件夾”,因為正常都是進入到主文件夾里去執(zhí)行操作。當然,你這里也可以寫剛剛創(chuàng)建的主文件夾的絕對路徑,如:F:/myCA。
certs、database、new_certs_dir、serial:都是和剛剛創(chuàng)建的文件/文件夾的路徑對應
注意:$dir/certs之類的表示,意思是查找的是dir路徑下的certs文件/文件夾

四、生成CA證書
4.1 生成密鑰對
openssl genrsa -out ca.key 2048
詳見“前情提要”內容
(我是把ca.key放入CA文件夾中進行了歸類,實際看自己喜好)
修改openssl.cnf里關于CA私鑰路徑的配置

注意:這里路徑以你實際的為準即可,不絕對。

4.2 生成CSR
openssl req -new -key ca.key -out ca.csr
詳見“前情提要”內容

4.3 生成自簽名證書CA 途徑一:
使用X509命令生成V1自簽名證書
openssl x509 -req -days 3650 -sha256 -in ca.csr -signkey ca.key -out ca.crt
使用X509命令生成V3自簽名證書(推薦)
openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions v3_ca -in?test.csr -signkey test.key -out test.crt
詳見“前情提要”內容

4.4 生成自簽名證書CA 途徑二:
使用本方式前,請先看“七、簽發(fā)子證書”開頭關于policy_match的相關描述,避免根證書出問題。
4.4.1 修改openssl.cnf配置:

解釋:原配置是usr_cert,使得頒發(fā)出來的證書的擴展字段用的是末端證書的配置。而如果要頒發(fā)CA證書,則應該使用CA證書的配置,所以我這里是將其配置成V3版本的CA證書配置。否則等下頒發(fā)出來的自簽名CA證書,用的是V3的版本,卻缺少必要的擴展字段,在證書鏈校驗時會失敗。
注意:頒發(fā)CA時,臨時使用v3_ca的配置;頒發(fā)末端證書時,要記得改回成usr_cert
有效期配置:原文件默認有效時間長度是365天,可以將其修改成你希望的有效時間長度,如:3650天。
4.4.2 自簽名頒證:
openssl ca -selfsign -in ./CA/ca.csr -config openssl.cnf -out ./CA/ca.crt -keyfile ./CA/ca.key

注意1:此處使用“-config openssl.cnf”指定使用剛剛我們修改過的配置文件
注意2:此處使用“-out ./CA/ca.crt”指定結果文件的額外輸出位置。不指定,就只是會輸出到剛剛openssl.cnf里配置的new_certs_dir對應路徑中,指定后,new_certs_dir對應路徑依舊會有一本以序列號命名的證書文件。
注意3:此處使用“-keyfile ./CA/ca.key”指定本次簽發(fā)證書使用的私鑰,如果不指定,那么就會使用配置文件里配置的私鑰,即:

注意4:每次證書簽發(fā)過程會有兩次提示,第一次是讓你確認以下是否真的要簽發(fā)證書,第二次是讓你確認是否要向數(shù)據(jù)庫(就是那個index.txt文件)中添加本次頒發(fā)證書的信息。

五、第一次簽發(fā)完證書
你會發(fā)現(xiàn),出現(xiàn)了三個新的文件

index.txt.attr:簽發(fā)證書時的屬性
index.txt.old:上一版本的index.txt文件內容
serial.old:? 上一版本的serial文件內容

5.1 index.txt

內容解析:
[證書狀態(tài)] [證書生效時間] [證書到期時間] [證書吊銷時間] [證書序列號] [證書存放路徑] [特征名稱(DN)值]
證書狀態(tài)可選值:
V(Vaild),有效
R(Rovoked),吊銷
E(Expire),過期
證書生效時間:UTC時間(網(wǎng)友說有,但是該字段我在1.1.1h和3.0版本沒見過)
證書到期時間:UTC時間,如圖是:23年9月15日14:02:15過期
證書吊銷時間:UTC時間,在證書吊銷后出現(xiàn)。如果有設置吊銷原因,則后面會跟著顯示吊銷原因
證書序列號:十六進制格式(依據(jù)serial文件得來)
證書存放路徑:生成證書存儲的具體位置,若標記為unknown(ca指令指定了證書輸出文件)不影響證書庫的正常使用
特征名稱(DN)值:證書主題名(Subject)
對于過期的證書,文本數(shù)據(jù)庫并不會自動更新,需要使用ca指令的-updatedb選項進行更新,一般來說,在生成CRL之前,都應該使用-updatedb進行更新
時間都是零時區(qū)時間

5.2 index.txt.attr

該文件內容為第一次頒證時自動生成,生成的依據(jù)來源是配置的openssl.cnf(默認是yes)

當為yes時,嚴格要求所有簽發(fā)的csr的subject不重復,最直觀的就是同一本csr不能重復簽發(fā)

當為no時,允許簽發(fā)的csr的subject重復,最直觀的就是同一本csr可以重復簽發(fā)

如果要調整配置,直接在index.txt.attr中修改yes/no即可。(如果一開始就準備允許subject重復,則可以在第一次頒證前就在openssl.cnf里將該屬性設置為no)
官方文檔描述:
如果給定值?yes,則數(shù)據(jù)庫中的有效證書條目必須具有唯一的使用者。如果給出值?no,則幾個有效的證書條目可能具有完全相同的主題。默認值為?yes,以便與較舊(0.9.8 之前)版本的 OpenSSL 兼容。但是,為了使 CA 證書滾動更新更容易,建議使用值?no,尤其是在與?-selfsign?命令行選項結合使用時。
請注意,在某些情況下,創(chuàng)建沒有任何主題的證書是有效的。如果有多個證書沒有主題,則不算作重復。
注:
當?shù)诙晤C發(fā)證書時,又會多出一個文件。這個文件即頒發(fā)證書前的index.txt.attr的文件內容備份。


5.3 serial

序列號從我們一開始填入的初始值01,變成了02

六、配置CA證書路徑
將剛剛生成的自簽名的CA證書存在路徑配置進openssl.cnf中,這樣,當我們沒有手動指定使用哪本CA時,openssl就會默認使用這個路徑對應的CA證書來簽發(fā)子證書。

注:路徑要依據(jù)實際情況配置

七、簽發(fā)子證書
注:請頒發(fā)子證書前,先查看openssl.cnf的配置是否正確。通常情況下,擴展應該配置成usr_cert。

注:默認情況下,配置要求根證書CA和要頒發(fā)的子證書的CSR的國家、城市、組織相同,不然不允許頒發(fā)子證書。但是實際兩者不一定會相同。所以,可以依據(jù)實際情況進行匹配策略的設置。
其中:
match: 該變量在證書請求中的值必須和CA的subject中的對應項完全相同,否則拒簽。
supplied: 該變量在證書請求中必須提供(值可以不同),否則拒簽。
optional: 該變量在證書請求中可以存在也可以不存在(相當于沒有要求)。?
除非preserve=yes或者在ca命令中使用了-preserveDN,否則在簽發(fā)證書時將刪除匹配策略中未提及的對象。

注意:不知道為什么,默認的policy_match里沒有配置localityName這個字段,這會導致即便csr里有這個字段,也會因為策略未提及而忽略,這很不正常,所以,一定要先補充這個字段



7.1 頒發(fā)子CA
openssl ca -in test.csr -extensions v3_ca -config openssl.cnf
注:請自行生成test.csr,然后在開頭創(chuàng)建的主文件夾內執(zhí)行命令,并指定csr的路徑
注:其中,-extensions v3_ca的作用同剛剛的在openssl.cnf中對擴展字段的配置,即指定擴展字段要用哪個配置。如果是頒發(fā)末端證書,則可以使用-extensions usr_cert。默認情況下,csr里附帶的所有擴展字段都將被忽略,生成的證書的擴展字段全是配置提供。


7.2 頒發(fā)末端證書
openssl?ca?-in?test.csr?-config?openssl.cnf
注:請自行生成test.csr,然后在開頭創(chuàng)建的主文件夾內執(zhí)行命令,并指定csr的路徑

注:默認情況下,頒發(fā)出來的這本末端證書的擴展字段,用的是usr_cert里的配置,原csr中的擴展字段全部被忽略了。


7.3 批量簽發(fā)證書
openssl ca -config openssl.cnf -infiles testa.csr testb.csr testc.csr
使用命令:“-infiles”
執(zhí)行命令過程中,會一一列舉出證書細節(jié)讓你確認是否要簽發(fā)證書。
(內容過長,這里就不截圖舉例了,本質上和普通的證書簽發(fā)沒啥區(qū)別)
注意:-infiles必須放在最后,所有后續(xù)參數(shù)都被視為包含證書請求的文件的名稱。
另外建議:可以創(chuàng)建一個叫csrs的文件夾,專門用來放待簽發(fā)的或者簽發(fā)過的csr文件。

7.4 簽發(fā)證書時手動設置證書有效期范圍
openssl ca -in testd.csr -startdate 220917111120Z? -enddate 20220918114020Z? -config openssl.cnf
使用命令:
-startdate: 設置證書有效起始時間(實測時間比當前早也行)。我這里設置的是2022年9月17日11點11分20秒。
-enddate: 設置證書有效結束時間。我這里這里設置的是2022年9月18日11點40分20秒。
輸入事件的格式有兩種,都支持:
YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure)
YYYYMMDDHHMMSSZ (the same as an ASN1 GeneralizedTime structure)
最后的“Z”必須附帶。
注意:這里設置的時間,是零時區(qū)的時間。如果你要生成馬上過期的證書,則要注意進行時區(qū)的轉換。

八、讓CSR中的擴展字段起效
來自官網(wǎng)的警告提示:
應謹慎使用copy_extensions選項。如果不小心,則可能存在安全風險。例如,如果證書請求包含具有 CA:TRUE 的基本約束擴展,并且copy_extensions值設置為?copyall,并且用戶在顯示證書時未發(fā)現(xiàn)此擴展,則這將向請求者提供有效的 CA 證書。通過將copy_extensions設置為要復制并在配置文件中包含 CA:FALSE 的基本約束,可以避免這種情況。然后,如果請求包含基本約束擴展,則將被忽略。
建議還包括其他擴展(如?keyUsage)的值,以防止請求提供自己的值。
可以對 CA 證書本身施加其他限制。例如,如果 CA 證書具有:
basicConstraints = CA:TRUE, pathlen:0
那么即使證書是用CA:TRUE頒發(fā)的,它也是無效的。

開啟copy_extensions(默認是注釋掉)

字段解釋:是否將證書請求中的擴展項信息加入到證書擴展項中去。
取值范圍以及解釋:?
none: 忽略所有證書請求中的擴展項 (默認)
copy: 將證書擴展項中沒有的項目復制到證書中?
copyall: 將所有證書請求中的擴展項都復制過去,并且覆蓋證書擴展項中原來已經(jīng)存在的值。

在copy_extensions = copy的前提下
usr_cert模塊的配置修改為以下樣子:

再生成一本帶有擴展字段的CSR

使用上面介紹的命令,頒發(fā)末端證書
openssl?ca?-in?test.csr?-config?openssl.cnf

可見,當使用copy_extensions = copy時,ca頒證使用的配置模塊若有相應字段的描述,則會直接覆蓋csr中的對應字段;若沒有相應字段描述,則會使用csr中對應字段內容放入到最終的證書中。

九、創(chuàng)建crlnumber文件
先新建文件夾“crl”,然后創(chuàng)建“crlnumber”文件,往crlnumber里預置初始值“01”(或者是其他你喜歡的起始數(shù)字,這個數(shù)字將會作為被吊銷的證書記錄的編號,更新吊銷證書列表時會自增)。

修改openssl.cnf配置文件,使得路徑對應上


十、生成/更新吊銷證書列表(CRL)
10.1 更新文本數(shù)據(jù)庫?
還記得上文中生成第一本證書后的介紹說明文字嗎?有一處有提及,因為index.txt不會自動將過期證書設置為過期,所以在生成/更新crl前,需要先updatedb,更新數(shù)據(jù)庫索引以清除過期的證書。
openssl ca -updatedb -config openssl.cnf

當真的有證書過期時,會多出現(xiàn)一行,提示哪個編號的證書過期了。

與此同時,index.txt中對應行也發(fā)生了變化,原本valid狀態(tài)的證書變成了expire狀態(tài)

10.2 生成/更新CRL
openssl ca -gencrl -out ./crl/crl.pem -config openssl.cnf

注:我發(fā)現(xiàn),即便我還沒有開始吊銷任何一本證書,但是我執(zhí)行了這個命令,crlnumber就會產(chǎn)生crlnumber.old文件,然后crlnumber里的編號就會開始自增(但是是虛的,等吊銷證書真有變化后再生成,就恢復正常了)。
注:這里不能不寫“-out crl.pem”,不寫的話實測沒輸出內容

十一、吊銷證書
注:吊銷完證書,需要更新CRL,crl.pem里的數(shù)據(jù)才會是新的數(shù)據(jù)。
11.1 普通吊銷(不標注原因):
openssl ca -revoke ./newcerts/03.pem -config openssl.cnf

執(zhí)行成功后,會提示你剛剛吊銷的證書的序列號。

注:有沒有注意到,相當于上文中頒發(fā)完證書生成的字段,開頭的“V”變成了“R”,然后多了一個時間字段,這個時間,即為“證書吊銷的時間”,但是看樣子是零時區(qū)的時間。

11.2 標注原因的吊銷:
openssl ca -revoke ./newcerts/04.pem -crl_reason cessationOfOperation -config openssl.cnf
openssl ca -revoke ./newcerts/05.pem -crl_reason superseded -config openssl.cnf
可以看到,相對于不標注原因的寫法,index.txt里在吊銷證書的時間后面會附帶上我們指定的吊銷原因。

-crl_reason可選項:
unspecified
keyCompromise
CACompromise
affiliationChanged
superseded
cessationOfOperation
certificateHold
removeFromCRL
說明:
原因的匹配不區(qū)分大小寫。設置任何吊銷原因都會使 CRL v2。
在實踐中,“removeFromCRL”并不是特別有用,因為它僅用于當前未實現(xiàn)的增量 CRL。

十二、 查看crl文件內容
openssl crl -in ./crl/crl.pem -noout -text

可見,內容中有CA信息,有被吊銷的證書信息(證書序列號,吊銷時間,吊銷原因)


附加
1、OpenSSL官網(wǎng)對應地址
https://www.openssl.org/docs/man3.0/man1/openssl-ca.html
2、他人文章指路
https://www.cnblogs.com/f-ck-need-u/p/7115871.html
3、一些補充
上文中很多地方都有出現(xiàn)的配置[CA_default]的內容,實際上都可以像設置有效期一樣,在操作的時候使用命令進行手動指定。這些內容可以參考上面提供的兩個鏈接,篇幅有限,這邊不過多舉例說明。