數字證書、公私鑰小記

發佈於2016年2月23日 -

術語

  • X.509

    在密碼學中,X.509是由ITU-T爲了公開密鑰基礎建設(PKI)與授權管理基礎建設(PMI)提出的產業標準。X.509標準,規範了公開密鑰認證、證書吊銷列表、授權證書、證書路徑驗證算法等。[1]

  • PKCS(Public Key Cryptography Standards)

    PKCS是由RSA公司制定的一組關於公鑰加密的標準,和存儲相關的主要包括:

    PKCS1:定義了RSA的數理基礎、公私鑰格式,以及加解密、籤/驗章的流程 PKCS8:定義了私鑰消息的表示 PKCS12:定義了包含私鑰與公鑰證書的文件格式,其中私鑰採密碼保護

公私鑰及證書的生成

以下操作會用到opensslkeytool兩個工具。注意,如果不加聲明,證書和密鑰的存儲都是PEM格式。

生成私鑰

openssl genrsa -out ca.key 2048

openssl生成的私鑰是按PKCS#1編碼的,這種格式包括了密鑰的所有信息(如n、e、d、p、q等)[2],所以genrsa只生成私鑰。

如果需要PKCS#8編碼的私鑰,還需要額外的轉換:

openssl pkcs8 -topk8 -inform PEM in ca.key -outform PEM -nocrypt -out ca.pkcs8.key

如果需要公鑰,需要使用如下的命令:

從私鑰得到公鑰

openssl rsa -in ca.key -pubout -outform PEM -out ca.pub

生成的公鑰是按PKCS#8編碼的。大多數地方都採用這種格式表示公鑰。

生成根證書

openssl req -x509 -new -nodes -key ca.key -days 3000 -out ca.crt

生成的證書是按X.509編碼的,有效期是3000天。

簽發證書

有了根證書,就可以對證書籤名了。爲此,你需要先生成一個私鑰,例如叫做host.key,然後通過私鑰創建一個簽名請求(CSR):

openssl req -new -key host.key -out host.csr

openssl會問你一些問題,務必注意CN這個選項,它應當是放置該證書的主機名(域名或者IP地址),如果開啓了證書校驗,它必須跟客戶端建立連接時填寫的host一致。

有了CSR,就可以用根證書和它的密鑰來簽發證書了:

openssl x509 -req -in host.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 1000 -out host.crt

證書依舊是按X.509編碼的,有效期是1000天。

證書格式的轉換

以上生成的公私鑰和證書都是PEM格式的,但很多時候不同場景中還需要用到其他格式的證書:

p12/pfx

p12/pfx是按照PKCS#12編碼的對象,它通常由X.509證書和對應的私鑰組成。生成p12格式的方法如下:

openssl pkcs12 -export -in ca.crt -inkey ca.key -out ca.p12

由於文件中保存了私鑰,因此執行該命令,openssl會要求用戶輸入密碼,用於保護私鑰。

jks

在Java中,免不了使用JKS格式,JKS是Java標準的密鑰和證書保存格式,嚴格來說,Java的KeyStore存儲的是多個密鑰和證書。要生成它,需要用到keytool工具:

p12文件轉換爲jks

keytool -importkeystore -srckeystore cert.p12 -srcstoretype pkcs12 -destkeystore cert.jks

分別輸入目標jks文件的密碼和源p12文件的密碼,即可生成。

crt文件轉換爲jks

keytool -import -file ca.crt -keystore ca.jks

不同環境/工具對密鑰的需求

Java

在Java中,如果要使用java.security包中和RSA相關的算法,你需要PKCS#8編碼的公私鑰,分別使用PKCS8EncodedKeySpecX509EncodedKeySpec加載Base64解碼後的數據即可。

如果要使用javax.net.ssl下的KeyStore或者TrustStore,最好使用jks格式,可以省去很多麻煩。

C#

在.Net中,System.Security.Cryptography下的X509Certificate2X509Certificate已經過時了)使用p12格式。

一些問題

.Net 證書驗證

默認情況下,.Net會對證書做很嚴格的檢驗,包括但不限於證書鏈、吊銷情況,SSL/TLS中還會檢驗主機名。但對於自簽名證書,吊銷情況是無法通過檢查的,因此需要手動把吊銷檢驗關閉:

var chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.Build(yourCert);

Java 證書驗證

Java會自己維護一個證書鏈(位於JAVA_HOME/lib/security/cacerts),因此把證書加到系統的cacerts裏似乎是無效的。對於 SSLContext,必須要自己把證書加到 TrustStore裏。需要指出的是,如果使用JSSE,Java是不會驗證主機名的,必須自己處理,這裏有討論。

參考資料