引言
數字證書是一個經證書授權中心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件。為現實網絡安全化標準,如今大部分的 B2B、B2C、P2P、O2O 等商業網站,含有重要企業資料個人資料的信息資信網站,政府機構金融機構等服務網站大部分都使用了數字證書來加強網絡的安全性。
數字證書一般由經過國家認證的權威機構頒發,即CA(例如中國各地方的CA公司)中心簽發的證書,也可以由企業級CA系統進行簽發,例如:Symantec、ResellerClub、數安時代等。開發人員也可以通過工具自動生成證書進行開發,但不經過認證的證書將被視為無效證書,不保安全保護,但仍可正常運行。
在這篇文章里將為大家介紹數字證書的生成使用過程,以及對數據進行加密、解密、簽名、驗簽的使用方式。
希望能對各位的學習研究有所幫助,當中有所錯漏的地方敬請點評。
目錄
一、數字證書介紹
三、生成數字證書的方式
六、數字證書簽名與驗簽
一、數字證書介紹
1.1 什么是數字證書
數字證書就是互聯網通訊中標志通訊各方身份信息的一串數字,提供了一種在Internet上驗證通信實體身份的方式,數字證書不是數字身份證,而是身份認證機構蓋在數字身份證上的一個章或印(或者說加在數字身份證上的一個簽名)。它是由權威機構——CA機構,又稱為證書授權(Certificate Authority)中心發行的,人們可以在網上用它來識別對方的身份。
最簡單的證書包含密鑰、名稱以及證書授權中心的數字簽名。數字證書還有一個重要的特征就是只在特定的時間段內有效。數字證書是一種權威性的電子文檔,可以由權威公正的第三方機構,即CA(例如中國各地方的CA公司)中心簽發的證書,也可以由企業級CA系統進行簽發。
圖 V.1.1
1.2 數字證書的分類
從數字簽名使用對象的角度分,目前的數字證書類型主要包括:個人身份證書、企業或機構身份證書、支付網關證書、服務器證書、安全電子郵件證書、個人代碼簽名證書,這些數字證書特點各有不同。
符合 X.509 標準的數字安全證書,證書中包含個人身份信息和個人的公鑰,用于標識證書持有人的個人身份。數字安全證書和對應的私鑰存儲于 E-key 中,用于個人在網上進行合同簽定、定單、錄入審核、操作權限、支付信息等活動中標明身份。
符合 X.509 標準的數字安全證書,證書中包含企業信息和企業的公鑰,用于標識證書持有企業的身份。數字安全證書和對應的私鑰存儲于 E-key 或 IC 卡中,可以用于企業在電子商務方面的對外活動,如合同簽定、網上證券交易、交易支付信息等方面。
支付網關證書是證書簽發中心針對支付網關簽發的數字證書,是支付網關實現數據加解密的主要工具,用于數字簽名和信息加密。支付網關證書僅用于支付網關提供的服務(Internet 上各種安全協議與銀行現有網絡數據格式的轉換)。支付網關證書只能在有效狀態下使用。支付網關證書不可被申請者轉讓。
符合 X.509 標準的數字安全證書,證書中包含服務器信息和服務器的公鑰,在網絡通訊中用于標識和驗證服務器的身份。數字安全證書和對應的私鑰存儲于 E-key 中。服務器軟件利用證書機制保證與其他服務器或客戶端通信時雙方身份的真實性、安全性、可信任度等。
代碼簽名證書是 CA 中心簽發給軟件提供商的數字證書,包含軟件提供商的身份信息、公鑰及 CA 的簽名。軟件提供商使用代碼簽名證書對軟件進行簽名后放到 Internet 上,當用戶在 Internet 上下載該軟件時,將會得到提示,從而可以確信:軟件的來源;軟件自簽名后到下載前,沒有遭到修改或破壞。代碼簽名證書可以對 32-bit .exe 、 .cab 、 .ocx 、 .class 等程序和文件 進行簽名。
符合 X.509 標準的數字安全證書,通過 IE 或 Netscape 申請,用 IE 申請的證書存儲于 WINDOWS 的注冊表中,用 NETSCAPE 申請的存儲于個人用戶目錄下的文件中。用于安全電子郵件或向需要客戶驗證的 WEB 服務器(https 服務) 表明身份。
個人代碼簽名證書是 CA 中心簽發給軟件提供人的數字證書,包含軟件提供個人的身份信息、公鑰及 CA 的簽名。軟件提供人使用代碼簽名證書對軟件進行簽名后放到 Internet 上,當用戶在 Internet 上下載該軟件時,將會得到提示,從而可以確信:軟件的來源;軟件自簽名后到下載前,沒有遭到修改或破壞。代碼簽名證書可以對 32-bit .exe 、 .cab 、 .ocx 、 .class 等程序和文件進行簽名。
從數字證書的技術角度分,CA中心發放的證書分為兩類:SSL證書和SET證書。一般地說,SSL 證書(安全套接層)是服務于銀行對企業或企業對企業的電子商務活動的;而SET(安全電子交易)證書則服務于持卡消費、網上購物。雖然它們都是用于識別身份和數字簽名的證書,但它們的信任體系完全不同,而且所符合的標準也不一樣。
1.3 數字證書的格式
證書主要的文件類型和協議有: PEM、DER、PFX、JKS、KDB、CER、KEY、CSR、CRT、CRL 、OCSP、SCEP等。
1.3.1 PEM 格式
Openssl使用 PEM(Privacy Enhanced Mail)格式來存放各種信息,它是 openssl 默認采用的信息存放方式。Openssl 中的 PEM 文件一般包含如下信息:
內容類型:表明本文件存放的是什么信息內容,它的形式為“——-BEGIN XXXX ——”,與結尾的“——END XXXX——”對應。
頭信息:表明數據是如果被處理后存放,openssl 中用的最多的是加密信息,比如加密算法以及初始化向量 iv。
信息體:為 BASE64 編碼的數據。可以包括所有私鑰(RSA 和 DSA)、公鑰(RSA 和 DSA)和 (x509) 證書。它存儲用 Base64 編碼的 DER 格式數據,用 ascii 報頭包圍,因此適合系統之間的文本模式傳輸。
使用PEM格式存儲的證書:
1 -----BEGIN CERTIFICATE-----
2 MIIF6TCCBNGgAwIBAgIQSSOR8EYFvAGtG16qv0lZ4DANBgkqhkiG9w0BAQsFADBC
3 MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
4 UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE3MDQyNDAwMDAwMFoXDTE5MDQyNDIzNTk1
5 OVowITEfMB0GA1UEAwwWc2VjdXJpdHkucHVqaW53YW5nLmNvbTCCASIwDQYJKoZI
6 hvcNAQEBBQADggEPADCCAQoCggEBANrPWriCfyigreL9cVAyEPesYScRd176xhH0
7 



.
8 -----END CERTIFICATE-----
使用PEM格式存儲的私鑰:
1 -----BEGIN RSA PRIVATE KEY-----
2 MIIF6TCCBNGgAwIBAgIQSSOR8EYFvAGtG16qv0lZ4DANBgkqhkiG9w0BAQsFADBC
3 MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
4 UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE3MDQyNDAwMDAwMFoXDTE5MDQyNDIzNTk1
5 OVowITEfMB0GA1UEAwwWc2VjdXJpdHkucHVqaW53YW5nLmNvbTCCASIwDQYJKoZI
6 hvcNAQEBBQADggEPADCCAQoCggEBANrPWriCfyigreL9cVAyEPesYScRd176xhH0
7 


8 -----END RSA PRIVATE KEY-----
使用PEM格式存儲的證書請求文件:
1 -----BEGIN CERTIFICATE REQUEST-----
2 MIIF6TCCBNGgAwIBAgIQSSOR8EYFvAGtG16qv0lZ4DANBgkqhkiG9w0BAQsFADBC
3 MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
4 UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE3MDQyNDAwMDAwMFoXDTE5MDQyNDIzNTk1
5 OVowITEfMB0GA1UEAwwWc2VjdXJpdHkucHVqaW53YW5nLmNvbTCCASIwDQYJKoZI
6 hvcNAQEBBQADggEPADCCAQoCggEBANrPWriCfyigreL9cVAyEPesYScRd176xhH0
7 



.
8 -----END CERTIFICATE REQUEST-----
1.3.2 DER 格式
辨別編碼規則 (DER) 可包含所有私鑰、公鑰和證書。它是大多數瀏覽器的缺省格式,并按 ASN1 DER 格式存儲。它是無報頭的 - PEM 是用文本報頭包圍的 DER。
PFX 或 P12 – 公鑰加密標準 #12 (PKCS#12) 可包含所有私鑰、公鑰和證書。其以二進制格式存儲,也稱為 PFX 文件。通常可以將Apache/OpenSSL使用的“KEY文件 + CRT文件”格式合并轉換為標準的PFX文件,你可以將PFX文件格式導入到微軟IIS 5/6、微軟ISA、微軟Exchange Server等軟件。轉換時需要輸入PFX文件的加密密碼。
1.3.3 JKS格式
通常可以將Apache/OpenSSL使用的“KEY文件 + CRT文件”格式”轉換為標準的Java Key Store(JKS)文件。JKS文件格式被廣泛的應用在基于Java的WEB服務器、應用服務器、中間件。你可以將JKS文件導入到TOMCAT、 WEBLOGIC 等軟件。
1.3.4 KDB格式
通常可以將Apache/OpenSSL使用的“KEY文件 + CRT文件”格式轉換為標準的IBM KDB文件。KDB文件格式被廣泛的應用在IBM的WEB服務器、應用服務器、中間件。你可以將KDB文件導入到IBM HTTP Server、IBM Websphere 等軟件。
1.3.5 CSR 格式(證書請求文件 Certificate Signing Request)
生成 X509 數字證書前,一般先由用戶提交證書申請文件,然后由 CA 來簽發證書。大致過程如下(X509 證書申請的格式標準為 pkcs#10 和 rfc2314):
用戶生成自己的公私鑰對;
構造自己的證書申請文件,符合 PKCS#10 標準。該文件主要包括了用戶信息、公鑰以及一些可選的屬性信息,并用自己的私鑰給該內容簽名;
用戶將證書申請文件提交給 CA;
CA 驗證簽名,提取用戶信息,并加上其他信息(比如頒發者等信息),用 CA 的私鑰簽發數字證書;
說明:數字證書(如x.509)是將用戶(或其他實體)身份與公鑰綁定的信息載體。一個合法的數字證書不僅要符合 X509 格式規范,還必須有 CA的簽名。用戶不僅有自己的數字證書,還必須有對應的私鑰。X509v3數字證書主要包含的內容有:證書版本、證書序列號、簽名算法、頒發者信息、有效時間、持有者信息、公鑰信息、頒發者 ID、持有者 ID 和擴展項。
1.3.6 OCSP格式(在線證書狀態協議 Online Certificate StatusProtocol,rfc2560)
用于實時表明證書狀態。OCSP 客戶端通過查詢 OCSP服務來確定一個證書的狀態,可以提供給使用者一個或多個數字證書的有效性資料,它建立一個可實時響應的機制,讓用戶可以實時確認每一張證書的有效性,解決由CRL引發的安全問題。。OCSP 可以通過 HTTP協議來實現。rfc2560 定義了 OCSP 客戶端和服務端的消息格式。
1.3.7 CRL格式(證書吊銷列表 Certification Revocation List)
是一種包含撤銷的證書列表的簽名數據結構。CRL是證書撤銷狀態的公布形式,CRL 就像信用卡的黑名單,用于公布某些數字證書不再有效。CRL是一種離線的證書狀態信息。它以一定的周期進行更新。CRL 可以分為完全 CRL和增量 CRL。在完全 CRL中包含了所有的被撤銷證書信息,增量 CRL 由一系列的 CRL 來表明被撤銷的證書信息,它每次發布的 CRL 是對前面發布 CRL的增量擴充。基本的 CRL 信息有:被撤銷證書序列號、撤銷時間、撤銷原因、簽名者以及 CRL 簽名等信息。基于 CRL的驗證是一種不嚴格的證書認證。CRL 能證明在 CRL 中被撤銷的證書是無效的。但是,它不能給出不在 CRL中的證書的狀態。如果執行嚴格的認證,需要采用在線方式進行認證,即 OCSP認證。一般是由CA簽名的一組電子文檔,包括了被廢除證書的唯一標識(證書序列號),CRL用來列出已經過期或廢除的數字證書。它每隔一段時間就會更新,因此必須定期下載該清單,才會取得最新信息。
1.3.8 SCEP (簡單證書注冊協議)
基于文件的證書登記方式需要從您的本地計算機將文本文件復制和粘貼到證書發布中心,和從證書發布中心復制和粘貼到您的本地計算機。 SCEP可以自動處理這個過程但是CRLs仍然需要手工的在本地計算機和CA發布中心之間進行復制和粘貼。
1.3.9 PKCS7 (加密消息語法(pkcs7)
是各種消息存放的格式標準。這些消息包括:數據、簽名數據、數字信封、簽名數字信封、摘要數據和加密數據。
1.3.10 PKCS12 (個人數字證書標準,Public Key Cryptography Standards #12)
包含了公鑰和私鑰的二進制格式的證書形式,一般以 pfx 作為證書文件后綴名。用于存放用戶證書、crl、用戶私鑰以及證書鏈,pkcs12 中的私鑰是加密存放的。
1.3.11 CER 一般指使用DER格式的證書
CER 證書一般是以 DER 二進制編碼的證書,證書中沒有私鑰,以 *.cer 作為證書文件后綴名。證書可以以 BASE64 編碼輸出,以Base64 編碼的證書證書中沒有私鑰,BASE64 編碼格式的證書文件,也是以 *.cer 作為證書文件后綴名。
1.3.12 CRT 證書文件,可以是PEM格式
1.3.13 KEY 一般指PEM格式的私鑰文件
二、加密算法介紹
在生成數據證書是用戶可選擇不同的加密方式對數據進行加密,常見的加密算法可以分成三類,對稱加密算法,非對稱加密算法和Hash算法。
2.1 對稱加密
在對稱加密算法中,加密使用的密鑰和解密使用的密鑰是相同的。也就是說,加密和解密都是使用的同一個密鑰。因此對稱加密算法要保證安全性的話,密鑰要做好保密,只能讓使用的人知道,不能對外公開。在對稱加密算法中,加密和解密都是使用同一個密鑰,不區分公鑰和私鑰。
對稱加密算法的優點在于加解密的高速度和使用長密鑰時的難破解性。假設兩個用戶需要使用對稱加密方法加密然后交換數據,則用戶最少需要2個密鑰并交換使用,如果企業內用戶有n個,則整個企業共需要n×(n-1) 個密鑰,密鑰的生成和分發將成為企業信息部門的惡夢。對稱加密算法的安全性取決于加密密鑰的保存情況,但要求企業中每一個持有密鑰的人都保守秘密是不可能的,他們通常會有意無意的把密鑰泄漏出去——如果一個用戶使用的密鑰被入侵者所獲得,入侵者便可以讀取該用戶密鑰加密的所有文檔,如果整個企業共用一個加密密鑰,那整個企業文檔的保密性便無從談起。
常見的對稱加密算法:DES、3DES、DESX、Blowfish、IDEA、RC4、RC5、RC6和AES。
DES是一種分組數據加密技術(先將數據分成固定長度的小數據塊,之后進行加密),速度較快,適用于大量數據加密,而3DES是一種基于DES的加密算法,使用3個不同密匙對同一個分組數據塊進行3次加密,如此以使得密文強度更高。
相較于DES和3DES算法而言,AES算法有著更高的速度和資源使用效率,安全級別也較之更高了,被稱為下一代加密標準。
2.2 非對稱加密
在非對稱加密算法中,加密使用的密鑰和解密使用的密鑰是不相同的,也稱為公私鑰加密,也就是說加密使用的密鑰和解密使用的密鑰不同。
假設兩個用戶要加密交換數據,雙方交換公鑰,使用時一方用對方的公鑰加密,另一方即可用自己的私鑰解密。如果企業中有n個用戶,企業需要生成n對密鑰,并分發n個公鑰。由于公鑰是可以公開的,用戶只要保管好自己的私鑰即可,因此加密密鑰的分發將變得十分簡單。同時,由于每個用戶的私鑰是唯一的,其他用戶除了可以可以通過信息發送者的公鑰來驗證信息的來源是否真實,還可以確保發送者無法否認曾發送過該信息。非對稱加密的缺點是加解密速度要遠遠慢于對稱加密,在某些極端情況下,甚至能比非對稱加密慢上1000倍。
常見的非對稱加密算法:RSA、ECC(移動設備用)、Diffie-Hellman、El Gamal、DSA(數字簽名用)。
RSA和DSA的安全性及其它各方面性能都差不多,而ECC較之則有著很多的性能優越,包括處理速度,帶寬要求,存儲空間等等。
2.3 Hash算法
Hash算法特別的地方在于它是一種單向算法,用戶可以通過Hash算法對目標信息生成一段特定長度的唯一的Hash值,卻不能通過這個Hash值重新獲得目標信息。因此Hash算法常用在不可還原的密碼存儲、信息完整性校驗等。
常見的Hash算法:MD2、MD4、MD5、HAVAL、SHA、SHA-1、HMAC、HMAC-MD5、HMAC-SHA1。
這幾種算法只生成一串不可逆的密文,經常用其效驗數據傳輸過程中是否經過修改,因為相同的生成算法對于同一明文只會生成唯一的密文,若相同算法生成的密文不同,則證明傳輸數據進行過了修改。通常在數據傳說過程前,使用MD5和SHA1算法均需要發送和接收數據雙方在數據傳送之前就知道密匙生成算法,而HMAC與之不同的是需要生成一個密匙,發送方用此密匙對數據進行摘要處理(生成密文),接收方再利用此密匙對接收到的數據進行摘要處理,再判斷生成的密文是否相同。
加密算法的效能通常可以按照算法本身的復雜程度、密鑰長度(密鑰越長越安全)、加解密速度等來衡量。上述的算法中,除了DES密鑰長度不夠、MD2速度較慢已逐漸被淘汰外,其他算法仍在目前的加密系統產品中使用。
三、生成數字證書的方式
數字證書可以通過在線工具,腳本代碼,KEYTOOL工具,OPEN SSL工具等多種方式生成。下面以常用的RSA非對稱加密為例子,向大家介紹幾種常用的數字證書生成方式。
3.1 利用KEYTOOL工具生成數字證書
KEYTOOL 是個密鑰和證書管理工具,可以在 JAVA 環境下管理安全鑰匙與證書的生成與安裝。它還是一個有效的安全鑰匙和證書的管理工具,使用戶能夠管理自己的公鑰/私鑰對及相關證書。它管理一個存儲了私有鑰匙和驗證相應公共鑰匙的與它們相關聯的X.509 證書鏈的keystore(相當一個數據庫,里面可存放多個X.509標準的證書)。能夠使用戶使用數字簽名來管理他們自己的私有/公共鑰匙對,管理用來作自我鑒定的相關的證書,管理數據完整性和鑒定服務,還能使用戶在通信時緩存它們的公共鑰匙。
3.1.1 KEYTOOL命令介紹
KEYTOOL通是以keytool開關,當中常用命令有:
1 keytool -genkey -alias casserver -keypass cas123 -keyalg RSA -keystore casserver.keystore -validity 365
2 keytool -export -alias casserver -storepass cas123 -file casserver.cer -keystore casserver.keystore
3 keytool -import -trustcacerts -alias casserver -storepass cas123 -file casserver.cer –keystore cacerts
- -genkey 在用戶主目錄中創建一個默認文件".keystore",還會產生一個mykey的別名,mykey中包含用戶的公鑰、私鑰和證書
在沒有指定生成位置的情況下,keystore會存在用戶系統默認目錄,如:對于window xp系統,會生成在系統的C:/Documents and Settings/UserName/文件名為“.keystore”
- -alias 產生別名 -keystore 指定密鑰庫的名稱(產生的各類信息將不在.keystore文件中) -keyalg 指定密鑰的算法 (如 RSA DSA(如果不指定默認采用DSA))
- -validity 指定創建的證書有效期多少天-keysize 指定密鑰長度
- -storepass 指定密鑰庫的密碼(獲取keystore信息所需的密碼)
- -keypass 指定別名條目的密碼(私鑰的密碼)
- -dname 指定證書擁有者信息
例如: "CN=名字與姓氏,OU=組織單位名稱,O=組織名稱,L=城市或區域名稱,ST=州或省份名稱,C=單位的兩字母國家代碼"
- -list 顯示密鑰庫中的證書信息
例如: keytool -list -v -keystore 是指定 keystore -storepass 密碼 -v 顯示密鑰庫中的證書詳細信息
- -export 將別名指定的證書導出到文件
例如:keytool -export -alias 需要導出的別名 -keystore 指定keystore -file 指定導出的證書位置及證書名稱 -storepass 密碼
- -file 參數指定導出到文件的文件名
- -delete 刪除密鑰庫中某條目
例如:keytool -delete -alias 指定需刪除的別 -keystore 指定keystore -storepass 密碼
- -printcert 查看導出的證書信息
例如:keytool -printcert -file leslie.crt
- -keypasswd 修改密鑰庫中指定條目口令
例如:keytool -keypasswd -alias 需修改的別名 -keypass 舊密碼 -new 新密碼 -storepass keystore密碼 -keystore sage
- -storepasswd 修改keystore口令
例如:keytool -storepasswd -keystore c:/leslie.keystore(需修改口令的keystore) -storepass 123456(原始密碼) -new 888888(新密碼)
- -import 將已簽名數字證書導入密鑰庫
例如: keytool -import -alias 指定導入條目的別名 -keystore 指定keystore -file 需導入的證書
3.1.2 生成 *.keystore 文件流程
首先執行以下命令,分別輸入密鑰庫口令、姓名,單位,組織,城市,省份,國家等信息,經確認后生成對應的 leslie.keystore 文件。注意 *.keystore 文件相當于一個資源庫,后面的公鑰、私鑰、證書等都依賴于它生成,必須謹慎保管。
1 keytool -genkey -alias everygold -keypass 123456 -keyalg RSA -keystore leslie.keystore -validity 365
提示: -alias指定別名為 everygold; -keyalg 指定 RSA 算法;-keypass 指定私鑰密碼為123456;
-keystore 指定密鑰文件名稱為 leslie.keystore;-validity指定有效期為365天。

3.1.3 生成數字證書
根據上述生成的 leslie.keystore 文件,執行以下命令就可以生成數字證書 leslie.cer
1 keytool -export -alias everygold -storepass 123456 -file leslie.cer -keystore leslie.keystore
提示: -alias 指定別名為 everygold; -storepass 指定私鑰為 123456;
-file 指定導出證書的文件名為 leslie.cer;-keystore 指定之前生成的密鑰文件的文件名
注意: -alias 和- storepass 必須為生成 leslie.keystore 密鑰文件時所指定的別名和密碼一致,否則證書導出失敗
生成證書

若需要獲取 BASE64 或 DER 證書,可以使用導出功能,在圖片上按 “復制到文件”,選擇文件格式即可。

以文本格式打開導出的 BASE64 證書,可以看到
1 -----BEGIN CERTIFICATE-----
2 MIICRjCCAa+gAwIBAgIEIvzKsDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD
3 TjELMAkGA1UECBMCR0QxCzAJBgNVBAcTAkdaMQ4wDAYDVQQKEwVwdWppbjEMMAoG
4 A1UECxMDU3VuMQ8wDQYDVQQDEwZMZXNsaWUwHhcNMTcwODI5MDMwMjE4WhcNMTgw
5 ODI5MDMwMjE4WjBWMQswCQYDVQQGEwJDTjELMAkGA1UECBMCR0QxCzAJBgNVBAcT
6 AkdaMQ4wDAYDVQQKEwVwdWppbjEMMAoGA1UECxMDU3VuMQ8wDQYDVQQDEwZMZXNs
7 aWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKFVrCaKFi2GtJjyuWSPrJah
8 





9 -----END CERTIFICATE-----
或者使用以下語句,也可查看到導出的證書
1 keytool -list -rfc -keystore d:/leslie.keystore -storepass 123456
顯示結果與上述方法一致

3.2 腳本代碼生成證書
若對KEYTOOL工具不太熟悉的朋友 ,也可通過JAVA代碼直接生成數字證書,原理與KEYTOOL生成的基本一致。
1 public class CerTest {
2 public static void main(String[] args){
3 CerTest test=new CerTest();
4 //生成 keystore 文件
5 test.getKeyStore();
6 //生成 *.cer 證書文件
7 test.export();
8 }
9
10 public void execCommand(String[] arstringCommand) {
11 for (int i = 0; i < arstringCommand.length; i++) {
12 System.out.print(arstringCommand[i] + " ");
13 }
14 try {
15 Runtime.getRuntime().exec(arstringCommand);
16
17 } catch (Exception e) {
18 System.out.println(e.getMessage());
19 }
20 }
21
22 public void execCommand(String arstringCommand) {
23 try {
24 Runtime.getRuntime().exec(arstringCommand);
25
26 } catch (Exception e) {
27 System.out.println(e.getMessage());
28 }
29 }
30
31 /**
32 * 生成 *.keystore
33 */
34 public void getKeyStore() {
35 String[] arstringCommand = new String[] {
36
37 "cmd ", "/k",
38 "start", // cmd Shell命令
39
40 "keytool",
41 "-genkey", // -genkey表示生成密鑰
42 "-validity", // -validity指定證書有效期(單位:天),這里是365天
43 "365",
44 "-keysize",// 指定密鑰長度
45 "1024",
46 "-alias", // -alias指定別名,這里是everygold
47 "everygold",
48 "-keyalg", // -keyalg 指定密鑰的算法 (如 RSA DSA(如果不指定默認采用DSA))
49 "RSA",
50 "-keystore", // -keystore指定存儲位置,這里是d:/leslie.keystore
51 "d:/leslie.keystore",
52 "-dname",// CN=(名字與姓氏), OU=(組織單位名稱), O=(組織名稱), L=(城市或區域名稱),
53 // ST=(州或省份名稱), C=(單位的兩字母國家代碼)"
54 "CN=(leslie), OU=(everygold), O=(pujinwang), L=(Guangzhou), ST=(Guangdong), C=(CN)",
55 "-storepass", // 指定密鑰庫的密碼(獲取keystore信息所需的密碼)
56 "123456",
57 "-keypass",// 指定別名條目的密碼(私鑰的密碼)
58 "123456",
59 "-v"// -v 顯示密鑰庫中的證書詳細信息
60 };
61 execCommand(arstringCommand);
62 }
63
64 /**
65 * 導出證書文件
66 */
67 public void export() {
68
69 String[] arstringCommand = new String[] {
70
71 "cmd ", "/k",
72 "start", // cmd Shell命令
73 "keytool",
74 "-export", // - export指定為導出操作
75 "-keystore", // -keystore指定keystore文件,這里是d:/leslie.keystore
76 "d:/leslie.keystore",
77 "-alias", // -alias指定別名,這里是ss
78 "everygold",
79 "-file",//-file指向導出路徑
80 "d:/leslie.cer",
81 "-storepass",// 指定密鑰庫的密碼
82 "123456"
83 };
84 execCommand(arstringCommand);
85
86 }
87 }
運行成功后,可獲取與3.1節相同的 leslie.keystore 文件與 leslie.cer 數字證書。
若需要獲取 BASE64 或 DER 證書,也可使用與 3.1.3 節所述方式獲取 ,在此不再重復介紹。
3.3 利用在線工具獲取數字證書
如果覺得使用KEYTOOL或代碼生成數字證書過于繁瑣,可以直接使用在線生成工具生成免費數字證書。一般在線生成的證書有效期為 3 個月到 1年,到期后需要續費或證書無效。以下是幾個常用的在線證書生成工具,由于用法比較簡單,在此不作詳細介紹。
- Amazon Web Services (AWS) 是 Amazon.com 旗下的一個網絡云服務站點
address:https://aws.amazon.com/cn/?nc2=h_lg
- ChinaSSL是亞狐科技旗下專為客戶提供數字證書、網絡安全服務的站點
address:https://csr.chinassl.net/index.html
- MySSL 則是亞洲誠信(TRUSTASIA)旗下專為用戶提供網絡安全云服務平臺
address:https://myssl.com/csr_create.html
四、獲取公鑰和私鑰
在第二節已經介紹過,在加密算法中有對稱加密,非對稱加密,Hash算法等幾類。在對稱加密算法中,加密使用的密鑰和解密使用的密鑰是相同的,使用起來比較簡單。而公鑰與私鑰一般用于非對稱的加密方式,是安全性最高,使用最為頻密的加密方式,下面幾節,將為大家介紹一下非對稱加密的使用方式。
公鑰(Public Key)與私鑰(Private Key)是通過一種算法得到的一個密鑰對(即一個公鑰和一個私鑰),公鑰是密鑰對中公開的部分,私鑰則是非公開的部分。公鑰通常用于加密會話密鑰、驗證數字簽名,加密數據可以用相應的私鑰進行數據解密。通過這種算法得到的密鑰對能保證在世界范圍內是唯一的。使用這個密鑰對的時候,如果用其中一個密鑰加密一段數據,必須用另一個密鑰解密。比如用公鑰加密數據就必須用私鑰解密,如果用私鑰簽名則必須用公鑰驗簽,否則數據將不會成功生成。
由于使用 KEYTOOL 等工具無法直接導出公鑰和私鑰,所以必須通過代碼進行導出。而公鑰和私鑰都是二進制數據,所以一般 用Base 64 方式進行保存。下面以上述有證書為例子,導出對應的公鑰與私鑰。
1 public abstract class Coder {
2
3 /**
4 * BASE64解密
5 *
6 * @param key
7 * @return
8 * @throws Exception
9 */
10 public static byte[] decryptBASE64(String key) throws Exception {
11 return (new BASE64Decoder()).decodeBuffer(key);
12 }
13
14 /**
15 * BASE64加密
16 *
17 * @param key
18 * @return
19 * @throws Exception
20 */
21 public static String encryptBASE64(byte[] key) throws Exception {
22 return (new BASE64Encoder()).encodeBuffer(key).replace("\r", "").replace("\n", "");
23 }
24 }
25
26 public class KeyStoreTool{
27
28 /**
29 * Java密鑰庫(Java Key Store,JKS)KEY_STORE
30 */
31 public static final String KEY_STORE = "JKS";
32
33 public static final String X509 = "X.509";
34
35 /**
36 * 獲得KeyStore
37 *
38 * @version 2016-3-16
39 * @param keyStorePath
40 * @param password
41 * @return
42 * @throws Exception
43 */
44 public static KeyStore getKeyStore(String keyStorePath, String password)
45 throws Exception {
46
47 FileInputStream is = new FileInputStream(keyStorePath);
48 KeyStore ks = KeyStore.getInstance(KEY_STORE);
49 ks.load(is, password.toCharArray());
50 is.close();
51 return ks;
52 }
53
54 /**
55 * 由KeyStore獲得私鑰
56 * @param keyStorePath
57 * @param alias
58 * @param storePass
59 * @return
60 * @throws Exception
61 */
62 public static PrivateKey getPrivateKey(String keyStorePath, String alias, String storePass, String keyPass) throws Exception {
63 KeyStore ks = getKeyStore(keyStorePath, storePass);
64 PrivateKey key = (PrivateKey) ks.getKey(alias, keyPass.toCharArray());
65 return key;
66 }
67
68 /**
69 * 由Certificate獲得公鑰
70 * @param keyStorePath
71 * KeyStore路徑
72 * @param alias
73 * 別名
74 * @param storePass
75 * KeyStore訪問密碼
76 * @return
77 * @throws Exception
78 */
79 public static PublicKey getPublicKey(String keyStorePath, String alias, String storePass) throws Exception {
80 KeyStore ks = getKeyStore(keyStorePath, storePass);
81 PublicKey key = ks.getCertificate(alias).getPublicKey();
82 return key;
83 }
84
85 /**
86 * 從KeyStore中獲取公鑰,并經BASE64編碼
87 * @param keyStorePath
88 * @param alias
89 * @param storePass
90 * @return
91 * @throws Exception
92 */
93 public static String getStrPublicKey(String keyStorePath, String alias,String storePass) throws Exception{
94 PublicKey key = getPublicKey(keyStorePath, alias, storePass);
95 String strKey = Coder.encryptBASE64(key.getEncoded());
96 return strKey;
97 }
98
99 /*
100 * 獲取經BASE64編碼后的私鑰
101 * @param keyStorePath
102 * @param alias
103 * @param storePass
104 * @param keyPass
105 * @return
106 * @throws Exception
107 */
108 public static String getStrPrivateKey(String keyStorePath, String alias,String storePass, String keyPass) throws Exception{
109
110 PrivateKey key = getPrivateKey(keyStorePath, alias, storePass, keyPass);
111 String strKey = Coder.encryptBASE64(key.getEncoded());
112 return strKey;
113 }
114
115 public static void main(String args[]){
116 // 公鑰
117 String strPublicKey = "";
118 // 私鑰
119 String strPrivateKey = "";
120
121 try {
122 strPublicKey = KeyStoreCoder.getStrPublicKey("d://leslie.keystore", "everygold", "123456");
123 System.out.println("公鑰 = 【" + strPublicKey + "】");
124
125 strPrivateKey = KeyStoreCoder.getStrPrivateKey("d://leslie.keystore", "everygold", "123456", "123456");
126 System.out.println("\n私鑰 = 【" + strPrivateKey + "】");
127 } catch (Exception e1) {
128 e1.printStackTrace();
129 }
130 }
131 }
輸出結果:

為方便保存,一般我們會以Base64位方式把公鑰與私鑰存儲起來
publicKey.key 公鑰文件:
1 -----BEGIN PUBLIC KEY-----
2 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbUPe3WGFA0QPGCrPrXCUR7K7M
3 aZQY1btYZrAFjpT/k00zkj/AfcUeEZk6Tf+9mgvZ3KRVvSFaA9kYiVCJOjGfnW2H
4 fk6u7iOwSs/kwpC5uUzdoWlc5ZX7iC9SACXJgDg/T5HBRpXpsEkxhzWLUKy1FQDC
5 KduLuEFdzaO4XsSX7QIDAQAB
6 -----END PUBLIC KEY-----
privateKey.key 私鑰文件:
1 -----BEGIN PRIVATE KEY-----
2 MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBANtQ97dYYUDRA8YK
3 s+tcJRHsrsxplBjVu1hmsAWOlP+TTTOSP8B9xR4RmTpN/72aC9ncpFW9IVoD2RiJ
4 UIk6MZ+dbYd+Tq7uI7BKz+TCkLm5TN2haVzllfuIL1IAJcmAOD9PkcFGlemwSTGH
5 NYtQrLUVAMIp24u4QV3No7hexJftAgMBAAECgYEAkcm/8Yv5kimfFY3VzhXBuqmY
6 BOAGB4BEel5AkmEWoNIdVrPYVzAD0ZonPn/NCg+V4yvtveTsf7bhIJNfCum5Q8NL
7 V0YNn5+C1JMZoI9BrRXQjCH30Oy78QfHH9ATigDZ7cr/ke/0hJqO4hks++XlM6Oy
8 MIuoHy1WUTy5Hm3qbWECQQDvZFDixgwYwiyC9fzEj8NmWyINZx+Ny2DhnZKtKN6r
9 o7aplGfBPU9NZ/vLQk7AZPS+24hhu7CYlOUhhhoQjWr1AkEA6ogaMAQfPslyrl1W
10 R2KkPEOVbSSy4IJ5ZIBeJCDisgEtLb9EZ4JzXIfN6usyiJNtwf5k04zEkWBz1f5r
11 mtTOGQJBAMSodkI1TA6yxPo4thOLvovBZfH4u1UytD3jwnD52CLMdOxOfAWlJhaC
12 y7iomiU3Sk/X7OvM0kAmYSzvC055vlkCQQDOR0sRNG7u4Gv3pKyAOOhPAPpqdr7F
13 7LwsgyNKD4qUGajM9c+KYxhoKCIbHybhLRp6Z+/yiXtSik0XyKCIG+fxAkEAsdlz
14 YkpcG6T38wC0px+Mhq06AIhEF3sy3wLbM3d4ABlNMj3HqlHMPtvCV1L3dpc/8y89
15 dAPu9OiHf8nyar9eVQ==
16 -----END PRIVATE KEY-----
現在 *.keystore 、*.cer 證書、Base64 證書、公鑰文件 public.key 私鑰文件 private.key 都已成功生成,下面的章節將為大家介紹數據加密、數據解密、數字簽名、數字驗簽的使用方式。但在此之前,我想先為大家講解一下它們的概念與應用場景。
記得在第二節曾經向大家介紹過對稱加密與非對稱加密的區別,由于對稱加密的加密與解密的密鑰都是一致,加密解密雙方都同時擁有密鑰,容易造成密鑰的泄露。所以一般企業在不同的業務流程下都會使用不同的密鑰,以防數據被泄露。但在大型的企業中,若使用對稱加密這種方式,企業將會產生大量的密鑰,難于管理而且安全性難以保障,并不可取,所以就產生了非對稱加密方式。
非對稱加密的情況下,在企業需要獲取客戶端數據時,可以把公鑰向客戶端公開,數據進行加密后,就算加密數據被涉取,在沒有私鑰的情況,數據內容都不會被破解,確保了數據的安全性。這時,只要企業保證私鑰的保密前提下,一個公鑰可以向多個客戶端進行公開用作數據傳輸加密。
而數字簽名的應用場景有點相反,數字簽名是企業為客戶端確認數據來源的準確性而提供的服務。一般應用于政府機關、行政部門、金融行業、資訊行業等企業的數據發布上。數據都是由企業通過私鑰進行簽名,只要客戶端擁有對應的公鑰,就可以對數據進行驗簽。只要驗簽成功,就能證明該數據是來源此數字證書所屬的企業,以保證數據來源的可靠性。一般在國家政策的發布,企業數據的公開,經濟數據的公開等場景下應該最為廣泛。
五、數字證書加密與解密
經過上面的介紹,大家應該了解到數據加密、數據解密、數字簽名、數字驗簽的使用場景。
下面將為大家介紹數據加密與解密方式:
1 public abstract class Coder {
2
3 /**
4 * BASE64解密
5 *
6 * @param key
7 * @return
8 * @throws Exception
9 */
10 public static byte[] decryptBASE64(String key) throws Exception {
11 return (new BASE64Decoder()).decodeBuffer(key);
12 }
13
14 /**
15 * BASE64加密
16 *
17 * @param key
18 * @return
19 * @throws Exception
20 */
21 public static String encryptBASE64(byte[] key) throws Exception {
22 return (new BASE64Encoder()).encodeBuffer(key).replace("\r", "").replace("\n", "");
23 }
24 }
25
26 public class MyCoder extends Coder{
27
28 /**
29 * 使用公鑰加密數據
30 * @param publicKey
31 * @param srcData
32 * @return
33 * @throws Exception
34 */
35 public static String encryptByPublicKey(String publicKey, String srcData) throws Exception{
36 //解密
37 byte[] pk = Coder.decryptBASE64(publicKey);
38 X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
39 KeyFactory kf = KeyFactory.getInstance("RSA");
40 //獲取公鑰
41 PublicKey pubKey = kf.generatePublic(spec);
42
43 // 對數據加密
44 Cipher cipher = Cipher.getInstance("RSA");
45 cipher.init(Cipher.ENCRYPT_MODE, pubKey);
46
47 byte[] doFinal = cipher.doFinal(srcData.getBytes());
48 return encryptBASE64(doFinal);
49 }
50
51
52 /*
53 * 使用私鑰解密數據
54 * @param privateKey
55 * @param data
56 * @return
57 * @throws Exception
58 */
59 public static String descryptByPrivateKey(String privateKey, String data) throws Exception{
60 // BASE64轉碼解密私鑰
61 byte[] pk = Coder.decryptBASE64(privateKey);
62 // BASE64轉碼解密密文
63 byte[] text = decryptBASE64(data);
64 PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pk);
65 KeyFactory kf = KeyFactory.getInstance("RSA");
66 // 獲取私鑰
67 PrivateKey prvKey = kf.generatePrivate(spec);
68
69 // 對數據加密
70 Cipher cipher = Cipher.getInstance("RSA");
71 cipher.init(Cipher.DECRYPT_MODE, prvKey);
72
73 byte[] doFinal = cipher.doFinal(text);
74 return new String(doFinal);
75 }
76
77 public static void main(){
78 // 公鑰
79 String strPublicKey = "";
80 // 私鑰
81 String strPrivateKey = "";
82
83 try {
84 strPublicKey = KeyStoreTool.getStrPublicKey("d://leslie.keystore", "everygold", "123456");
85 strPrivateKey = KeyStoreTool.getStrPrivateKey("d://leslie.keystore", "everygold", "123456", "123456");
86 } catch (Exception e1) {
87 e1.printStackTrace();
88 }
89
90 // 原文
91 String originalText = "原文 = 雖然我窮,但是再窮也要去旅游!";
92 System.out.println(originalText);
93
94 try {
95 // RSA算法 公鑰加密隨機數
96 String secretText = MyCoder.encryptByPublicKey(strPublicKey, originalText);
97 System.out.println("\n經RSA公鑰加密后 = " + secretText);
98 System.out.println("\n經RSA公鑰加密后長度 = " + secretText.length());
99
100 String text = MyCoder.descryptByPrivateKey(strPrivateKey, secretText);
101 System.out.println("\n經RSA私鑰解密后 = 【" + text + "】");
102 System.out.println("\n經RSA私鑰解密后長度 = 【" + text.length() + "】");
103
104 } catch (Exception e) {
105 e.printStackTrace();
106 }
107 }
108 }
測試結果

當然,如果公鑰和私鑰已經保存在 public.key 與 private.key 文件里,就可以直接從文件讀取,無須再通過 *. keystore 文件獲取。
六、數字證書簽名與驗簽
上面介紹過簽名與驗簽主要用于政策機關,金融機構,權威信息網站對外公報信息時使用。
一般使用場景下,簽名與驗簽往往會與加密解密同時使用,企業會生成兩對密鑰,一對用于對企業正式的注冊名稱進行簽名,另一對用于詳細數據的加密。客戶驗簽后就可證明信息來源的真確性,然后再對詳細信息進行解密。
簽名與驗簽代碼如下:
1 public class MySign {
2 /*
3 * @param keyStorePath 密鑰庫存儲路徑
4 * @param alias 密鑰庫別名
5 * @param password 密鑰庫密碼
6 */
7 private static String keyStorePath,alias,password;
8
9 private static Certificate getCertificate()
10 throws Exception {
11 KeyStore keyStore = KeyStoreTool.getKeyStore(keyStorePath, password);
12 Certificate certificate = keyStore.getCertificate(alias);
13 return certificate;
14 }
15
16 public static void setKeyStorePath(String path){
17 MySign.keyStorePath=path;
18 }
19
20 public static void setAlias(String alias){
21 MySign.alias=alias;
22 }
23
24 public static void setPassword(String password){
25 MySign.password=password;
26 }
27
28 /*
29 * 生成數據簽名
30 * @param data 源數據
31 */
32 public static byte[] sign(byte[] data)
33 throws Exception {
34 // 獲得證書
35 X509Certificate x509Certificate = (X509Certificate) getCertificate();
36 // 獲取KeyStore
37 KeyStore keyStore = KeyStoreTool.getKeyStore(keyStorePath, password);
38 // 取得私鑰
39 PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
40 // 構建簽名
41 Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
42 signature.initSign(privateKey);
43 signature.update(data);
44 return signature.sign();
45 }
46
47 /*
48 * 生成數據簽名并以BASE64編碼
49 * @param data 源數據
50 */
51 public static String signToBase64(String data)
52 throws Exception {
53 byte[] byteData=data.getBytes();
54 return Base64.encode(sign(byteData));
55 }
56
57 /*
58 * 對二進制數據進行驗簽
59 * @param data 已加密數據
60 * @param sign 數據簽名[BASE64]
61 */
62 public static boolean verifySign(byte[] data, String sign)
63 throws Exception {
64 // 獲得證書
65 X509Certificate x509Certificate = (X509Certificate) getCertificate();
66 // 獲得公鑰
67 PublicKey publicKey = x509Certificate.getPublicKey();
68 // 構建簽名
69 Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
70 signature.initVerify(publicKey);
71 signature.update(data);
72 return signature.verify(Base64.decode(sign));
73 }
74
75 /*
76 * 對String數據進行驗簽
77 * @param data 字符串
78 * @param sign 數據簽名[BASE64]
79 */
80 public static boolean verifySginString(String data, String sign)
81 throws Exception {
82 byte[] byteData = data.getBytes();
83 return verifySign(byteData, sign);
84 }
85
86 public static void main(String[] args) throws Exception {
87
88 MySign.setKeyStorePath("d://leslie.keystore");
89 MySign.setPassword("123456");
90 MySign.setAlias("everygold");
91 String sign="驢友的天空俱樂部";
92 String base64=MySign.signToBase64(sign);
93 System.out.println("簽名為:"+sign+"\n\n簽名后數據:\n"+base64);
94 boolean isRight=MySign.verifySginString(sign,base64);
95 System.out.println("\n驗簽結果:"+isRight);
96 }
97 }