術語
-
X.509
在密碼學中,X.509是由ITU-T爲了公開密鑰基礎建設(PKI)與授權管理基礎建設(PMI)提出的產業標準。X.509標準,規範了公開密鑰認證、證書吊銷列表、授權證書、證書路徑驗證算法等。[1]
-
PKCS(Public Key Cryptography Standards)
PKCS是由RSA公司制定的一組關於公鑰加密的標準,和存儲相關的主要包括:
PKCS1:定義了RSA的數理基礎、公私鑰格式,以及加解密、籤/驗章的流程 PKCS8:定義了私鑰消息的表示 PKCS12:定義了包含私鑰與公鑰證書的文件格式,其中私鑰採密碼保護
公私鑰及證書的生成
以下操作會用到openssl
和keytool
兩個工具。注意,如果不加聲明,證書和密鑰的存儲都是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編碼的公私鑰,分別使用PKCS8EncodedKeySpec
和X509EncodedKeySpec
加載Base64解碼後的數據即可。
如果要使用javax.net.ssl
下的KeyStore
或者TrustStore
,最好使用jks
格式,可以省去很多麻煩。
C#
在.Net中,System.Security.Cryptography
下的X509Certificate2
(X509Certificate
已經過時了)使用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是不會驗證主機名的,必須自己處理,這裏有討論。