CA認證原理以及實現(上)


轉自: http://yale.iteye.com/blog/1675344

 

原理基礎
數字證書為發布公鑰提供了一種簡便的途徑,其數字證書則成為加密算法以及公鑰的載體,依靠數字證書,我們可以構建一個簡單的加密網絡應用平台,數字證書就好比我們生活中的身份證,現實中,身份證由公安機關簽發,而網絡用戶的身份憑證由數字證書頒發認證機構—CA簽發,只有經過CA簽發的證書在網絡中才具備可認證性,CA並不是一個單純的防御手段,它集合了多種密碼學算法:
消息摘要算法:MD5、和SHA(對數字證書本省做摘要處理,用於驗證數據完整性服務器)
對稱加密算法:RC2、RC4、IDEA、DES、AES(對數據進行加密/解密操作,用於保證數據保密性服務)
非對稱加密算法:RSA、DH(對數據進行加密/解密操作,用於保證數據保密性服務)
數字簽名算法:RSA、DSA(對數據進行簽名/驗證操作,保證數據的完整性和抗否認性)。

證書的簽發過程實際上是對申請數字證書的公鑰做數字簽名,證書的驗證過程實際上是對數字證書的公鑰做驗證簽名,其中還包含證書有效期驗證,通過CA數字證書,我們對網絡上傳輸的數據進行加密/解密和簽名/驗證操作,確保數據機密性、完整性、抗否認性、認證性,保證交易實體身份的真實性,保證網絡安全性。

 

所有證書有多種文件編碼格式,主要包括:
CER編碼(規范編碼格式):是數字證書的一種編碼格式,它是BER(基本編碼格式)的一個變種,比BER規定得更嚴格
DER(卓越編碼格式):同樣是BER的一個變種,與CER的不同在於,DER使用定長模式,而CER使用變長模式。

所有證書都符合公鑰基礎設施(PKI)制定的ITU-T X509國際標准,PKCS(公鑰加密標准)由RSA實驗室和其他安全系統開發商為促進公鑰密碼的發展而制定的一系列標准,比如:PKCS#7(密碼消息語法標准----文件后綴名:.p7b、.p7c、.spc)、PKCS#10(證書請求語法標准----文件后綴名:.p10、.csr)、PKCS#12(個人信息交換語法標准----文件后綴名:.p12、.pfx)等

在獲得數字證書后,可以將其保存在電腦中,也可以保存在USB Key等相應的設備中。

 

我們先來看一個簡單的證書機構簽發的流程:

這里的認證機構如何是證書申請者本身,將獲得自簽名證書。


當客戶端獲得服務器下發的數字證書后,即可使用數字證書進行加密交互:

 

數字證書的應用環境是在https安全協議中,使用流程遠比上述加密交互流程復雜,但是相關操作封裝在傳輸層,對於應用層透明,在https安全協議中使用非對稱加密算法交換密鑰,使用對稱加密算法對數據進行加密/解密操作,提高加密/解密效率

 

要獲得數字證書,我們需要使用數字證書管理工具: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握手協議交互后,數據交互雙方確定了本次會話使用的對稱加密算法以及密鑰,就可以開始進行加密數據交互了,以下是握手協議服務器端和客戶端構建加密交互的相關流程圖:

協商算法

1、 隨機數為后續構建密鑰准備
2、 其他信息包括服務器證書、甚至包含獲取客戶端證書的請求

 

驗證算法
如果服務器端回復客戶端時帶有其他信息,則進入數字證書驗證階段
客戶端驗證服務器端證書:

 

服務器端驗證客戶端證書:

 

產生密鑰
當服務器端和客戶端經過上述流程后,就開始密鑰構建交互了,服務器端和客戶端最初需要主密鑰為構建會話密鑰做准備:

上述5、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);  
    } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM