原理基礎
數字證書爲發佈公鑰提供了一種簡便的途徑,其數字證書則成爲加密算法以及公鑰的載體,依靠數字證書,咱們能夠構建一個簡單的加密網絡應用平臺,數字證書就比如咱們生活中的身份證,現實中,身份證由公安機關簽發,而網絡用戶的身份憑證由數字證書頒發認證機構—CA簽發,只有通過CA簽發的證書在網絡中才具有可認證性,CA並非一個單純的防護手段,它集合了多種密碼學算法:
消息摘要算法:MD五、和SHA(對數字證書本省作摘要處理,用於驗證數據完整性服務器)
對稱加密算法:RC二、RC四、IDEA、DES、AES(對數據進行加密/解密操做,用於保證數據保密性服務)
非對稱加密算法:RSA、DH(對數據進行加密/解密操做,用於保證數據保密性服務)
數字簽名算法:RSA、DSA(對數據進行簽名/驗證操做,保證數據的完整性和抗否定性)。 javascript
證書的簽發過程其實是對申請數字證書的公鑰作數字簽名,證書的驗證過程其實是對數字證書的公鑰作驗證簽名,其中還包含證書有效期驗證,經過CA數字證書,咱們對網絡上傳輸的數據進行加密/解密和簽名/驗證操做,確保數據機密性、完整性、抗否定性、認證性,保證交易實體身份的真實性,保證網絡安全性。 java
全部證書有多種文件編碼格式,主要包括:
CER編碼(規範編碼格式):是數字證書的一種編碼格式,它是BER(基本編碼格式)的一個變種,比BER規定得更嚴格
DER(卓越編碼格式):一樣是BER的一個變種,與CER的不一樣在於,DER使用定長模式,而CER使用變長模式。 算法
全部證書都符合公鑰基礎設施(PKI)制定的ITU-T X509國際標準,PKCS(公鑰加密標準)由RSA實驗室和其餘安全系統開發商爲促進公鑰密碼的發展而制定的一系列標準,好比:PKCS#7(密碼消息語法標準----文件後綴名:.p7b、.p7c、.spc)、PKCS#10(證書請求語法標準----文件後綴名:.p十、.csr)、PKCS#12(我的信息交換語法標準----文件後綴名:.p十二、.pfx)等 安全
在得到數字證書後,能夠將其保存在電腦中,也能夠保存在USB Key等相應的設備中。 服務器
咱們先來看一個簡單的證書機構簽發的流程: 網絡
這裏的認證機構如何是證書申請者自己,將得到自簽名證書。 app
當客戶端得到服務器下發的數字證書後,便可使用數字證書進行加密交互:
異步
數字證書的應用環境是在https安全協議中,使用流程遠比上述加密交互流程複雜,可是相關操做封裝在傳輸層,對於應用層透明,在https安全協議中使用非對稱加密算法交換密鑰,使用對稱加密算法對數據進行加密/解密操做,提升加密/解密效率 socket
要得到數字證書,咱們須要使用數字證書管理工具:KeyTool和OpenSSL構建CSR(數字證書籤發申請),交由CA機構簽發,造成最終的數字證書,這裏咱們不對KeyTool作講解(KeyTool不含有根證書,所以KeyTool沒有辦法做爲CA),網上資料對keytool講解的也挺多的,咱們下面針對OpenSSL進行講解。 工具
在咱們搭建OPEN SSL環境前,咱們要知道HTTPS協議和SSL/TLS協議,簡單的說,HTTPS就是HTTP+SSL(secure socket layer)/TLS(Transport Layer Security)協議,HTTPS協議爲數字證書提供了最佳的應用環境,HTTPS協議通常在服務器中配置,如HTTP服務器APACHE、TOMCAT等。
SSL:位於TCP/IP中的網絡傳輸層,做爲網絡通信提供安全以及數據完整性的一種安全協議
TLS:做爲SSL協議的繼承者,成爲下一代網絡安全性和數據完整性安全協議
SSL共有3個版本:1.0、2.0、3.0,TLS也有1.0、2.0、3.0,一般咱們說的SSL/TLS協議指的是SSL3.0/TLS1.0的網絡傳輸層安全協議
SSL/TLS協議分爲兩層:
記錄協議:建議在可靠的傳輸協議之上,爲高層協議提供數據封裝、壓縮、加密等基本功能的支持
握手協議:創建在SSL記錄協議之上,用於在實際的數據傳輸開始前,通信雙方進行身份認證、協商加密算法、交換加密密鑰等
通過了SSL/TLS握手協議交互後,數據交互雙方肯定了本次會話使用的對稱加密算法以及密鑰,就能夠開始進行加密數據交互了,如下是握手協議服務器端和客戶端構建加密交互的相關流程圖:
協商算法
一、 隨機數爲後續構建密鑰準備
二、 其餘信息包括服務器證書、甚至包含獲取客戶端證書的請求
驗證算法
若是服務器端回覆客戶端時帶有其餘信息,則進入數字證書驗證階段
客戶端驗證服務器端證書:
服務器端驗證客戶端證書:
產生密鑰
當服務器端和客戶端通過上述流程後,就開始密鑰構建交互了,服務器端和客戶端最初須要主密鑰爲構建會話密鑰作準備:
上述五、6不存在次序關係,由於是異步完成
會話密鑰
完成上述主密鑰構建操做後,服務器端和客戶端將創建會話密鑰,完成握手協議:
加密交互
上述服務器端和客戶端完成了握手協議之後就進入正式會話階段,若是上述流程中有任何一端受到外界因素干擾發生異常,則從新進入協商算法階段,下面流程表現進入會話階段後,服務器端和客戶端將使用會話密鑰進行加密交互:
代碼解釋
在JAVA 6 以上版本中提供了完善的數字證書管理的實現,咱們不須要關注相關具體算法,僅經過操做密鑰庫和數字證書就能夠完成相應的加密/解密和簽名/驗證操做,密鑰庫管理私鑰,數字證書管理公鑰,私鑰和密鑰分屬消息傳遞兩方,進行加密消息的傳遞。
所以,咱們能夠將密鑰庫看作私鑰相關操做的入口,數字證書則是公鑰相關操做的入口:
- /****
- * 得到私鑰,得到私鑰後,經過RSA算方法實現進行"私鑰加密,公鑰解密"和"公鑰加密,私鑰解密"操做
- * @param keyStorePath 密鑰庫路徑
- * @param alias 別名
- * @param password 密碼
- * @return 私鑰
- */
- private static PrivateKey getPrivateKeyByKeyStore(String keyStorePath,String alias,String password)throws Exception{
- //得到密鑰庫
- KeyStore ks = getKeyStore(keyStorePath,password);
- //得到私鑰
- return (PrivateKey)ks.getKey(alias, password.toCharArray());
-
- }
-
- /****
- * 由Certificate得到公鑰,得到公鑰後,經過RSA算方法實現進行"私鑰加密,公鑰解密"和"公鑰加密,私鑰解密"操做
- * @param certificatePath 證書路徑
- * @return 公鑰
- */
- private static PublicKey getPublicKeyByCertificate(String certificatePath)throws Exception {
- //得到證書
- Certificate certificate = getCertificate(certificatePath);
- //得到公鑰
- return certificate.getPublicKey();
- }
-
- /****
- * 加載數字證書,JAVA 6僅支持x.509的數字證書
- * @param certificatePath 證書路徑
- * @return 證書
- * @throws Exception
- */
- private static Certificate getCertificate(String certificatePath) throws Exception{
- //實例化證書工廠
- CertificateFactory certificateFactory = CertificateFactory.getInstance("x.509");
- //取得證書文件流
- FileInputStream in = new FileInputStream(certificatePath);
- //生成證書
- Certificate certificate = certificateFactory.generateCertificate(in);
- //關閉證書文件流
- in.close();
- return certificate;
- }
-
- /****
- * 得到Certificate
- * @param keyStorePath 密鑰庫路徑
- * @param alias 別名
- * @param password 密碼
- * @return 證書
- * @throws Exception
- */
- private static Certificate getCertificate(String keyStorePath,String alias,String password) throws Exception{
- //由密鑰庫得到數字證書構建數字簽名對象
- //得到密鑰庫
- KeyStore ks = getKeyStore(keyStorePath,password);
- //得到證書
- return ks.getCertificate(alias);
- }
-
- /****
- * 加載密鑰庫,加載了之後,咱們就能經過相應的方法得到私鑰,也能夠得到數字證書
- * @param keyStorePath 密鑰庫路徑
- * @param password 密碼
- * @return 密鑰庫
- * @throws Exception
- */
- private static KeyStore getKeyStore(String keyStorePath,String password) throws Exception{
- //實例化密鑰庫
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
- //得到密鑰庫文件流
- FileInputStream is = new FileInputStream(keyStorePath);
- //加載密鑰庫
- ks.load(is,password.toCharArray());
- //關閉密鑰庫文件流
- is.close();
- return ks;
- }
-
- /****
- * 私鑰加密
- * @param data 待加密的數據
- * @param keyStorePath 密鑰庫路徑
- * @param alias 別名
- * @param password 密碼
- * @return 加密數據
- * @throws Exception
- */
- public static byte[] encryptByPriateKey(byte[] data,String keyStorePath,String alias,String password) throws Exception{
- //得到私鑰
- PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath,alias,password);
- //對數據加密
- Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
- return cipher.doFinal(data);
- }
-
- /****
- * 私鑰解密
- * @param data 待解密數據
- * @param keyStorePath 密鑰庫路徑
- * @param alias 別名
- * @param password 密碼
- * @return 解密數據
- * @throws Exception
- */
- public static byte[] decryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password) throws Exception{
- //取得私鑰
- PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath,alias,password);
- //對數據解密
- Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE,privateKey);
- return cipher.doFinal(data);
- }
-
- /****
- * 公鑰加密
- * @param data 等待加密數據
- * @param certificatePath 證書路徑
- * @return 加密數據
- * @throws Exception
- */
- public static byte[] encryptByPublicKey(byte[] data,String certificatePath) throws Exception{
- //取得公鑰
- PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
- //對數據加密
- Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE,publicKey);
- return cipher.doFinal(data);
- }
-
- /****
- * 公鑰解密
- * @param data 等待解密的數據
- * @param certificatePath 證書路徑
- * @return 解密數據
- * @throws Exception
- */
- public static byte[] decryptByPublicKey(byte[] data,String certificatePath)throws Exception{
- //取得公鑰
- PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
- //對數據解密
- Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, publicKey);
- return cipher.doFinal(data);
- }
-
- /****
- * @param sign 簽名
- * @param keyStorePath 密鑰庫路徑
- * @param alias 別名
- * @param password 密碼
- * @return 簽名
- * @throws Exception
- */
- public static byte[] sign(byte[] sign,String keyStorePath,String alias,String password)throws Exception{
- //得到證書
- X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath,alias,password);
- //構建簽名,由證書指定簽名算法
- Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
- //獲取私鑰
- PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath,alias,password);
- //初始化簽名,由私鑰構建
- signature.initSign(privateKey);
- signature.update(sign);
- return signature.sign();
- }
-
-
- /****
- * 驗證簽名
- * @param data 數據
- * @param sign 簽名
- * @param certificatePath 證書路徑
- * @return 驗證經過爲真
- * @throws Exception
- */
- public static boolean verify(byte[] data,byte[] sign,String certificatePath) throws Exception{
- //得到證書
- X509Certificate x509Certificate = (X509Certificate)getCertificate(certificatePath);
- //由證書構建簽名
- Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
- //由證書初始化簽名,其實是使用了證書中的公鑰
- signature.initVerify(x509Certificate);
- signature.update(data);
- return signature.verify(sign);
- }
-
- //咱們假定密鑰庫文件yale.keystore存儲在D盤根目錄,數字證書文件yale.cer也存儲在D盤根目錄
- /****
- * 公鑰加密---私鑰解密
- * @throws Exception
- */
- public static void test1() throws Exception{
- System.err.println("公鑰加密---私鑰解密");
- String inputStr = "數字證書";
- byte[] data = inputStr.getBytes();
- //公鑰加密
- byte[] encrypt = CertificateCoder.encryptByPublicKey(data, certificatePath);
- //私鑰解密
- byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt, keyStorePath, alias, password);
- String outputStr = new String(decrypt);
- System.err.println("加密前:\n" + inputStr);
- System.err.println("解密後:\n" + outputStr);
- }
-
- /****
- * 私鑰加密---公鑰解密
- * @throws Exception
- */
- public static void test2()throws Exception{
- System.err.println("私鑰加密---公鑰解密");
- String inputStr = "數字簽名";
- byte[] data = inputStr.getBytes();
- //私鑰加密
- byte[] encodedData = CertificateCoder.encryptByPriateKey(data, keyStorePath, alias, password);
- //公鑰加密
- byte[] decodeData = CertificateCoder.decryptByPublicKey(encodedData, certificatePath);
- String outputStr = new String (decodeData);
- System.err.println("加密前:\n" + inputStr);
- System.err.println("解密後:\n" + outputStr);
- }
-
- public static void testSign()throws Exception{
- String inputStr = "簽名";
- byte[] data = inputStr.getBytes();
- System.err.println("私鑰簽名---公鑰驗證");
- //產生簽名
- byte[] sign = CertificateCoder.sign(data, keyStorePath, alias, password);
- System.err.println("簽名:\n" + Hex.encodeHexString(sign));
- //驗證簽名
- boolean status = CertificateCoder.verify(data, sign, certificatePath);
- System.err.println("狀態:\n " + status);
- }