Java加密技術


如基本的單向加密算法: 

  • BASE64 嚴格地說,屬於編碼格式,而非加密算法
  • MD5(Message Digest algorithm 5,信息摘要算法)
  • SHA(Secure Hash Algorithm,安全散列算法)
  • HMAC(Hash Message Authentication Code,散列消息鑒別碼)


    復雜的對稱加密(DES、PBE)、非對稱加密算法: 

  • DES(Data Encryption Standard,數據加密算法)
  • PBE(Password-based encryption,基於密碼驗證)
  • RSA(算法的名字以發明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman)
  • DH(Diffie-Hellman算法,密鑰一致協議)
  • DSA(Digital Signature Algorithm,數字簽名)
  • ECC(Elliptic Curves Cryptography,橢圓曲線密碼編碼學)



    本篇內容簡要介紹BASE64MD5SHAHMAC幾種方法。 
    MD5SHAHMAC這三種加密算法,可謂是非可逆加密,就是不可解密的加密方法。我們通常只把他們作為加密的基礎。單純的以上三種的加密並不可靠。 

BASE64 
按照RFC2045的定義,Base64被定義為:Base64內容傳送編碼被設計用來把任意序列的8位字節描述為一種不易被人直接識別的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.) 
常見於郵件、http加密,截取http信息,你就會發現登錄操作的用戶名、密碼字段通過BASE64加密的。 

 

通過java代碼實現如下: 

Java代碼    收藏代碼
  1. /** 
  2.  * BASE64解密 
  3.  *  
  4.  * @param key 
  5.  * @return 
  6.  * @throws Exception 
  7.  */  
  8. public static byte[] decryptBASE64(String key) throws Exception {  
  9.     return (new BASE64Decoder()).decodeBuffer(key);  
  10. }  
  11.   
  12. /** 
  13.  * BASE64加密 
  14.  *  
  15.  * @param key 
  16.  * @return 
  17.  * @throws Exception 
  18.  */  
  19. public static String encryptBASE64(byte[] key) throws Exception {  
  20.     return (new BASE64Encoder()).encodeBuffer(key);  
  21. }  


主要就是BASE64Encoder、BASE64Decoder兩個類,我們只需要知道使用對應的方法即可。另,BASE加密后產生的字節位數是8的倍數,如果不夠位數以=符號填充。 

MD5 
MD5 -- message-digest algorithm 5 (信息-摘要算法)縮寫,廣泛用於加密和解密技術,常用於文件校驗。校驗?不管文件多大,經過MD5后都能生成唯一的MD5值。好比現在的ISO校驗,都是MD5校驗。怎么用?當然是把ISO經過MD5后產生MD5的值。一般下載linux-ISO的朋友都見過下載鏈接旁邊放着MD5的串。就是用來驗證文件是否一致的。 

 

通過java代碼實現如下: 

Java代碼    收藏代碼
  1. /** 
  2.  * MD5加密 
  3.  *  
  4.  * @param data 
  5.  * @return 
  6.  * @throws Exception 
  7.  */  
  8. public static byte[] encryptMD5(byte[] data) throws Exception {  
  9.   
  10.     MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
  11.     md5.update(data);  
  12.   
  13.     return md5.digest();  
  14.   
  15. }  



通常我們不直接使用上述MD5加密。通常將MD5產生的字節數組交給BASE64再加密一把,得到相應的字符串。 

SHA 
SHA(Secure Hash Algorithm,安全散列算法),數字簽名等密碼學應用中重要的工具,被廣泛地應用於電子商務等信息安全領域。雖然,SHA與MD5通過碰撞法都被破解了, 但是SHA仍然是公認的安全加密算法,較之MD5更為安全。 

 

通過java代碼實現如下: 

Java代碼    收藏代碼
  1.     /** 
  2.      * SHA加密 
  3.      *  
  4.      * @param data 
  5.      * @return 
  6.      * @throws Exception 
  7.      */  
  8.     public static byte[] encryptSHA(byte[] data) throws Exception {  
  9.   
  10.         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
  11.         sha.update(data);  
  12.   
  13.         return sha.digest();  
  14.   
  15.     }  
  16. }  



HMAC 
HMAC(Hash Message Authentication Code,散列消息鑒別碼,基於密鑰的Hash算法的認證協議。消息鑒別碼實現鑒別的原理是,用公開函數和密鑰產生一個固定長度的值作為認證標識,用這個標識鑒別消息的完整性。使用一個密鑰生成一個固定大小的小數據塊,即MAC,並將其加入到消息中,然后傳輸。接收方利用與發送方共享的密鑰進行鑒別認證等。 

 

通過java代碼實現如下: 

Java代碼    收藏代碼
  1. /** 
  2.  * 初始化HMAC密鑰 
  3.  *  
  4.  * @return 
  5.  * @throws Exception 
  6.  */  
  7. public static String initMacKey() throws Exception {  
  8.     KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);  
  9.   
  10.     SecretKey secretKey = keyGenerator.generateKey();  
  11.     return encryptBASE64(secretKey.getEncoded());  
  12. }  
  13.   
  14. /** 
  15.  * HMAC加密 
  16.  *  
  17.  * @param data 
  18.  * @param key 
  19.  * @return 
  20.  * @throws Exception 
  21.  */  
  22. public static byte[] encryptHMAC(byte[] data, String key) throws Exception {  
  23.   
  24.     SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);  
  25.     Mac mac = Mac.getInstance(secretKey.getAlgorithm());  
  26.     mac.init(secretKey);  
  27.   
  28.     return mac.doFinal(data);  
  29.   
  30. }  



給出一個完整類,如下: 

Java代碼    收藏代碼
  1. import java.security.MessageDigest;  
  2.   
  3. import javax.crypto.KeyGenerator;  
  4. import javax.crypto.Mac;  
  5. import javax.crypto.SecretKey;  
  6.   
  7. import sun.misc.BASE64Decoder;  
  8. import sun.misc.BASE64Encoder;  
  9.   
  10. /** 
  11.  * 基礎加密組件 
  12.  *  
  13.  * @author 梁棟 
  14.  * @version 1.0 
  15.  * @since 1.0 
  16.  */  
  17. public abstract class Coder {  
  18.     public static final String KEY_SHA = "SHA";  
  19.     public static final String KEY_MD5 = "MD5";  
  20.   
  21.     /** 
  22.      * MAC算法可選以下多種算法 
  23.      *  
  24.      * <pre> 
  25.      * HmacMD5  
  26.      * HmacSHA1  
  27.      * HmacSHA256  
  28.      * HmacSHA384  
  29.      * HmacSHA512 
  30.      * </pre> 
  31.      */  
  32.     public static final String KEY_MAC = "HmacMD5";  
  33.   
  34.     /** 
  35.      * BASE64解密 
  36.      *  
  37.      * @param key 
  38.      * @return 
  39.      * @throws Exception 
  40.      */  
  41.     public static byte[] decryptBASE64(String key) throws Exception {  
  42.         return (new BASE64Decoder()).decodeBuffer(key);  
  43.     }  
  44.   
  45.     /** 
  46.      * BASE64加密 
  47.      *  
  48.      * @param key 
  49.      * @return 
  50.      * @throws Exception 
  51.      */  
  52.     public static String encryptBASE64(byte[] key) throws Exception {  
  53.         return (new BASE64Encoder()).encodeBuffer(key);  
  54.     }  
  55.   
  56.     /** 
  57.      * MD5加密 
  58.      *  
  59.      * @param data 
  60.      * @return 
  61.      * @throws Exception 
  62.      */  
  63.     public static byte[] encryptMD5(byte[] data) throws Exception {  
  64.   
  65.         MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
  66.         md5.update(data);  
  67.   
  68.         return md5.digest();  
  69.   
  70.     }  
  71.   
  72.     /** 
  73.      * SHA加密 
  74.      *  
  75.      * @param data 
  76.      * @return 
  77.      * @throws Exception 
  78.      */  
  79.     public static byte[] encryptSHA(byte[] data) throws Exception {  
  80.   
  81.         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
  82.         sha.update(data);  
  83.   
  84.         return sha.digest();  
  85.   
  86.     }  
  87.   
  88.     /** 
  89.      * 初始化HMAC密鑰 
  90.      *  
  91.      * @return 
  92.      * @throws Exception 
  93.      */  
  94.     public static String initMacKey() throws Exception {  
  95.         KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);  
  96.   
  97.         SecretKey secretKey = keyGenerator.generateKey();  
  98.         return encryptBASE64(secretKey.getEncoded());  
  99.     }  
  100.   
  101.     /** 
  102.      * HMAC加密 
  103.      *  
  104.      * @param data 
  105.      * @param key 
  106.      * @return 
  107.      * @throws Exception 
  108.      */  
  109.     public static byte[] encryptHMAC(byte[] data, String key) throws Exception {  
  110.   
  111.         SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);  
  112.         Mac mac = Mac.getInstance(secretKey.getAlgorithm());  
  113.         mac.init(secretKey);  
  114.   
  115.         return mac.doFinal(data);  
  116.   
  117.     }  
  118. }  



再給出一個測試類: 

Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import org.junit.Test;  
  4.   
  5. /** 
  6.  *  
  7.  * @author 梁棟 
  8.  * @version 1.0 
  9.  * @since 1.0 
  10.  */  
  11. public class CoderTest {  
  12.   
  13.     @Test  
  14.     public void test() throws Exception {  
  15.         String inputStr = "簡單加密";  
  16.         System.err.println("原文:\n" + inputStr);  
  17.   
  18.         byte[] inputData = inputStr.getBytes();  
  19.         String code = Coder.encryptBASE64(inputData);  
  20.   
  21.         System.err.println("BASE64加密后:\n" + code);  
  22.   
  23.         byte[] output = Coder.decryptBASE64(code);  
  24.   
  25.         String outputStr = new String(output);  
  26.   
  27.         System.err.println("BASE64解密后:\n" + outputStr);  
  28.   
  29.         // 驗證BASE64加密解密一致性  
  30.         assertEquals(inputStr, outputStr);  
  31.   
  32.         // 驗證MD5對於同一內容加密是否一致  
  33.         assertArrayEquals(Coder.encryptMD5(inputData), Coder  
  34.                 .encryptMD5(inputData));  
  35.   
  36.         // 驗證SHA對於同一內容加密是否一致  
  37.         assertArrayEquals(Coder.encryptSHA(inputData), Coder  
  38.                 .encryptSHA(inputData));  
  39.   
  40.         String key = Coder.initMacKey();  
  41.         System.err.println("Mac密鑰:\n" + key);  
  42.   
  43.         // 驗證HMAC對於同一內容,同一密鑰加密是否一致  
  44.         assertArrayEquals(Coder.encryptHMAC(inputData, key), Coder.encryptHMAC(  
  45.                 inputData, key));  
  46.   
  47.         BigInteger md5 = new BigInteger(Coder.encryptMD5(inputData));  
  48.         System.err.println("MD5:\n" + md5.toString(16));  
  49.   
  50.         BigInteger sha = new BigInteger(Coder.encryptSHA(inputData));  
  51.         System.err.println("SHA:\n" + sha.toString(32));  
  52.   
  53.         BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));  
  54.         System.err.println("HMAC:\n" + mac.toString(16));  
  55.     }  
  56. }  



控制台輸出: 

Console代碼    收藏代碼
  1. 原文:  
  2. 簡單加密  
  3. BASE64加密后:  
  4. 566A5Y2V5Yqg5a+G  
  5.   
  6. BASE64解密后:  
  7. 簡單加密  
  8. Mac密鑰:  
  9. uGxdHC+6ylRDaik++leFtGwiMbuYUJ6mqHWyhSgF4trVkVBBSQvY/a22xU8XT1RUemdCWW155Bke  
  10. pBIpkd7QHg==  
  11.   
  12. MD5:  
  13. -550b4d90349ad4629462113e7934de56  
  14. SHA:  
  15. 91k9vo7p400cjkgfhjh0ia9qthsjagfn  
  16. HMAC:  
  17. 2287d192387e95694bdbba2fa941009a  



注意 
編譯時,可能會看到如下提示: 

引用

 


警告:sun.misc.BASE64Decoder 是 Sun 的專用 API,可能會在未來版本中刪除 

import sun.misc.BASE64Decoder; 
               ^ 
警告:sun.misc.BASE64Encoder 是 Sun 的專用 API,可能會在未來版本中刪除 

import sun.misc.BASE64Encoder; 
               ^ 



BASE64Encoder和BASE64Decoder是非官方JDK實現類。雖然可以在JDK里能找到並使用,但是在API里查不到。JRE 中 sun 和 com.sun 開頭包的類都是未被文檔化的,他們屬於 java, javax 類庫的基礎,其中的實現大多數與底層平台有關,一般來說是不推薦使用的。 


    BASE64的加密解密是雙向的,可以求反解。 
    MD5、SHA以及HMAC是單向加密,任何數據加密后只會產生唯一的一個加密串,通常用來校驗數據在傳輸過程中是否被修改。其中HMAC算法有一個密鑰,增強了數據傳輸過程中的安全性,強化了算法外的不可控因素。 
    單向加密的用途主要是為了校驗數據在傳輸過程中是否被修改。 

 接下來我們介紹對稱加密算法,最常用的莫過於DES數據加密算法。 
DES 
DES-Data Encryption Standard,即數據加密算法。是IBM公司於1975年研究成功並公開發表的。DES算法的入口參數有三個:Key、Data、Mode。其中Key為8個字節共64位,是DES算法的工作密鑰;Data也為8個字節64位,是要被加密或被解密的數據;Mode為DES的工作方式,有兩種:加密或解密。 
  DES算法把64位的明文輸入塊變為64位的密文輸出塊,它所使用的密鑰也是64位。 

 

通過java代碼實現如下:Coder類見 Java加密技術(一) 

Java代碼    收藏代碼
  1. import java.security.Key;  
  2. import java.security.SecureRandom;  
  3.   
  4. import javax.crypto.Cipher;  
  5. import javax.crypto.KeyGenerator;  
  6. import javax.crypto.SecretKey;  
  7. import javax.crypto.SecretKeyFactory;  
  8. import javax.crypto.spec.DESKeySpec;  
  9.   
  10.   
  11. /** 
  12.  * DES安全編碼組件 
  13.  *  
  14.  * <pre> 
  15.  * 支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR) 
  16.  * DES                  key size must be equal to 56 
  17.  * DESede(TripleDES)    key size must be equal to 112 or 168 
  18.  * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available 
  19.  * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive) 
  20.  * RC2                  key size must be between 40 and 1024 bits 
  21.  * RC4(ARCFOUR)         key size must be between 40 and 1024 bits 
  22.  * 具體內容 需要關注 JDK Document http://.../docs/technotes/guides/security/SunProviders.html 
  23.  * </pre> 
  24.  *  
  25.  * @author 梁棟 
  26.  * @version 1.0 
  27.  * @since 1.0 
  28.  */  
  29. public abstract class DESCoder extends Coder {  
  30.     /** 
  31.      * ALGORITHM 算法 <br> 
  32.      * 可替換為以下任意一種算法,同時key值的size相應改變。 
  33.      *  
  34.      * <pre> 
  35.      * DES                  key size must be equal to 56 
  36.      * DESede(TripleDES)    key size must be equal to 112 or 168 
  37.      * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available 
  38.      * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive) 
  39.      * RC2                  key size must be between 40 and 1024 bits 
  40.      * RC4(ARCFOUR)         key size must be between 40 and 1024 bits 
  41.      * </pre> 
  42.      *  
  43.      * 在Key toKey(byte[] key)方法中使用下述代碼 
  44.      * <code>SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);</code> 替換 
  45.      * <code> 
  46.      * DESKeySpec dks = new DESKeySpec(key); 
  47.      * SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); 
  48.      * SecretKey secretKey = keyFactory.generateSecret(dks); 
  49.      * </code> 
  50.      */  
  51.     public static final String ALGORITHM = "DES";  
  52.   
  53.     /** 
  54.      * 轉換密鑰<br> 
  55.      *  
  56.      * @param key 
  57.      * @return 
  58.      * @throws Exception 
  59.      */  
  60.     private static Key toKey(byte[] key) throws Exception {  
  61.         DESKeySpec dks = new DESKeySpec(key);  
  62.         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);  
  63.         SecretKey secretKey = keyFactory.generateSecret(dks);  
  64.   
  65.         // 當使用其他對稱加密算法時,如AES、Blowfish等算法時,用下述代碼替換上述三行代碼  
  66.         // SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);  
  67.   
  68.         return secretKey;  
  69.     }  
  70.   
  71.     /** 
  72.      * 解密 
  73.      *  
  74.      * @param data 
  75.      * @param key 
  76.      * @return 
  77.      * @throws Exception 
  78.      */  
  79.     public static byte[] decrypt(byte[] data, String key) throws Exception {  
  80.         Key k = toKey(decryptBASE64(key));  
  81.   
  82.         Cipher cipher = Cipher.getInstance(ALGORITHM);  
  83.         cipher.init(Cipher.DECRYPT_MODE, k);  
  84.   
  85.         return cipher.doFinal(data);  
  86.     }  
  87.   
  88.     /** 
  89.      * 加密 
  90.      *  
  91.      * @param data 
  92.      * @param key 
  93.      * @return 
  94.      * @throws Exception 
  95.      */  
  96.     public static byte[] encrypt(byte[] data, String key) throws Exception {  
  97.         Key k = toKey(decryptBASE64(key));  
  98.         Cipher cipher = Cipher.getInstance(ALGORITHM);  
  99.         cipher.init(Cipher.ENCRYPT_MODE, k);  
  100.   
  101.         return cipher.doFinal(data);  
  102.     }  
  103.   
  104.     /** 
  105.      * 生成密鑰 
  106.      *  
  107.      * @return 
  108.      * @throws Exception 
  109.      */  
  110.     public static String initKey() throws Exception {  
  111.         return initKey(null);  
  112.     }  
  113.   
  114.     /** 
  115.      * 生成密鑰 
  116.      *  
  117.      * @param seed 
  118.      * @return 
  119.      * @throws Exception 
  120.      */  
  121.     public static String initKey(String seed) throws Exception {  
  122.         SecureRandom secureRandom = null;  
  123.   
  124.         if (seed != null) {  
  125.             secureRandom = new SecureRandom(decryptBASE64(seed));  
  126.         } else {  
  127.             secureRandom = new SecureRandom();  
  128.         }  
  129.   
  130.         KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);  
  131.         kg.init(secureRandom);  
  132.   
  133.         SecretKey secretKey = kg.generateKey();  
  134.   
  135.         return encryptBASE64(secretKey.getEncoded());  
  136.     }  
  137. }  


延續上一個類的實現,我們通過MD5以及SHA對字符串加密生成密鑰,這是比較常見的密鑰生成方式。 
再給出一個測試類: 

Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3.   
  4. import org.junit.Test;  
  5.   
  6. /** 
  7.  *  
  8.  * @author 梁棟 
  9.  * @version 1.0 
  10.  * @since 1.0 
  11.  */  
  12. public class DESCoderTest {  
  13.   
  14.     @Test  
  15.     public void test() throws Exception {  
  16.         String inputStr = "DES";  
  17.         String key = DESCoder.initKey();  
  18.         System.err.println("原文:\t" + inputStr);  
  19.   
  20.         System.err.println("密鑰:\t" + key);  
  21.   
  22.         byte[] inputData = inputStr.getBytes();  
  23.         inputData = DESCoder.encrypt(inputData, key);  
  24.   
  25.         System.err.println("加密后:\t" + DESCoder.encryptBASE64(inputData));  
  26.   
  27.         byte[] outputData = DESCoder.decrypt(inputData, key);  
  28.         String outputStr = new String(outputData);  
  29.   
  30.         System.err.println("解密后:\t" + outputStr);  
  31.   
  32.         assertEquals(inputStr, outputStr);  
  33.     }  
  34. }  


得到的輸出內容如下: 

Console代碼    收藏代碼
  1. 原文: DES  
  2. 密鑰: f3wEtRrV6q0=  
  3.   
  4. 加密后:    C6qe9oNIzRY=  
  5.   
  6. 解密后:    DES  


    由控制台得到的輸出,我們能夠比對加密、解密后結果一致。這是一種簡單的加密解密方式,只有一個密鑰。 
    其實DES有很多同胞兄弟,如DESede(TripleDES)、AES、Blowfish、RC2、RC4(ARCFOUR)。這里就不過多闡述了,大同小異,只要換掉ALGORITHM換成對應的值,同時做一個代碼替換SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);就可以了,此外就是密鑰長度不同了。 

Java代碼    收藏代碼
    1. /** 
    2.  * DES          key size must be equal to 56 
    3.  * DESede(TripleDES) key size must be equal to 112 or 168 
    4.  * AES          key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available 
    5.  * Blowfish     key size must be multiple of 8, and can only range from 32 to 448 (inclusive) 
    6.  * RC2          key size must be between 40 and 1024 bits 
    7.  * RC4(ARCFOUR) key size must be between 40 and 1024 bits 
    8.  **/ 

除了DES,我們還知道有DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)等多種對稱加密方式,其實現方式大同小異,這里介紹對稱加密的另一個算法——PBE 

PBE 
    PBE——Password-based encryption(基於密碼加密)。其特點在於口令由用戶自己掌管,不借助任何物理媒體;采用隨機數(這里我們叫做鹽)雜湊多重加密等方法保證數據的安全性。是一種簡便的加密方式。 

 

通過java代碼實現如下:Coder類見 Java加密技術(一) 

Java代碼    收藏代碼
  1. import java.security.Key;  
  2. import java.util.Random;  
  3.   
  4. import javax.crypto.Cipher;  
  5. import javax.crypto.SecretKey;  
  6. import javax.crypto.SecretKeyFactory;  
  7. import javax.crypto.spec.PBEKeySpec;  
  8. import javax.crypto.spec.PBEParameterSpec;  
  9.   
  10. /** 
  11.  * PBE安全編碼組件 
  12.  *  
  13.  * @author 梁棟 
  14.  * @version 1.0 
  15.  * @since 1.0 
  16.  */  
  17. public abstract class PBECoder extends Coder {  
  18.     /** 
  19.      * 支持以下任意一種算法 
  20.      *  
  21.      * <pre> 
  22.      * PBEWithMD5AndDES  
  23.      * PBEWithMD5AndTripleDES  
  24.      * PBEWithSHA1AndDESede 
  25.      * PBEWithSHA1AndRC2_40 
  26.      * </pre> 
  27.      */  
  28.     public static final String ALGORITHM = "PBEWITHMD5andDES";  
  29.   
  30.     /** 
  31.      * 鹽初始化 
  32.      *  
  33.      * @return 
  34.      * @throws Exception 
  35.      */  
  36.     public static byte[] initSalt() throws Exception {  
  37.         byte[] salt = new byte[8];  
  38.         Random random = new Random();  
  39.         random.nextBytes(salt);  
  40.         return salt;  
  41.     }  
  42.   
  43.     /** 
  44.      * 轉換密鑰<br> 
  45.      *  
  46.      * @param password 
  47.      * @return 
  48.      * @throws Exception 
  49.      */  
  50.     private static Key toKey(String password) throws Exception {  
  51.         PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());  
  52.         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);  
  53.         SecretKey secretKey = keyFactory.generateSecret(keySpec);  
  54.   
  55.         return secretKey;  
  56.     }  
  57.   
  58.     /** 
  59.      * 加密 
  60.      *  
  61.      * @param data 
  62.      *            數據 
  63.      * @param password 
  64.      *            密碼 
  65.      * @param salt 
  66.      *            鹽 
  67.      * @return 
  68.      * @throws Exception 
  69.      */  
  70.     public static byte[] encrypt(byte[] data, String password, byte[] salt)  
  71.             throws Exception {  
  72.   
  73.         Key key = toKey(password);  
  74.   
  75.         PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);  
  76.         Cipher cipher = Cipher.getInstance(ALGORITHM);  
  77.         cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);  
  78.   
  79.         return cipher.doFinal(data);  
  80.   
  81.     }  
  82.   
  83.     /** 
  84.      * 解密 
  85.      *  
  86.      * @param data 
  87.      *            數據 
  88.      * @param password 
  89.      *            密碼 
  90.      * @param salt 
  91.      *            鹽 
  92.      * @return 
  93.      * @throws Exception 
  94.      */  
  95.     public static byte[] decrypt(byte[] data, String password, byte[] salt)  
  96.             throws Exception {  
  97.   
  98.         Key key = toKey(password);  
  99.   
  100.         PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);  
  101.         Cipher cipher = Cipher.getInstance(ALGORITHM);  
  102.         cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);  
  103.   
  104.         return cipher.doFinal(data);  
  105.   
  106.     }  
  107. }  



再給出一個測試類: 

Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import org.junit.Test;  
  4.   
  5. /** 
  6.  *  
  7.  * @author 梁棟 
  8.  * @version 1.0 
  9.  * @since 1.0 
  10.  */  
  11. public class PBECoderTest {  
  12.   
  13.     @Test  
  14.     public void test() throws Exception {  
  15.         String inputStr = "abc";  
  16.         System.err.println("原文: " + inputStr);  
  17.         byte[] input = inputStr.getBytes();  
  18.   
  19.         String pwd = "efg";  
  20.         System.err.println("密碼: " + pwd);  
  21.   
  22.         byte[] salt = PBECoder.initSalt();  
  23.   
  24.         byte[] data = PBECoder.encrypt(input, pwd, salt);  
  25.   
  26.         System.err.println("加密后: " + PBECoder.encryptBASE64(data));  
  27.   
  28.         byte[] output = PBECoder.decrypt(data, pwd, salt);  
  29.         String outputStr = new String(output);  
  30.   
  31.         System.err.println("解密后: " + outputStr);  
  32.         assertEquals(inputStr, outputStr);  
  33.     }  
  34.   
  35. }  



控制台輸出: 

Console代碼    收藏代碼
  1. 原文: abc  
  2. 密碼: efg  
  3. 加密后: iCZ0uRtaAhE=  
  4.   
  5. 解密后: abc  


    后續我們會介紹非對稱加密算法,如RSA、DSA、DH、ECC等。

接下來我們介紹典型的非對稱加密算法——RSA 

RSA 
    這種算法1978年就出現了,它是第一個既能用於數據加密也能用於數字簽名的算法。它易於理解和操作,也很流行。算法的名字以發明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。 
    這種加密算法的特點主要是密鑰的變化,上文我們看到DES只有一個密鑰。相當於只有一把鑰匙,如果這把鑰匙丟了,數據也就不安全了。RSA同時有兩把鑰匙,公鑰與私鑰。同時支持數字簽名。數字簽名的意義在於,對傳輸過來的數據進行校驗。確保數據在傳輸工程中不被修改。 

流程分析: 

  1. 甲方構建密鑰對兒,將公鑰公布給乙方,將私鑰保留。
  2. 甲方使用私鑰加密數據,然后用私鑰對加密后的數據簽名,發送給乙方簽名以及加密后的數據;乙方使用公鑰、簽名來驗證待解密數據是否有效,如果有效使用公鑰對數據解密。
  3. 乙方使用公鑰加密數據,向甲方發送經過加密后的數據;甲方獲得加密數據,通過私鑰解密。



按如上步驟給出序列圖,如下: 



通過java代碼實現如下:Coder類見 Java加密技術(一) 

Java代碼    收藏代碼
  1. import java.security.Key;  
  2. import java.security.KeyFactory;  
  3. import java.security.KeyPair;  
  4. import java.security.KeyPairGenerator;  
  5. import java.security.PrivateKey;  
  6. import java.security.PublicKey;  
  7. import java.security.Signature;  
  8. import java.security.interfaces.RSAPrivateKey;  
  9. import java.security.interfaces.RSAPublicKey;  
  10. import java.security.spec.PKCS8EncodedKeySpec;  
  11. import java.security.spec.X509EncodedKeySpec;  
  12.   
  13. import java.util.HashMap;  
  14. import java.util.Map;  
  15.   
  16. import javax.crypto.Cipher;  
  17.   
  18. /** 
  19.  * RSA安全編碼組件 
  20.  *  
  21.  * @author 梁棟 
  22.  * @version 1.0 
  23.  * @since 1.0 
  24.  */  
  25. public abstract class RSACoder extends Coder {  
  26.     public static final String KEY_ALGORITHM = "RSA";  
  27.     public static final String SIGNATURE_ALGORITHM = "MD5withRSA";  
  28.   
  29.     private static final String PUBLIC_KEY = "RSAPublicKey";  
  30.     private static final String PRIVATE_KEY = "RSAPrivateKey";  
  31.   
  32.     /** 
  33.      * 用私鑰對信息生成數字簽名 
  34.      *  
  35.      * @param data 
  36.      *            加密數據 
  37.      * @param privateKey 
  38.      *            私鑰 
  39.      *  
  40.      * @return 
  41.      * @throws Exception 
  42.      */  
  43.     public static String sign(byte[] data, String privateKey) throws Exception {  
  44.         // 解密由base64編碼的私鑰  
  45.         byte[] keyBytes = decryptBASE64(privateKey);  
  46.   
  47.         // 構造PKCS8EncodedKeySpec對象  
  48.         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
  49.   
  50.         // KEY_ALGORITHM 指定的加密算法  
  51.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  52.   
  53.         // 取私鑰匙對象  
  54.         PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
  55.   
  56.         // 用私鑰對信息生成數字簽名  
  57.         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
  58.         signature.initSign(priKey);  
  59.         signature.update(data);  
  60.   
  61.         return encryptBASE64(signature.sign());  
  62.     }  
  63.   
  64.     /** 
  65.      * 校驗數字簽名 
  66.      *  
  67.      * @param data 
  68.      *            加密數據 
  69.      * @param publicKey 
  70.      *            公鑰 
  71.      * @param sign 
  72.      *            數字簽名 
  73.      *  
  74.      * @return 校驗成功返回true 失敗返回false 
  75.      * @throws Exception 
  76.      *  
  77.      */  
  78.     public static boolean verify(byte[] data, String publicKey, String sign)  
  79.             throws Exception {  
  80.   
  81.         // 解密由base64編碼的公鑰  
  82.         byte[] keyBytes = decryptBASE64(publicKey);  
  83.   
  84.         // 構造X509EncodedKeySpec對象  
  85.         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
  86.   
  87.         // KEY_ALGORITHM 指定的加密算法  
  88.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  89.   
  90.         // 取公鑰匙對象  
  91.         PublicKey pubKey = keyFactory.generatePublic(keySpec);  
  92.   
  93.         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
  94.         signature.initVerify(pubKey);  
  95.         signature.update(data);  
  96.   
  97.         // 驗證簽名是否正常  
  98.         return signature.verify(decryptBASE64(sign));  
  99.     }  
  100.   
  101.     /** 
  102.      * 解密<br> 
  103.      * 用私鑰解密 
  104.      *  
  105.      * @param data 
  106.      * @param key 
  107.      * @return 
  108.      * @throws Exception 
  109.      */  
  110.     public static byte[] decryptByPrivateKey(byte[] data, String key)  
  111.             throws Exception {  
  112.         // 對密鑰解密  
  113.         byte[] keyBytes = decryptBASE64(key);  
  114.   
  115.         // 取得私鑰  
  116.         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
  117.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  118.         Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
  119.   
  120.         // 對數據解密  
  121.         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
  122.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  123.   
  124.         return cipher.doFinal(data);  
  125.     }  
  126.   
  127.     /** 
  128.      * 解密<br> 
  129.      * 用公鑰解密 
  130.      *  
  131.      * @param data 
  132.      * @param key 
  133.      * @return 
  134.      * @throws Exception 
  135.      */  
  136.     public static byte[] decryptByPublicKey(byte[] data, String key)  
  137.             throws Exception {  
  138.         // 對密鑰解密  
  139.         byte[] keyBytes = decryptBASE64(key);  
  140.   
  141.         // 取得公鑰  
  142.         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
  143.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  144.         Key publicKey = keyFactory.generatePublic(x509KeySpec);  
  145.   
  146.         // 對數據解密  
  147.         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
  148.         cipher.init(Cipher.DECRYPT_MODE, publicKey);  
  149.   
  150.         return cipher.doFinal(data);  
  151.     }  
  152.   
  153.     /** 
  154.      * 加密<br> 
  155.      * 用公鑰加密 
  156.      *  
  157.      * @param data 
  158.      * @param key 
  159.      * @return 
  160.      * @throws Exception 
  161.      */  
  162.     public static byte[] encryptByPublicKey(byte[] data, String key)  
  163.             throws Exception {  
  164.         // 對公鑰解密  
  165.         byte[] keyBytes = decryptBASE64(key);  
  166.   
  167.         // 取得公鑰  
  168.         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
  169.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  170.         Key publicKey = keyFactory.generatePublic(x509KeySpec);  
  171.   
  172.         // 對數據加密  
  173.         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
  174.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  175.   
  176.         return cipher.doFinal(data);  
  177.     }  
  178.   
  179.     /** 
  180.      * 加密<br> 
  181.      * 用私鑰加密 
  182.      *  
  183.      * @param data 
  184.      * @param key 
  185.      * @return 
  186.      * @throws Exception 
  187.      */  
  188.     public static byte[] encryptByPrivateKey(byte[] data, String key)  
  189.             throws Exception {  
  190.         // 對密鑰解密  
  191.         byte[] keyBytes = decryptBASE64(key);  
  192.   
  193.         // 取得私鑰  
  194.         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
  195.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  196.         Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
  197.   
  198.         // 對數據加密  
  199.         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
  200.         cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
  201.   
  202.         return cipher.doFinal(data);  
  203.     }  
  204.   
  205.     /** 
  206.      * 取得私鑰 
  207.      *  
  208.      * @param keyMap 
  209.      * @return 
  210.      * @throws Exception 
  211.      */  
  212.     public static String getPrivateKey(Map<String, Object> keyMap)  
  213.             throws Exception {  
  214.         Key key = (Key) keyMap.get(PRIVATE_KEY);  
  215.   
  216.         return encryptBASE64(key.getEncoded());  
  217.     }  
  218.   
  219.     /** 
  220.      * 取得公鑰 
  221.      *  
  222.      * @param keyMap 
  223.      * @return 
  224.      * @throws Exception 
  225.      */  
  226.     public static String getPublicKey(Map<String, Object> keyMap)  
  227.             throws Exception {  
  228.         Key key = (Key) keyMap.get(PUBLIC_KEY);  
  229.   
  230.         return encryptBASE64(key.getEncoded());  
  231.     }  
  232.   
  233.     /** 
  234.      * 初始化密鑰 
  235.      *  
  236.      * @return 
  237.      * @throws Exception 
  238.      */  
  239.     public static Map<String, Object> initKey() throws Exception {  
  240.         KeyPairGenerator keyPairGen = KeyPairGenerator  
  241.                 .getInstance(KEY_ALGORITHM);  
  242.         keyPairGen.initialize(1024);  
  243.   
  244.         KeyPair keyPair = keyPairGen.generateKeyPair();  
  245.   
  246.         // 公鑰  
  247.         RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  
  248.   
  249.         // 私鑰  
  250.         RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();  
  251.   
  252.         Map<String, Object> keyMap = new HashMap<String, Object>(2);  
  253.   
  254.         keyMap.put(PUBLIC_KEY, publicKey);  
  255.         keyMap.put(PRIVATE_KEY, privateKey);  
  256.         return keyMap;  
  257.     }  
  258. }  


再給出一個測試類: 

Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import org.junit.Before;  
  4. import org.junit.Test;  
  5.   
  6. import java.util.Map;  
  7.   
  8. /** 
  9.  *  
  10.  * @author 梁棟 
  11.  * @version 1.0 
  12.  * @since 1.0 
  13.  */  
  14. public class RSACoderTest {  
  15.     private String publicKey;  
  16.     private String privateKey;  
  17.   
  18.     @Before  
  19.     public void setUp() throws Exception {  
  20.         Map<String, Object> keyMap = RSACoder.initKey();  
  21.   
  22.         publicKey = RSACoder.getPublicKey(keyMap);  
  23.         privateKey = RSACoder.getPrivateKey(keyMap);  
  24.         System.err.println("公鑰: \n\r" + publicKey);  
  25.         System.err.println("私鑰: \n\r" + privateKey);  
  26.     }  
  27.   
  28.     @Test  
  29.     public void test() throws Exception {  
  30.         System.err.println("公鑰加密——私鑰解密");  
  31.         String inputStr = "abc";  
  32.         byte[] data = inputStr.getBytes();  
  33.   
  34.         byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);  
  35.   
  36.         byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,  
  37.                 privateKey);  
  38.   
  39.         String outputStr = new String(decodedData);  
  40.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  41.         assertEquals(inputStr, outputStr);  
  42.   
  43.     }  
  44.   
  45.     @Test  
  46.     public void testSign() throws Exception {  
  47.         System.err.println("私鑰加密——公鑰解密");  
  48.         String inputStr = "sign";  
  49.         byte[] data = inputStr.getBytes();  
  50.   
  51.         byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);  
  52.   
  53.         byte[] decodedData = RSACoder  
  54.                 .decryptByPublicKey(encodedData, publicKey);  
  55.   
  56.         String outputStr = new String(decodedData);  
  57.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  58.         assertEquals(inputStr, outputStr);  
  59.   
  60.         System.err.println("私鑰簽名——公鑰驗證簽名");  
  61.         // 產生簽名  
  62.         String sign = RSACoder.sign(encodedData, privateKey);  
  63.         System.err.println("簽名:\r" + sign);  
  64.   
  65.         // 驗證簽名  
  66.         boolean status = RSACoder.verify(encodedData, publicKey, sign);  
  67.         System.err.println("狀態:\r" + status);  
  68.         assertTrue(status);  
  69.   
  70.     }  
  71.   
  72. }  


控制台輸出: 

Console代碼    收藏代碼
  1. 公鑰:   
  2.   
  3. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYU/+I0+z1aBl5X6DUUOHQ7FZpmBSDbKTtx89J  
  4. EcB64jFCkunELT8qiKly7fzEqD03g8ALlu5XvX+bBqHFy7YPJJP0ekE2X3wjUnh2NxlqpH3/B/xm  
  5. 1ZdSlCwDIkbijhBVDjA/bu5BObhZqQmDwIxlQInL9oVz+o6FbAZCyHBd7wIDAQAB  
  6.   
  7. 私鑰:   
  8.   
  9. MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJhT/4jT7PVoGXlfoNRQ4dDsVmmY  
  10. FINspO3Hz0kRwHriMUKS6cQtPyqIqXLt/MSoPTeDwAuW7le9f5sGocXLtg8kk/R6QTZffCNSeHY3  
  11. GWqkff8H/GbVl1KULAMiRuKOEFUOMD9u7kE5uFmpCYPAjGVAicv2hXP6joVsBkLIcF3vAgMBAAEC  
  12. gYBvZHWoZHmS2EZQqKqeuGr58eobG9hcZzWQoJ4nq/CarBAjw/VovUHE490uK3S9ht4FW7Yzg3LV  
  13. /MB06Huifh6qf/X9NQA7SeZRRC8gnCQk6JuDIEVJOud5jU+9tyumJakDKodQ3Jf2zQtNr+5ZdEPl  
  14. uwWgv9c4kmpjhAdyMuQmYQJBANn6pcgvyYaia52dnu+yBUsGkaFfwXkzFSExIbi0MXTkhEb/ER/D  
  15. rLytukkUu5S5ecz/KBa8U4xIslZDYQbLz5ECQQCy5dutt7RsxN4+dxCWn0/1FrkWl2G329Ucewm3  
  16. QU9CKu4D+7Kqdj+Ha3lXP8F0Etaaapi7+EfkRUpukn2ItZV/AkEAlk+I0iphxT1rCB0Q5CjWDY5S  
  17. Df2B5JmdEG5Y2o0nLXwG2w44OLct/k2uD4cEcuITY5Dvi/4BftMCZwm/dnhEgQJACIktJSnJwxLV  
  18. o9dchENPtlsCM9C/Sd2EWpqISSUlmfugZbJBwR5pQ5XeMUqKeXZYpP+HEBj1nS+tMH9u2/IGEwJA  
  19. fL8mZiZXan/oBKrblAbplNcKWGRVD/3y65042PAEeghahlJMiYquV5DzZajuuT0wbJ5xQuZB01+X  
  20. nfpFpBJ2dw==  
  21.   
  22. 公鑰加密——私鑰解密  
  23. 加密前: abc  
  24.   
  25. 解密后: abc  
  26. 公鑰:   
  27.   
  28. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdOj40yEB48XqWxmPILmJAc7UecIN7F32etSHF  
  29. 9rwbuEh3+iTPOGSxhoSQpOED0vOb0ZIMkBXZSgsxLaBSin2RZ09YKWRjtpCA0kDkiD11gj4tzTiM  
  30. l9qq1kwSK7ZkGAgodEn3yIILVmQDuEImHOXFtulvJ71ka07u3LuwUNdB/wIDAQAB  
  31.   
  32. 私鑰:   
  33.   
  34. MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAN06PjTIQHjxepbGY8guYkBztR5w  
  35. g3sXfZ61IcX2vBu4SHf6JM84ZLGGhJCk4QPS85vRkgyQFdlKCzEtoFKKfZFnT1gpZGO2kIDSQOSI  
  36. PXWCPi3NOIyX2qrWTBIrtmQYCCh0SffIggtWZAO4QiYc5cW26W8nvWRrTu7cu7BQ10H/AgMBAAEC  
  37. gYEAz2JWBizjI31bqhP4XiP9PuY5F3vqBW4T+L9cFbQiyumKJc58yzTWUAUGKIIn3enXLG7dNqGr  
  38. mbJro4JeFIJ3CiVDpXR9+FluIgI4SXm7ioGKF2NOMA9LR5Fu82W+pLfpTN2y2SaLYWEDZyp53BxY  
  39. j9gUxaxi1MQs+C1ZgDF2xmECQQDy70bQntbRfysP+ppCtd56YRnES1Tyekw0wryS2tr+ivQJl7JF  
  40. gp5rPAOXpgrq36xHDwUspQ0sJ0vj0O7ywxr1AkEA6SAaLhrJJrYucC0jxwAhUYyaPN+aOsWymaRh  
  41. 9jA/Wc0wp29SbGTh5CcMuGpXm1g0M+FKW3dGiHgS3rVUKim4owJAbnxgapUzAgiiHxxMeDaavnHW  
  42. 9C2GrtjsO7qtZOTgYI/1uT8itvZW8lJTF+9OW8/qXE76fXl7ai9dFnl5kzMk2QJBALfHz/vCsArt  
  43. mkRiwY6zApE4Z6tPl1V33ymSVovvUzHnOdD1SKQdD5t+UV/crb3QVi8ED0t2B0u0ZSPfDT/D7kMC  
  44. QDpwdj9k2F5aokLHBHUNJPFDAp7a5QMaT64gv/d48ITJ68Co+v5WzLMpzJBYXK6PAtqIhxbuPEc2  
  45. I2k1Afmrwyw=  
  46.   
  47. 私鑰加密——公鑰解密  
  48. 加密前: sign  
  49.   
  50. 解密后: sign  
  51. 私鑰簽名——公鑰驗證簽名  
  52. 簽名:  
  53. ud1RsIwmSC1pN22I4IXteg1VD2FbiehKUfNxgVSHzvQNIK+d20FCkHCqh9djP3h94iWnIUY0ifU+  
  54. mbJkhAl/i5krExOE0hknOnPMcEP+lZV1RbJI2zG2YooSp2XDleqrQk5e/QF2Mx0Zxt8Xsg7ucVpn  
  55. i3wwbYWs9wSzIf0UjlM=  
  56.   
  57. 狀態:  
  58. true  



    簡要總結一下,使用公鑰加密、私鑰解密,完成了乙方到甲方的一次數據傳遞,通過私鑰加密、公鑰解密,同時通過私鑰簽名、公鑰驗證簽名,完成了一次甲方到乙方的數據傳遞與驗證,兩次數據傳遞完成一整套的數據交互! 

類似數字簽名,數字信封是這樣描述的: 

數字信封 
  數字信封用加密技術來保證只有特定的收信人才能閱讀信的內容。 
流程: 
    信息發送方采用對稱密鑰來加密信息,然后再用接收方的公鑰來加密此對稱密鑰(這部分稱為數字信封),再將它和信息一起發送給接收方;接收方先用相應的私鑰打開數字信封,得到對稱密鑰,然后使用對稱密鑰再解開信息。

接下來我們介紹DSA數字簽名,非對稱加密的另一種實現。 
DSA 
DSA-Digital Signature Algorithm 是Schnorr和ElGamal簽名算法的變種,被美國NIST作為DSS(DigitalSignature Standard)。簡單的說,這是一種更高級的驗證方式,用作數字簽名。不單單只有公鑰、私鑰,還有數字簽名。私鑰加密生成數字簽名,公鑰驗證數據及簽名。如果數據和簽名不匹配則認為驗證失敗!數字簽名的作用就是校驗數據在傳輸過程中不被修改。數字簽名,是單向加密的升級! 


通過java代碼實現如下:Coder類見 Java加密技術(一) 

Java代碼    收藏代碼
  1. import java.security.Key;  
  2. import java.security.KeyFactory;  
  3. import java.security.KeyPair;  
  4. import java.security.KeyPairGenerator;  
  5. import java.security.PrivateKey;  
  6. import java.security.PublicKey;  
  7. import java.security.SecureRandom;  
  8. import java.security.Signature;  
  9. import java.security.interfaces.DSAPrivateKey;  
  10. import java.security.interfaces.DSAPublicKey;  
  11. import java.security.spec.PKCS8EncodedKeySpec;  
  12. import java.security.spec.X509EncodedKeySpec;  
  13. import java.util.HashMap;  
  14. import java.util.Map;  
  15.   
  16. /** 
  17.  * DSA安全編碼組件 
  18.  *  
  19.  * @author 梁棟 
  20.  * @version 1.0 
  21.  * @since 1.0 
  22.  */  
  23. public abstract class DSACoder extends Coder {  
  24.   
  25.     public static final String ALGORITHM = "DSA";  
  26.   
  27.     /** 
  28.      * 默認密鑰字節數 
  29.      *  
  30.      * <pre> 
  31.      * DSA  
  32.      * Default Keysize 1024   
  33.      * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive). 
  34.      * </pre> 
  35.      */  
  36.     private static final int KEY_SIZE = 1024;  
  37.   
  38.     /** 
  39.      * 默認種子 
  40.      */  
  41.     private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";  
  42.   
  43.     private static final String PUBLIC_KEY = "DSAPublicKey";  
  44.     private static final String PRIVATE_KEY = "DSAPrivateKey";  
  45.   
  46.     /** 
  47.      * 用私鑰對信息生成數字簽名 
  48.      *  
  49.      * @param data 
  50.      *            加密數據 
  51.      * @param privateKey 
  52.      *            私鑰 
  53.      *  
  54.      * @return 
  55.      * @throws Exception 
  56.      */  
  57.     public static String sign(byte[] data, String privateKey) throws Exception {  
  58.         // 解密由base64編碼的私鑰  
  59.         byte[] keyBytes = decryptBASE64(privateKey);  
  60.   
  61.         // 構造PKCS8EncodedKeySpec對象  
  62.         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
  63.   
  64.         // KEY_ALGORITHM 指定的加密算法  
  65.         KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);  
  66.   
  67.         // 取私鑰匙對象  
  68.         PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
  69.   
  70.         // 用私鑰對信息生成數字簽名  
  71.         Signature signature = Signature.getInstance(keyFactory.getAlgorithm());  
  72.         signature.initSign(priKey);  
  73.         signature.update(data);  
  74.   
  75.         return encryptBASE64(signature.sign());  
  76.     }  
  77.   
  78.     /** 
  79.      * 校驗數字簽名 
  80.      *  
  81.      * @param data 
  82.      *            加密數據 
  83.      * @param publicKey 
  84.      *            公鑰 
  85.      * @param sign 
  86.      *            數字簽名 
  87.      *  
  88.      * @return 校驗成功返回true 失敗返回false 
  89.      * @throws Exception 
  90.      *  
  91.      */  
  92.     public static boolean verify(byte[] data, String publicKey, String sign)  
  93.             throws Exception {  
  94.   
  95.         // 解密由base64編碼的公鑰  
  96.         byte[] keyBytes = decryptBASE64(publicKey);  
  97.   
  98.         // 構造X509EncodedKeySpec對象  
  99.         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
  100.   
  101.         // ALGORITHM 指定的加密算法  
  102.         KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);  
  103.   
  104.         // 取公鑰匙對象  
  105.         PublicKey pubKey = keyFactory.generatePublic(keySpec);  
  106.   
  107.         Signature signature = Signature.getInstance(keyFactory.getAlgorithm());  
  108.         signature.initVerify(pubKey);  
  109.         signature.update(data);  
  110.   
  111.         // 驗證簽名是否正常  
  112.         return signature.verify(decryptBASE64(sign));  
  113.     }  
  114.   
  115.     /** 
  116.      * 生成密鑰 
  117.      *  
  118.      * @param seed 
  119.      *            種子 
  120.      * @return 密鑰對象 
  121.      * @throws Exception 
  122.      */  
  123.     public static Map<String, Object> initKey(String seed) throws Exception {  
  124.         KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);  
  125.         // 初始化隨機產生器  
  126.         SecureRandom secureRandom = new SecureRandom();  
  127.         secureRandom.setSeed(seed.getBytes());  
  128.         keygen.initialize(KEY_SIZE, secureRandom);  
  129.   
  130.         KeyPair keys = keygen.genKeyPair();  
  131.   
  132.         DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();  
  133.         DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();  
  134.   
  135.         Map<String, Object> map = new HashMap<String, Object>(2);  
  136.         map.put(PUBLIC_KEY, publicKey);  
  137.         map.put(PRIVATE_KEY, privateKey);  
  138.   
  139.         return map;  
  140.     }  
  141.   
  142.     /** 
  143.      * 默認生成密鑰 
  144.      *  
  145.      * @return 密鑰對象 
  146.      * @throws Exception 
  147.      */  
  148.     public static Map<String, Object> initKey() throws Exception {  
  149.         return initKey(DEFAULT_SEED);  
  150.     }  
  151.   
  152.     /** 
  153.      * 取得私鑰 
  154.      *  
  155.      * @param keyMap 
  156.      * @return 
  157.      * @throws Exception 
  158.      */  
  159.     public static String getPrivateKey(Map<String, Object> keyMap)  
  160.             throws Exception {  
  161.         Key key = (Key) keyMap.get(PRIVATE_KEY);  
  162.   
  163.         return encryptBASE64(key.getEncoded());  
  164.     }  
  165.   
  166.     /** 
  167.      * 取得公鑰 
  168.      *  
  169.      * @param keyMap 
  170.      * @return 
  171.      * @throws Exception 
  172.      */  
  173.     public static String getPublicKey(Map<String, Object> keyMap)  
  174.             throws Exception {  
  175.         Key key = (Key) keyMap.get(PUBLIC_KEY);  
  176.   
  177.         return encryptBASE64(key.getEncoded());  
  178.     }  
  179. }  



再給出一個測試類: 

Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import org.junit.Test;  
  6.   
  7. /** 
  8.  *  
  9.  * @author 梁棟 
  10.  * @version 1.0 
  11.  * @since 1.0 
  12.  */  
  13. public class DSACoderTest {  
  14.   
  15.     @Test  
  16.     public void test() throws Exception {  
  17.         String inputStr = "abc";  
  18.         byte[] data = inputStr.getBytes();  
  19.   
  20.         // 構建密鑰  
  21.         Map<String, Object> keyMap = DSACoder.initKey();  
  22.   
  23.         // 獲得密鑰  
  24.         String publicKey = DSACoder.getPublicKey(keyMap);  
  25.         String privateKey = DSACoder.getPrivateKey(keyMap);  
  26.   
  27.         System.err.println("公鑰:\r" + publicKey);  
  28.         System.err.println("私鑰:\r" + privateKey);  
  29.   
  30.         // 產生簽名  
  31.         String sign = DSACoder.sign(data, privateKey);  
  32.         System.err.println("簽名:\r" + sign);  
  33.   
  34.         // 驗證簽名  
  35.         boolean status = DSACoder.verify(data, publicKey, sign);  
  36.         System.err.println("狀態:\r" + status);  
  37.         assertTrue(status);  
  38.   
  39.     }  
  40.   
  41. }  


控制台輸出: 

Console代碼    收藏代碼
  1. 公鑰:  
  2. MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZp  
  3. RV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn  
  4. xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuE  
  5. C/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJ  
  6. FnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo  
  7. g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAIu4RUlcQLp49PI0MrbssOY+3uySVnp0TULSv  
  8. 5T4VaHoKzsLHgGTrwOvsGA+V3yCNl2WDu3D84bSLF7liTWgOj+SMOEaPk4VyRTlLXZWGPsf1Mfd9  
  9. 21XAbMeVyKDSHHVGbMjBScajf3bXooYQMlyoHiOt/WrCo+mv7efstMM0PGo=  
  10.   
  11. 私鑰:  
  12. MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2  
  13. USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4  
  14. O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmC  
  15. ouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCB  
  16. gLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhR  
  17. kImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFwIVAIegLUtmm2oQKQJTOiLugHTSjl/q  
  18.   
  19. 簽名:  
  20. MC0CFQCMg0J/uZmF8GuRpr3TNq48w60nDwIUJCyYNah+HtbU6NcQfy8Ac6LeLQs=  
  21.   
  22. 狀態:  
  23. true  



注意狀態為true,就驗證成功! 

ECC 
ECC-Elliptic Curves Cryptography,橢圓曲線密碼編碼學,是目前已知的公鑰體制中,對每比特所提供加密強度最高的一種體制。在軟件注冊保護方面起到很大的作用,一般的序列號通常由該算法產生。 
    當我開始整理《Java加密技術(二)》的時候,我就已經在開始研究ECC了,但是關於Java實現ECC算法的資料實在是太少了,無論是國內還是國外的資料,無論是官方還是非官方的解釋,最終只有一種答案——ECC算法在jdk1.5后加入支持,目前僅僅只能完成密鑰的生成與解析。 如果想要獲得ECC算法實現,需要調用硬件完成加密/解密(ECC算法相當耗費資源,如果單純使用CPU進行加密/解密,效率低下),涉及到Java Card領域,PKCS#11。 其實,PKCS#11配置很簡單,但缺乏硬件設備,無法嘗試! 

    盡管如此,我照舊提供相應的Java實現代碼,以供大家參考。 

通過java代碼實現如下:Coder類見 Java加密技術(一) 

Java代碼    收藏代碼
  1. import java.math.BigInteger;  
  2. import java.security.Key;  
  3. import java.security.KeyFactory;  
  4. import java.security.interfaces.ECPrivateKey;  
  5. import java.security.interfaces.ECPublicKey;  
  6. import java.security.spec.ECFieldF2m;  
  7. import java.security.spec.ECParameterSpec;  
  8. import java.security.spec.ECPoint;  
  9. import java.security.spec.ECPrivateKeySpec;  
  10. import java.security.spec.ECPublicKeySpec;  
  11. import java.security.spec.EllipticCurve;  
  12. import java.security.spec.PKCS8EncodedKeySpec;  
  13. import java.security.spec.X509EncodedKeySpec;  
  14. import java.util.HashMap;  
  15. import java.util.Map;  
  16.   
  17. import javax.crypto.Cipher;  
  18. import javax.crypto.NullCipher;  
  19.   
  20. import sun.security.ec.ECKeyFactory;  
  21. import sun.security.ec.ECPrivateKeyImpl;  
  22. import sun.security.ec.ECPublicKeyImpl;  
  23.   
  24. /** 
  25.  * ECC安全編碼組件 
  26.  *  
  27.  * @author 梁棟 
  28.  * @version 1.0 
  29.  * @since 1.0 
  30.  */  
  31. public abstract class ECCCoder extends Coder {  
  32.   
  33.     public static final String ALGORITHM = "EC";  
  34.     private static final String PUBLIC_KEY = "ECCPublicKey";  
  35.     private static final String PRIVATE_KEY = "ECCPrivateKey";  
  36.   
  37.     /** 
  38.      * 解密<br> 
  39.      * 用私鑰解密 
  40.      *  
  41.      * @param data 
  42.      * @param key 
  43.      * @return 
  44.      * @throws Exception 
  45.      */  
  46.     public static byte[] decrypt(byte[] data, String key) throws Exception {  
  47.         // 對密鑰解密  
  48.         byte[] keyBytes = decryptBASE64(key);  
  49.   
  50.         // 取得私鑰  
  51.         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
  52.         KeyFactory keyFactory = ECKeyFactory.INSTANCE;  
  53.   
  54.         ECPrivateKey priKey = (ECPrivateKey) keyFactory  
  55.                 .generatePrivate(pkcs8KeySpec);  
  56.   
  57.         ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(),  
  58.                 priKey.getParams());  
  59.   
  60.         // 對數據解密  
  61.         // TODO Chipher不支持EC算法 未能實現  
  62.         Cipher cipher = new NullCipher();  
  63.         // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());  
  64.         cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());  
  65.   
  66.         return cipher.doFinal(data);  
  67.     }  
  68.   
  69.     /** 
  70.      * 加密<br> 
  71.      * 用公鑰加密 
  72.      *  
  73.      * @param data 
  74.      * @param privateKey 
  75.      * @return 
  76.      * @throws Exception 
  77.      */  
  78.     public static byte[] encrypt(byte[] data, String privateKey)  
  79.             throws Exception {  
  80.         // 對公鑰解密  
  81.         byte[] keyBytes = decryptBASE64(privateKey);  
  82.   
  83.         // 取得公鑰  
  84.         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
  85.         KeyFactory keyFactory = ECKeyFactory.INSTANCE;  
  86.   
  87.         ECPublicKey pubKey = (ECPublicKey) keyFactory  
  88.                 .generatePublic(x509KeySpec);  
  89.   
  90.         ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(),  
  91.                 pubKey.getParams());  
  92.   
  93.         // 對數據加密  
  94.         // TODO Chipher不支持EC算法 未能實現  
  95.         Cipher cipher = new NullCipher();  
  96.         // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());  
  97.         cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());  
  98.   
  99.         return cipher.doFinal(data);  
  100.     }  
  101.   
  102.     /** 
  103.      * 取得私鑰 
  104.      *  
  105.      * @param keyMap 
  106.      * @return 
  107.      * @throws Exception 
  108.      */  
  109.     public static String getPrivateKey(Map<String, Object> keyMap)  
  110.             throws Exception {  
  111.         Key key = (Key) keyMap.get(PRIVATE_KEY);  
  112.   
  113.         return encryptBASE64(key.getEncoded());  
  114.     }  
  115.   
  116.     /** 
  117.      * 取得公鑰 
  118.      *  
  119.      * @param keyMap 
  120.      * @return 
  121.      * @throws Exception 
  122.      */  
  123.     public static String getPublicKey(Map<String, Object> keyMap)  
  124.             throws Exception {  
  125.         Key key = (Key) keyMap.get(PUBLIC_KEY);  
  126.   
  127.         return encryptBASE64(key.getEncoded());  
  128.     }  
  129.   
  130.     /** 
  131.      * 初始化密鑰 
  132.      *  
  133.      * @return 
  134.      * @throws Exception 
  135.      */  
  136.     public static Map<String, Object> initKey() throws Exception {  
  137.         BigInteger x1 = new BigInteger(  
  138.                 "2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8"16);  
  139.         BigInteger x2 = new BigInteger(  
  140.                 "289070fb05d38ff58321f2e800536d538ccdaa3d9"16);  
  141.   
  142.         ECPoint g = new ECPoint(x1, x2);  
  143.   
  144.         // the order of generator  
  145.         BigInteger n = new BigInteger(  
  146.                 "5846006549323611672814741753598448348329118574063"10);  
  147.         // the cofactor  
  148.         int h = 2;  
  149.         int m = 163;  
  150.         int[] ks = { 763 };  
  151.         ECFieldF2m ecField = new ECFieldF2m(m, ks);  
  152.         // y^2+xy=x^3+x^2+1  
  153.         BigInteger a = new BigInteger("1"2);  
  154.         BigInteger b = new BigInteger("1"2);  
  155.   
  156.         EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);  
  157.   
  158.         ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,  
  159.                 n, h);  
  160.         // 公鑰  
  161.         ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);  
  162.   
  163.         BigInteger s = new BigInteger(  
  164.                 "1234006549323611672814741753598448348329118574063"10);  
  165.         // 私鑰  
  166.         ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);  
  167.   
  168.         Map<String, Object> keyMap = new HashMap<String, Object>(2);  
  169.   
  170.         keyMap.put(PUBLIC_KEY, publicKey);  
  171.         keyMap.put(PRIVATE_KEY, privateKey);  
  172.   
  173.         return keyMap;  
  174.     }  
  175.   
  176. }  



    請注意上述代碼中的TODO內容,再次提醒注意,Chipher不支持EC算法 ,以上代碼僅供參考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。為了確保程序能夠正常執行,我們使用了NullCipher類,驗證程序。 

照舊提供一個測試類: 

Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import java.math.BigInteger;  
  4. import java.security.spec.ECFieldF2m;  
  5. import java.security.spec.ECParameterSpec;  
  6. import java.security.spec.ECPoint;  
  7. import java.security.spec.ECPrivateKeySpec;  
  8. import java.security.spec.ECPublicKeySpec;  
  9. import java.security.spec.EllipticCurve;  
  10. import java.util.Map;  
  11.   
  12. import org.junit.Test;  
  13.   
  14. /** 
  15.  *  
  16.  * @author 梁棟 
  17.  * @version 1.0 
  18.  * @since 1.0 
  19.  */  
  20. public class ECCCoderTest {  
  21.   
  22.     @Test  
  23.     public void test() throws Exception {  
  24.         String inputStr = "abc";  
  25.         byte[] data = inputStr.getBytes();  
  26.   
  27.         Map<String, Object> keyMap = ECCCoder.initKey();  
  28.   
  29.         String publicKey = ECCCoder.getPublicKey(keyMap);  
  30.         String privateKey = ECCCoder.getPrivateKey(keyMap);  
  31.         System.err.println("公鑰: \n" + publicKey);  
  32.         System.err.println("私鑰: \n" + privateKey);  
  33.   
  34.         byte[] encodedData = ECCCoder.encrypt(data, publicKey);  
  35.   
  36.         byte[] decodedData = ECCCoder.decrypt(encodedData, privateKey);  
  37.   
  38.         String outputStr = new String(decodedData);  
  39.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  40.         assertEquals(inputStr, outputStr);  
  41.     }  
  42. }  



控制台輸出: 

Console代碼    收藏代碼
    1. 公鑰:   
    2. MEAwEAYHKoZIzj0CAQYFK4EEAAEDLAAEAv4TwFN7vBGsqgfXk95ObV5clO7oAokHD7BdOP9YMh8u  
    3. gAU21TjM2qPZ  
    4.   
    5. 私鑰:   
    6. MDICAQAwEAYHKoZIzj0CAQYFK4EEAAEEGzAZAgEBBBTYJsR3BN7TFw7JHcAHFkwNmfil7w==  
    7.   
    8. 加密前: abc  
    9.   
    10. 解密后: abc  
    本篇的主要內容為Java證書體系的實現。  

請大家在閱讀本篇內容時先閱讀 Java加密技術(四),預先了解RSA加密算法。  

在構建Java代碼實現前,我們需要完成證書的制作。 
1.生成keyStroe文件 
在命令行下執行以下命令: 
Shell代碼    收藏代碼
  1. keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore  


其中 
-genkey表示生成密鑰 
-validity指定證書有效期,這里是 36000天 
-alias指定別名,這里是 www.zlex.org 
-keyalg指定算法,這里是 RSA 
-keystore指定存儲位置,這里是 d:\zlex.keystore 

在這里我使用的密碼為  123456 

控制台輸出: 
Console代碼    收藏代碼
  1. 輸入keystore密碼:  
  2. 再次輸入新密碼:  
  3. 您的名字與姓氏是什么?  
  4.   [Unknown]:  www.zlex.org  
  5. 您的組織單位名稱是什么?  
  6.   [Unknown]:  zlex  
  7. 您的組織名稱是什么?  
  8.   [Unknown]:  zlex  
  9. 您所在的城市或區域名稱是什么?  
  10.   [Unknown]:  BJ  
  11. 您所在的州或省份名稱是什么?  
  12.   [Unknown]:  BJ  
  13. 該單位的兩字母國家代碼是什么  
  14.   [Unknown]:  CN  
  15. CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正確嗎?  
  16.   [否]:  Y  
  17.   
  18. 輸入<tomcat>的主密碼  
  19.         (如果和 keystore 密碼相同,按回車):  
  20. 再次輸入新密碼:  

這時,在D盤下會生成一個zlex.keystore的文件。 

2.生成自簽名證書 
光有keyStore文件是不夠的,還需要證書文件,證書才是直接提供給外界使用的公鑰憑證。 
導出證書: 
Shell代碼    收藏代碼
  1. keytool -export -keystore d:\zlex.keystore -alias www.zlex.org -file d:\zlex.cer -rfc  


其中 
-export指定為導出操作 
-keystore指定 keystore文件 
-alias指定導出 keystore文件中的別名 
-file指向 導出路徑 
-rfc以文本格式輸出,也就是以 BASE64編碼輸出 
這里的密碼是  123456 

控制台輸出: 
Console代碼    收藏代碼
  1. 輸入keystore密碼:  
  2. 保存在文件中的認證 <d:\zlex.cer>  


當然,使用方是需要導入證書的! 
可以通過自簽名證書完成CAS單點登錄系統的構建!  

Ok,准備工作完成,開始Java實現! 

通過java代碼實現如下: Coder類見 Java加密技術(一) 
Java代碼    收藏代碼
  1. import java.io.FileInputStream;  
  2. import java.security.KeyStore;  
  3. import java.security.PrivateKey;  
  4. import java.security.PublicKey;  
  5. import java.security.Signature;  
  6. import java.security.cert.Certificate;  
  7. import java.security.cert.CertificateFactory;  
  8. import java.security.cert.X509Certificate;  
  9. import java.util.Date;  
  10.   
  11. import javax.crypto.Cipher;  
  12.   
  13. /** 
  14.  * 證書組件 
  15.  *  
  16.  * @author 梁棟 
  17.  * @version 1.0 
  18.  * @since 1.0 
  19.  */  
  20. public abstract class CertificateCoder extends Coder {  
  21.   
  22.   
  23.     /** 
  24.      * Java密鑰庫(Java Key Store,JKS)KEY_STORE 
  25.      */  
  26.     public static final String KEY_STORE = "JKS";  
  27.   
  28.     public static final String X509 = "X.509";  
  29.   
  30.     /** 
  31.      * 由KeyStore獲得私鑰 
  32.      *  
  33.      * @param keyStorePath 
  34.      * @param alias 
  35.      * @param password 
  36.      * @return 
  37.      * @throws Exception 
  38.      */  
  39.     private static PrivateKey getPrivateKey(String keyStorePath, String alias,  
  40.             String password) throws Exception {  
  41.         KeyStore ks = getKeyStore(keyStorePath, password);  
  42.         PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());  
  43.         return key;  
  44.     }  
  45.   
  46.     /** 
  47.      * 由Certificate獲得公鑰 
  48.      *  
  49.      * @param certificatePath 
  50.      * @return 
  51.      * @throws Exception 
  52.      */  
  53.     private static PublicKey getPublicKey(String certificatePath)  
  54.             throws Exception {  
  55.         Certificate certificate = getCertificate(certificatePath);  
  56.         PublicKey key = certificate.getPublicKey();  
  57.         return key;  
  58.     }  
  59.   
  60.     /** 
  61.      * 獲得Certificate 
  62.      *  
  63.      * @param certificatePath 
  64.      * @return 
  65.      * @throws Exception 
  66.      */  
  67.     private static Certificate getCertificate(String certificatePath)  
  68.             throws Exception {  
  69.         CertificateFactory certificateFactory = CertificateFactory  
  70.                 .getInstance(X509);  
  71.         FileInputStream in = new FileInputStream(certificatePath);  
  72.   
  73.         Certificate certificate = certificateFactory.generateCertificate(in);  
  74.         in.close();  
  75.   
  76.         return certificate;  
  77.     }  
  78.   
  79.     /** 
  80.      * 獲得Certificate 
  81.      *  
  82.      * @param keyStorePath 
  83.      * @param alias 
  84.      * @param password 
  85.      * @return 
  86.      * @throws Exception 
  87.      */  
  88.     private static Certificate getCertificate(String keyStorePath,  
  89.             String alias, String password) throws Exception {  
  90.         KeyStore ks = getKeyStore(keyStorePath, password);  
  91.         Certificate certificate = ks.getCertificate(alias);  
  92.   
  93.         return certificate;  
  94.     }  
  95.   
  96.     /** 
  97.      * 獲得KeyStore 
  98.      *  
  99.      * @param keyStorePath 
  100.      * @param password 
  101.      * @return 
  102.      * @throws Exception 
  103.      */  
  104.     private static KeyStore getKeyStore(String keyStorePath, String password)  
  105.             throws Exception {  
  106.         FileInputStream is = new FileInputStream(keyStorePath);  
  107.         KeyStore ks = KeyStore.getInstance(KEY_STORE);  
  108.         ks.load(is, password.toCharArray());  
  109.         is.close();  
  110.         return ks;  
  111.     }  
  112.   
  113.     /** 
  114.      * 私鑰加密 
  115.      *  
  116.      * @param data 
  117.      * @param keyStorePath 
  118.      * @param alias 
  119.      * @param password 
  120.      * @return 
  121.      * @throws Exception 
  122.      */  
  123.     public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
  124.             String alias, String password) throws Exception {  
  125.         // 取得私鑰  
  126.         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
  127.   
  128.         // 對數據加密  
  129.         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
  130.         cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
  131.   
  132.         return cipher.doFinal(data);  
  133.   
  134.     }  
  135.   
  136.     /** 
  137.      * 私鑰解密 
  138.      *  
  139.      * @param data 
  140.      * @param keyStorePath 
  141.      * @param alias 
  142.      * @param password 
  143.      * @return 
  144.      * @throws Exception 
  145.      */  
  146.     public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
  147.             String alias, String password) throws Exception {  
  148.         // 取得私鑰  
  149.         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
  150.   
  151.         // 對數據加密  
  152.         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
  153.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  154.   
  155.         return cipher.doFinal(data);  
  156.   
  157.     }  
  158.   
  159.     /** 
  160.      * 公鑰加密 
  161.      *  
  162.      * @param data 
  163.      * @param certificatePath 
  164.      * @return 
  165.      * @throws Exception 
  166.      */  
  167.     public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
  168.             throws Exception {  
  169.   
  170.         // 取得公鑰  
  171.         PublicKey publicKey = getPublicKey(certificatePath);  
  172.         // 對數據加密  
  173.         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
  174.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  175.   
  176.         return cipher.doFinal(data);  
  177.   
  178.     }  
  179.   
  180.     /** 
  181.      * 公鑰解密 
  182.      *  
  183.      * @param data 
  184.      * @param certificatePath 
  185.      * @return 
  186.      * @throws Exception 
  187.      */  
  188.     public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
  189.             throws Exception {  
  190.         // 取得公鑰  
  191.         PublicKey publicKey = getPublicKey(certificatePath);  
  192.   
  193.         // 對數據加密  
  194.         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
  195.         cipher.init(Cipher.DECRYPT_MODE, publicKey);  
  196.   
  197.         return cipher.doFinal(data);  
  198.   
  199.     }  
  200.   
  201.     /** 
  202.      * 驗證Certificate 
  203.      *  
  204.      * @param certificatePath 
  205.      * @return 
  206.      */  
  207.     public static boolean verifyCertificate(String certificatePath) {  
  208.         return verifyCertificate(new Date(), certificatePath);  
  209.     }  
  210.   
  211.     /** 
  212.      * 驗證Certificate是否過期或無效 
  213.      *  
  214.      * @param date 
  215.      * @param certificatePath 
  216.      * @return 
  217.      */  
  218.     public static boolean verifyCertificate(Date date, String certificatePath) {  
  219.         boolean status = true;  
  220.         try {  
  221.             // 取得證書  
  222.             Certificate certificate = getCertificate(certificatePath);  
  223.             // 驗證證書是否過期或無效  
  224.             status = verifyCertificate(date, certificate);  
  225.         } catch (Exception e) {  
  226.             status = false;  
  227.         }  
  228.         return status;  
  229.     }  
  230.   
  231.     /** 
  232.      * 驗證證書是否過期或無效 
  233.      *  
  234.      * @param date 
  235.      * @param certificate 
  236.      * @return 
  237.      */  
  238.     private static boolean verifyCertificate(Date date, Certificate certificate) {  
  239.         boolean status = true;  
  240.         try {  
  241.             X509Certificate x509Certificate = (X509Certificate) certificate;  
  242.             x509Certificate.checkValidity(date);  
  243.         } catch (Exception e) {  
  244.             status = false;  
  245.         }  
  246.         return status;  
  247.     }  
  248.   
  249.     /** 
  250.      * 簽名 
  251.      *  
  252.      * @param keyStorePath 
  253.      * @param alias 
  254.      * @param password 
  255.      *  
  256.      * @return 
  257.      * @throws Exception 
  258.      */  
  259.     public static String sign(byte[] sign, String keyStorePath, String alias,  
  260.             String password) throws Exception {  
  261.         // 獲得證書  
  262.         X509Certificate x509Certificate = (X509Certificate) getCertificate(  
  263.                 keyStorePath, alias, password);  
  264.         // 獲取私鑰  
  265.         KeyStore ks = getKeyStore(keyStorePath, password);  
  266.         // 取得私鑰  
  267.         PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password  
  268.                 .toCharArray());  
  269.   
  270.         // 構建簽名  
  271.         Signature signature = Signature.getInstance(x509Certificate  
  272.                 .getSigAlgName());  
  273.         signature.initSign(privateKey);  
  274.         signature.update(sign);  
  275.         return encryptBASE64(signature.sign());  
  276.     }  
  277.   
  278.     /** 
  279.      * 驗證簽名 
  280.      *  
  281.      * @param data 
  282.      * @param sign 
  283.      * @param certificatePath 
  284.      * @return 
  285.      * @throws Exception 
  286.      */  
  287.     public static boolean verify(byte[] data, String sign,  
  288.             String certificatePath) throws Exception {  
  289.         // 獲得證書  
  290.         X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
  291.         // 獲得公鑰  
  292.         PublicKey publicKey = x509Certificate.getPublicKey();  
  293.         // 構建簽名  
  294.         Signature signature = Signature.getInstance(x509Certificate  
  295.                 .getSigAlgName());  
  296.         signature.initVerify(publicKey);  
  297.         signature.update(data);  
  298.   
  299.         return signature.verify(decryptBASE64(sign));  
  300.   
  301.     }  
  302.   
  303.     /** 
  304.      * 驗證Certificate 
  305.      *  
  306.      * @param keyStorePath 
  307.      * @param alias 
  308.      * @param password 
  309.      * @return 
  310.      */  
  311.     public static boolean verifyCertificate(Date date, String keyStorePath,  
  312.             String alias, String password) {  
  313.         boolean status = true;  
  314.         try {  
  315.             Certificate certificate = getCertificate(keyStorePath, alias,  
  316.                     password);  
  317.             status = verifyCertificate(date, certificate);  
  318.         } catch (Exception e) {  
  319.             status = false;  
  320.         }  
  321.         return status;  
  322.     }  
  323.   
  324.     /** 
  325.      * 驗證Certificate 
  326.      *  
  327.      * @param keyStorePath 
  328.      * @param alias 
  329.      * @param password 
  330.      * @return 
  331.      */  
  332.     public static boolean verifyCertificate(String keyStorePath, String alias,  
  333.             String password) {  
  334.         return verifyCertificate(new Date(), keyStorePath, alias, password);  
  335.     }  
  336. }  


再給出一個測試類: 
Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import org.junit.Test;  
  4.   
  5. /** 
  6.  *  
  7.  * @author 梁棟 
  8.  * @version 1.0 
  9.  * @since 1.0 
  10.  */  
  11. public class CertificateCoderTest {  
  12.     private String password = "123456";  
  13.     private String alias = "www.zlex.org";  
  14.     private String certificatePath = "d:/zlex.cer";  
  15.     private String keyStorePath = "d:/zlex.keystore";  
  16.   
  17.     @Test  
  18.     public void test() throws Exception {  
  19.         System.err.println("公鑰加密——私鑰解密");  
  20.         String inputStr = "Ceritifcate";  
  21.         byte[] data = inputStr.getBytes();  
  22.   
  23.         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
  24.                 certificatePath);  
  25.   
  26.         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
  27.                 keyStorePath, alias, password);  
  28.         String outputStr = new String(decrypt);  
  29.   
  30.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  31.   
  32.         // 驗證數據一致  
  33.         assertArrayEquals(data, decrypt);  
  34.   
  35.         // 驗證證書有效  
  36.         assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
  37.   
  38.     }  
  39.   
  40.     @Test  
  41.     public void testSign() throws Exception {  
  42.         System.err.println("私鑰加密——公鑰解密");  
  43.   
  44.         String inputStr = "sign";  
  45.         byte[] data = inputStr.getBytes();  
  46.   
  47.         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
  48.                 keyStorePath, alias, password);  
  49.   
  50.         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
  51.                 certificatePath);  
  52.   
  53.         String outputStr = new String(decodedData);  
  54.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  55.         assertEquals(inputStr, outputStr);  
  56.   
  57.         System.err.println("私鑰簽名——公鑰驗證簽名");  
  58.         // 產生簽名  
  59.         String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
  60.                 password);  
  61.         System.err.println("簽名:\r" + sign);  
  62.   
  63.         // 驗證簽名  
  64.         boolean status = CertificateCoder.verify(encodedData, sign,  
  65.                 certificatePath);  
  66.         System.err.println("狀態:\r" + status);  
  67.         assertTrue(status);  
  68.   
  69.     }  
  70. }  


控制台輸出: 
Console代碼    收藏代碼
  1. 公鑰加密——私鑰解密  
  2. 加密前: Ceritificate  
  3.   
  4. 解密后: Ceritificate  
  5.   
  6. 私鑰加密——公鑰解密  
  7. 加密前: sign  
  8.   
  9. 解密后: sign  
  10. 私鑰簽名——公鑰驗證簽名  
  11. 簽名:  
  12. pqBn5m6PJlfOjH0A6U2o2mUmBsfgyEY1NWCbiyA/I5Gc3gaVNVIdj/zkGNZRqTjhf3+J9a9z9EI7  
  13. 6F2eWYd7punHx5oh6hfNgcKbVb52EfItl4QEN+djbXiPynn07+Lbg1NOjULnpEd6ZhLP1YwrEAuM  
  14. OfvX0e7/wplxLbySaKQ=  
  15.   
  16. 狀態:  
  17. true  


由此完成了證書驗證體系!  

同樣,我們可以對代碼做簽名——代碼簽名!  
通過工具JarSigner可以完成代碼簽名。 
這里我們對tools.jar做代碼簽名,命令如下: 
Shell代碼    收藏代碼
  1. jarsigner -storetype jks -keystore zlex.keystore -verbose tools.jar www.zlex.org  

控制台輸出: 
Console代碼    收藏代碼
  1. 輸入密鑰庫的口令短語:  
  2.  正在更新: META-INF/WWW_ZLEX.SF  
  3.  正在更新: META-INF/WWW_ZLEX.RSA  
  4.   正在簽名: org/zlex/security/Security.class  
  5.   正在簽名: org/zlex/tool/Main$1.class  
  6.   正在簽名: org/zlex/tool/Main$2.class  
  7.   正在簽名: org/zlex/tool/Main.class  
  8.   
  9. 警告:  
  10. 簽名者證書將在六個月內過期。  


此時,我們可以對簽名后的jar做驗證!  
驗證tools.jar,命令如下: 
Shell代碼    收藏代碼
  1. jarsigner -verify -verbose -certs tools.jar  

控制台輸出: 
Console代碼    收藏代碼
  1.          402 Sat Jun 20 16:25:14 CST 2009 META-INF/MANIFEST.MF  
  2.          532 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.SF  
  3.          889 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.RSA  
  4. sm       590 Wed Dec 10 13:03:42 CST 2008 org/zlex/security/Security.class  
  5.   
  6.       X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
  7.       [證書將在 09-9-18 下午3:27 到期]  
  8.   
  9. sm       705 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$1.class  
  10.   
  11.       X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
  12.       [證書將在 09-9-18 下午3:27 到期]  
  13.   
  14. sm       779 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$2.class  
  15.   
  16.       X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
  17.       [證書將在 09-9-18 下午3:27 到期]  
  18.   
  19. sm     12672 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main.class  
  20.   
  21.       X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
  22.       [證書將在 09-9-18 下午3:27 到期]  
  23.   
  24.   
  25.   s = 已驗證簽名  
  26.   m = 在清單中列出條目  
  27.   k = 在密鑰庫中至少找到了一個證書  
  28.   i = 在身份作用域內至少找到了一個證書  
  29.   
  30. jar 已驗證。  
  31.   
  32. 警告:  
  33. 此 jar 包含簽名者證書將在六個月內過期的條目。  


代碼簽名認證的用途主要是對發布的軟件做驗證,支持 Sun Java .jar (Java Applet) 文件(J2SE)和 J2ME MIDlet Suite 文件。 
    在 Java加密技術(八)中,我們模擬了一個基於RSA非對稱加密網絡的安全通信。現在我們深度了解一下現有的安全網絡通信——SSL。 
    我們需要構建一個由CA機構簽發的有效證書,這里我們使用上文中生成的自簽名證書 zlex.cer 
    這里,我們將證書導入到我們的密鑰庫。 

Shell代碼    收藏代碼
  1. keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore  


其中 
-import表示 導入 
-alias指定別名,這里是 www.zlex.org 
-file指定算法,這里是 d:/zlex.cer 
-keystore指定存儲位置,這里是 d:/zlex.keystore 
在這里我使用的密碼為 654321 

控制台輸出: 
Console代碼    收藏代碼
  1. 輸入keystore密碼:  
  2. 再次輸入新密碼:  
  3. 所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
  4. 簽發人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
  5. 序列號:4a1e48df  
  6. 有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009  
  7. 證書指紋:  
  8.          MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A  
  9.          SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4  
  10.          簽名算法名稱:SHA1withRSA  
  11.          版本: 3  
  12. 信任這個認證? [否]:  y  
  13. 認證已添加至keystore中  


OK,最復雜的准備工作已經完成。 
接下來我們將域名 www.zlex.org定位到本機上。打開 C:\Windows\System32\drivers\etc\hosts文件,將 www.zlex.org綁定在本機上。在文件末尾追加 127.0.0.1       www.zlex.org。現在通過地址欄訪問 http://www.zlex.org,或者通過 ping命令,如果能夠定位到本機,域名映射就搞定了。 
現在,配置tomcat。先將 zlex.keystore拷貝到tomcat的conf目錄下,然后配置 server.xml。將如下內容加入配置文件
Xml代碼    收藏代碼
  1. <Connector  
  2.     SSLEnabled="true"  
  3.     URIEncoding="UTF-8"  
  4.     clientAuth="false"  
  5.     keystoreFile="conf/zlex.keystore"  
  6.     keystorePass="123456"  
  7.     maxThreads="150"  
  8.     port="443"  
  9.     protocol="HTTP/1.1"  
  10.     scheme="https"  
  11.     secure="true"  
  12.     sslProtocol="TLS" />  

注意 clientAuth="false"測試階段,置為 false,正式使用時建議使用 true。現在啟動tomcat,訪問 https://www.zlex.org/ 
顯然,證書未能通過認證,這個時候你可以選擇安裝證書(上文中的 zlex.cer文件就是證書),作為 受信任的根證書頒發機構導入,再次重啟瀏覽器(IE,其他瀏覽器對於域名www.zlex.org不支持本地方式訪問),訪問 https://www.zlex.org/,你會看到地址欄中會有個小鎖 ,就說明安裝成功。所有的瀏覽器聯網操作已經在RSA加密解密系統的保護之下了。但似乎我們感受不到。 
這個時候很多人開始懷疑,如果我們要手工做一個這樣的https的訪問是不是需要把瀏覽器的這些個功能都實現呢?不需要! 

接着上篇內容,給出如下代碼實現: 
Java代碼    收藏代碼
  1. import java.io.FileInputStream;  
  2. import java.security.KeyStore;  
  3. import java.security.PrivateKey;  
  4. import java.security.PublicKey;  
  5. import java.security.Signature;  
  6. import java.security.cert.Certificate;  
  7. import java.security.cert.CertificateFactory;  
  8. import java.security.cert.X509Certificate;  
  9. import java.util.Date;  
  10.   
  11. import javax.crypto.Cipher;  
  12. import javax.net.ssl.HttpsURLConnection;  
  13. import javax.net.ssl.KeyManagerFactory;  
  14. import javax.net.ssl.SSLContext;  
  15. import javax.net.ssl.SSLSocketFactory;  
  16. import javax.net.ssl.TrustManagerFactory;  
  17.   
  18. /** 
  19.  * 證書組件 
  20.  *  
  21.  * @author 梁棟 
  22.  * @version 1.0 
  23.  * @since 1.0 
  24.  */  
  25. public abstract class CertificateCoder extends Coder {  
  26.   
  27.     /** 
  28.      * Java密鑰庫(Java Key Store,JKS)KEY_STORE 
  29.      */  
  30.     public static final String KEY_STORE = "JKS";  
  31.   
  32.     public static final String X509 = "X.509";  
  33.     public static final String SunX509 = "SunX509";  
  34.     public static final String SSL = "SSL";  
  35.   
  36.     /** 
  37.      * 由KeyStore獲得私鑰 
  38.      *  
  39.      * @param keyStorePath 
  40.      * @param alias 
  41.      * @param password 
  42.      * @return 
  43.      * @throws Exception 
  44.      */  
  45.     private static PrivateKey getPrivateKey(String keyStorePath, String alias,  
  46.             String password) throws Exception {  
  47.         KeyStore ks = getKeyStore(keyStorePath, password);  
  48.         PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());  
  49.         return key;  
  50.     }  
  51.   
  52.     /** 
  53.      * 由Certificate獲得公鑰 
  54.      *  
  55.      * @param certificatePath 
  56.      * @return 
  57.      * @throws Exception 
  58.      */  
  59.     private static PublicKey getPublicKey(String certificatePath)  
  60.             throws Exception {  
  61.         Certificate certificate = getCertificate(certificatePath);  
  62.         PublicKey key = certificate.getPublicKey();  
  63.         return key;  
  64.     }  
  65.   
  66.     /** 
  67.      * 獲得Certificate 
  68.      *  
  69.      * @param certificatePath 
  70.      * @return 
  71.      * @throws Exception 
  72.      */  
  73.     private static Certificate getCertificate(String certificatePath)  
  74.             throws Exception {  
  75.         CertificateFactory certificateFactory = CertificateFactory  
  76.                 .getInstance(X509);  
  77.         FileInputStream in = new FileInputStream(certificatePath);  
  78.   
  79.         Certificate certificate = certificateFactory.generateCertificate(in);  
  80.         in.close();  
  81.   
  82.         return certificate;  
  83.     }  
  84.   
  85.     /** 
  86.      * 獲得Certificate 
  87.      *  
  88.      * @param keyStorePath 
  89.      * @param alias 
  90.      * @param password 
  91.      * @return 
  92.      * @throws Exception 
  93.      */  
  94.     private static Certificate getCertificate(String keyStorePath,  
  95.             String alias, String password) throws Exception {  
  96.         KeyStore ks = getKeyStore(keyStorePath, password);  
  97.         Certificate certificate = ks.getCertificate(alias);  
  98.   
  99.         return certificate;  
  100.     }  
  101.   
  102.     /** 
  103.      * 獲得KeyStore 
  104.      *  
  105.      * @param keyStorePath 
  106.      * @param password 
  107.      * @return 
  108.      * @throws Exception 
  109.      */  
  110.     private static KeyStore getKeyStore(String keyStorePath, String password)  
  111.             throws Exception {  
  112.         FileInputStream is = new FileInputStream(keyStorePath);  
  113.         KeyStore ks = KeyStore.getInstance(KEY_STORE);  
  114.         ks.load(is, password.toCharArray());  
  115.         is.close();  
  116.         return ks;  
  117.     }  
  118.   
  119.     /** 
  120.      * 私鑰加密 
  121.      *  
  122.      * @param data 
  123.      * @param keyStorePath 
  124.      * @param alias 
  125.      * @param password 
  126.      * @return 
  127.      * @throws Exception 
  128.      */  
  129.     public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
  130.             String alias, String password) throws Exception {  
  131.         // 取得私鑰  
  132.         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
  133.   
  134.         // 對數據加密  
  135.         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
  136.         cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
  137.   
  138.         return cipher.doFinal(data);  
  139.   
  140.     }  
  141.   
  142.     /** 
  143.      * 私鑰解密 
  144.      *  
  145.      * @param data 
  146.      * @param keyStorePath 
  147.      * @param alias 
  148.      * @param password 
  149.      * @return 
  150.      * @throws Exception 
  151.      */  
  152.     public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
  153.             String alias, String password) throws Exception {  
  154.         // 取得私鑰  
  155.         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
  156.   
  157.         // 對數據加密  
  158.         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
  159.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  160.   
  161.         return cipher.doFinal(data);  
  162.   
  163.     }  
  164.   
  165.     /** 
  166.      * 公鑰加密 
  167.      *  
  168.      * @param data 
  169.      * @param certificatePath 
  170.      * @return 
  171.      * @throws Exception 
  172.      */  
  173.     public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
  174.             throws Exception {  
  175.   
  176.         // 取得公鑰  
  177.         PublicKey publicKey = getPublicKey(certificatePath);  
  178.         // 對數據加密  
  179.         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
  180.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  181.   
  182.         return cipher.doFinal(data);  
  183.   
  184.     }  
  185.   
  186.     /** 
  187.      * 公鑰解密 
  188.      *  
  189.      * @param data 
  190.      * @param certificatePath 
  191.      * @return 
  192.      * @throws Exception 
  193.      */  
  194.     public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
  195.             throws Exception {  
  196.         // 取得公鑰  
  197.         PublicKey publicKey = getPublicKey(certificatePath);  
  198.   
  199.         // 對數據加密  
  200.         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
  201.         cipher.init(Cipher.DECRYPT_MODE, publicKey);  
  202.   
  203.         return cipher.doFinal(data);  
  204.   
  205.     }  
  206.   
  207.     /** 
  208.      * 驗證Certificate 
  209.      *  
  210.      * @param certificatePath 
  211.      * @return 
  212.      */  
  213.     public static boolean verifyCertificate(String certificatePath) {  
  214.         return verifyCertificate(new Date(), certificatePath);  
  215.     }  
  216.   
  217.     /** 
  218.      * 驗證Certificate是否過期或無效 
  219.      *  
  220.      * @param date 
  221.      * @param certificatePath 
  222.      * @return 
  223.      */  
  224.     public static boolean verifyCertificate(Date date, String certificatePath) {  
  225.         boolean status = true;  
  226.         try {  
  227.             // 取得證書  
  228.             Certificate certificate = getCertificate(certificatePath);  
  229.             // 驗證證書是否過期或無效  
  230.             status = verifyCertificate(date, certificate);  
  231.         } catch (Exception e) {  
  232.             status = false;  
  233.         }  
  234.         return status;  
  235.     }  
  236.   
  237.     /** 
  238.      * 驗證證書是否過期或無效 
  239.      *  
  240.      * @param date 
  241.      * @param certificate 
  242.      * @return 
  243.      */  
  244.     private static boolean verifyCertificate(Date date, Certificate certificate) {  
  245.         boolean status = true;  
  246.         try {  
  247.             X509Certificate x509Certificate = (X509Certificate) certificate;  
  248.             x509Certificate.checkValidity(date);  
  249.         } catch (Exception e) {  
  250.             status = false;  
  251.         }  
  252.         return status;  
  253.     }  
  254.   
  255.     /** 
  256.      * 簽名 
  257.      *  
  258.      * @param keyStorePath 
  259.      * @param alias 
  260.      * @param password 
  261.      *  
  262.      * @return 
  263.      * @throws Exception 
  264.      */  
  265.     public static String sign(byte[] sign, String keyStorePath, String alias,  
  266.             String password) throws Exception {  
  267.         // 獲得證書  
  268.         X509Certificate x509Certificate = (X509Certificate) getCertificate(  
  269.                 keyStorePath, alias, password);  
  270.         // 獲取私鑰  
  271.         KeyStore ks = getKeyStore(keyStorePath, password);  
  272.         // 取得私鑰  
  273.         PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password  
  274.                 .toCharArray());  
  275.   
  276.         // 構建簽名  
  277.         Signature signature = Signature.getInstance(x509Certificate  
  278.                 .getSigAlgName());  
  279.         signature.initSign(privateKey);  
  280.         signature.update(sign);  
  281.         return encryptBASE64(signature.sign());  
  282.     }  
  283.   
  284.     /** 
  285.      * 驗證簽名 
  286.      *  
  287.      * @param data 
  288.      * @param sign 
  289.      * @param certificatePath 
  290.      * @return 
  291.      * @throws Exception 
  292.      */  
  293.     public static boolean verify(byte[] data, String sign,  
  294.             String certificatePath) throws Exception {  
  295.         // 獲得證書  
  296.         X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
  297.         // 獲得公鑰  
  298.         PublicKey publicKey = x509Certificate.getPublicKey();  
  299.         // 構建簽名  
  300.         Signature signature = Signature.getInstance(x509Certificate  
  301.                 .getSigAlgName());  
  302.         signature.initVerify(publicKey);  
  303.         signature.update(data);  
  304.   
  305.         return signature.verify(decryptBASE64(sign));  
  306.   
  307.     }  
  308.   
  309.     /** 
  310.      * 驗證Certificate 
  311.      *  
  312.      * @param keyStorePath 
  313.      * @param alias 
  314.      * @param password 
  315.      * @return 
  316.      */  
  317.     public static boolean verifyCertificate(Date date, String keyStorePath,  
  318.             String alias, String password) {  
  319.         boolean status = true;  
  320.         try {  
  321.             Certificate certificate = getCertificate(keyStorePath, alias,  
  322.                     password);  
  323.             status = verifyCertificate(date, certificate);  
  324.         } catch (Exception e) {  
  325.             status = false;  
  326.         }  
  327.         return status;  
  328.     }  
  329.   
  330.     /** 
  331.      * 驗證Certificate 
  332.      *  
  333.      * @param keyStorePath 
  334.      * @param alias 
  335.      * @param password 
  336.      * @return 
  337.      */  
  338.     public static boolean verifyCertificate(String keyStorePath, String alias,  
  339.             String password) {  
  340.         return verifyCertificate(new Date(), keyStorePath, alias, password);  
  341.     }  
  342.   
  343.     /** 
  344.      * 獲得SSLSocektFactory 
  345.      *  
  346.      * @param password 
  347.      *            密碼 
  348.      * @param keyStorePath 
  349.      *            密鑰庫路徑 
  350.      *  
  351.      * @param trustKeyStorePath 
  352.      *            信任庫路徑 
  353.      * @return 
  354.      * @throws Exception 
  355.      */  
  356.     private static SSLSocketFactory getSSLSocketFactory(String password,  
  357.             String keyStorePath, String trustKeyStorePath) throws Exception {  
  358.         // 初始化密鑰庫  
  359.         KeyManagerFactory keyManagerFactory = KeyManagerFactory  
  360.                 .getInstance(SunX509);  
  361.         KeyStore keyStore = getKeyStore(keyStorePath, password);  
  362.         keyManagerFactory.init(keyStore, password.toCharArray());  
  363.   
  364.         // 初始化信任庫  
  365.         TrustManagerFactory trustManagerFactory = TrustManagerFactory  
  366.                 .getInstance(SunX509);  
  367.         KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, password);  
  368.         trustManagerFactory.init(trustkeyStore);  
  369.   
  370.         // 初始化SSL上下文  
  371.         SSLContext ctx = SSLContext.getInstance(SSL);  
  372.         ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory  
  373.                 .getTrustManagers(), null);  
  374.         SSLSocketFactory sf = ctx.getSocketFactory();  
  375.   
  376.         return sf;  
  377.     }  
  378.   
  379.     /** 
  380.      * 為HttpsURLConnection配置SSLSocketFactory 
  381.      *  
  382.      * @param conn 
  383.      *            HttpsURLConnection 
  384.      * @param password 
  385.      *            密碼 
  386.      * @param keyStorePath 
  387.      *            密鑰庫路徑 
  388.      *  
  389.      * @param trustKeyStorePath 
  390.      *            信任庫路徑 
  391.      * @throws Exception 
  392.      */  
  393.     public static void configSSLSocketFactory(HttpsURLConnection conn,  
  394.             String password, String keyStorePath, String trustKeyStorePath)  
  395.             throws Exception {  
  396.         conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,  
  397.                 trustKeyStorePath));  
  398.     }  
  399. }  


增加了 configSSLSocketFactory方法供外界調用,該方法為HttpsURLConnection配置了SSLSocketFactory。當HttpsURLConnection配置了SSLSocketFactory后,我們就可以通過HttpsURLConnection的getInputStream、getOutputStream,像往常使用HttpURLConnection做操作了。尤其要說明一點,未配置SSLSocketFactory前,HttpsURLConnection的getContentLength()獲得值永遠都是 -1。 

給出相應測試類: 
Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import java.io.DataInputStream;  
  4. import java.io.InputStream;  
  5. import java.net.URL;  
  6.   
  7. import javax.net.ssl.HttpsURLConnection;  
  8.   
  9. import org.junit.Test;  
  10.   
  11. /** 
  12.  *  
  13.  * @author 梁棟 
  14.  * @version 1.0 
  15.  * @since 1.0 
  16.  */  
  17. public class CertificateCoderTest {  
  18.     private String password = "123456";  
  19.     private String alias = "www.zlex.org";  
  20.     private String certificatePath = "d:/zlex.cer";  
  21.     private String keyStorePath = "d:/zlex.keystore";  
  22.     private String clientKeyStorePath = "d:/zlex-client.keystore";  
  23.     private String clientPassword = "654321";  
  24.   
  25.     @Test  
  26.     public void test() throws Exception {  
  27.         System.err.println("公鑰加密——私鑰解密");  
  28.         String inputStr = "Ceritifcate";  
  29.         byte[] data = inputStr.getBytes();  
  30.   
  31.         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
  32.                 certificatePath);  
  33.   
  34.         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
  35.                 keyStorePath, alias, password);  
  36.         String outputStr = new String(decrypt);  
  37.   
  38.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  39.   
  40.         // 驗證數據一致  
  41.         assertArrayEquals(data, decrypt);  
  42.   
  43.         // 驗證證書有效  
  44.         assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
  45.   
  46.     }  
  47.   
  48.     @Test  
  49.     public void testSign() throws Exception {  
  50.         System.err.println("私鑰加密——公鑰解密");  
  51.   
  52.         String inputStr = "sign";  
  53.         byte[] data = inputStr.getBytes();  
  54.   
  55.         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
  56.                 keyStorePath, alias, password);  
  57.   
  58.         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
  59.                 certificatePath);  
  60.   
  61.         String outputStr = new String(decodedData);  
  62.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  63.         assertEquals(inputStr, outputStr);  
  64.   
  65.         System.err.println("私鑰簽名——公鑰驗證簽名");  
  66.         // 產生簽名  
  67.         String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
  68.                 password);  
  69.         System.err.println("簽名:\r" + sign);  
  70.   
  71.         // 驗證簽名  
  72.         boolean status = CertificateCoder.verify(encodedData, sign,  
  73.                 certificatePath);  
  74.         System.err.println("狀態:\r" + status);  
  75.         assertTrue(status);  
  76.   
  77.     }  
  78.   
  79.     @Test  
  80.     public void testHttps() throws Exception {  
  81.         URL url = new URL("https://www.zlex.org/examples/");  
  82.         HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();  
  83.   
  84.         conn.setDoInput(true);  
  85.         conn.setDoOutput(true);  
  86.   
  87.         CertificateCoder.configSSLSocketFactory(conn, clientPassword,  
  88.                 clientKeyStorePath, clientKeyStorePath);  
  89.   
  90.         InputStream is = conn.getInputStream();  
  91.   
  92.         int length = conn.getContentLength();  
  93.   
  94.         DataInputStream dis = new DataInputStream(is);  
  95.         byte[] data = new byte[length];  
  96.         dis.readFully(data);  
  97.   
  98.         dis.close();  
  99.         System.err.println(new String(data));  
  100.         conn.disconnect();  
  101.     }  
  102. }  

注意 testHttps方法,幾乎和我們往常做HTTP訪問沒有差別,我們來看控制台輸出: 
Console代碼    收藏代碼
  1. <!--  
  2.   Licensed to the Apache Software Foundation (ASF) under one or more  
  3.   contributor license agreements.  See the NOTICE file distributed with  
  4.   this work for additional information regarding copyright ownership.  
  5.   The ASF licenses this file to You under the Apache License, Version 2.0  
  6.   (the "License"); you may not use this file except in compliance with  
  7.   the License.  You may obtain a copy of the License at  
  8.   
  9.       http://www.apache.org/licenses/LICENSE-2.0  
  10.   
  11.   Unless required by applicable law or agreed to in writing, software  
  12.   distributed under the License is distributed on an "AS IS" BASIS,  
  13.   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  14.   See the License for the specific language governing permissions and  
  15.   limitations under the License.  
  16. -->  
  17. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  
  18. <HTML><HEAD><TITLE>Apache Tomcat Examples</TITLE>  
  19. <META http-equiv=Content-Type content="text/html">  
  20. </HEAD>  
  21. <BODY>  
  22. <P>  
  23. <H3>Apache Tomcat Examples</H3>  
  24. <P></P>  
  25. <ul>  
  26. <li><a href="servlets">Servlets examples</a></li>  
  27. <li><a href="jsp">JSP Examples</a></li>  
  28. </ul>  
  29. </BODY></HTML>  

通過瀏覽器直接訪問 https://www.zlex.org/examples/你也會獲得上述內容。也就是說應用甲方作為服務器構建tomcat服務,乙方可以通過上述方式訪問甲方受保護的SSL應用,並且不需要考慮具體的加密解密問題。甲乙雙方可以經過相應配置,通過雙方的tomcat配置有效的SSL服務,簡化上述代碼實現,完全通過證書配置完成SSL雙向認證!
    在 Java 加密技術(九)中,我們使用自簽名證書完成了認證。接下來,我們使用第三方CA簽名機構完成證書簽名。 
    這里我們使用 thawte提供的測試用21天免費ca證書。 
    1.要在該網站上注明你的域名,這里使用 www.zlex.org作為測試用域名( 請勿使用該域名作為你的域名地址,該域名受法律保護!請使用其他非注冊域名!)。 
    2.如果域名有效,你會收到郵件要求你訪問 https://www.thawte.com/cgi/server/try.exe獲得ca證書。 
    3.復述密鑰庫的創建。 
   
Shell代碼    收藏代碼
  1. keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore  


在這里我使用的密碼為  123456 

控制台輸出: 
Console代碼    收藏代碼
  1. 輸入keystore密碼:  
  2. 再次輸入新密碼:  
  3. 您的名字與姓氏是什么?  
  4.   [Unknown]:  www.zlex.org  
  5. 您的組織單位名稱是什么?  
  6.   [Unknown]:  zlex  
  7. 您的組織名稱是什么?  
  8.   [Unknown]:  zlex  
  9. 您所在的城市或區域名稱是什么?  
  10.   [Unknown]:  BJ  
  11. 您所在的州或省份名稱是什么?  
  12.   [Unknown]:  BJ  
  13. 該單位的兩字母國家代碼是什么  
  14.   [Unknown]:  CN  
  15. CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正確嗎?  
  16.   [否]:  Y  
  17.   
  18. 輸入<tomcat>的主密碼  
  19.         (如果和 keystore 密碼相同,按回車):  
  20. 再次輸入新密碼:  


    4.通過如下命令,從zlex.keystore中導出CA證書申請。 
   
Shell代碼    收藏代碼
  1. keytool -certreq -alias www.zlex.org -file d:\zlex.csr -keystore d:\zlex.keystore -v  
你會獲得zlex.csr文件,可以用記事本打開,內容如下格式:
Text代碼    收藏代碼
  1. -----BEGIN NEW CERTIFICATE REQUEST-----  
  2. MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMQswCQYDVQQHEwJCSjENMAsG  
  3. A1UEChMEemxleDENMAsGA1UECxMEemxleDEVMBMGA1UEAxMMd3d3LnpsZXgub3JnMIGfMA0GCSqG  
  4. SIb3DQEBAQUAA4GNADCBiQKBgQCR6DXU9Mp+mCKO7cv9JPsj0n1Ec/GpM09qvhpgX3FNad/ZWSDc  
  5. vU77YXZSoF9hQp3w1LC+eeKgd2MlVpXTvbVwBNVd2HiQPp37ic6BUUjSaX8LHtCl7l0BIEye9qQ2  
  6. j8G0kak7e8ZA0s7nb3Ymq/K8BV7v0MQIdhIc1bifK9ZDewIDAQABoAAwDQYJKoZIhvcNAQEFBQAD  
  7. gYEAMA1r2fbZPtNx37U9TRwadCH2TZZecwKJS/hskNm6ryPKIAp9APWwAyj8WJHRBz5SpZM4zmYO  
  8. oMCI8BcnY2A4JP+R7/SwXTdH/xcg7NVghd9A2SCgqMpF7KMfc5dE3iygdiPu+UhY200Dvpjx8gmJ  
  9. 1UbH3+nqMUyCrZgURFslOUY=  
  10. -----END NEW CERTIFICATE REQUEST-----  

    5.將上述文件內容拷貝到 https://www.thawte.com/cgi/server/try.exe中,點擊next,獲得回應內容,這里是p7b格式。 
內容如下:
Text代碼    收藏代碼
  1. -----BEGIN PKCS7-----  
  2. MIIF3AYJKoZIhvcNAQcCoIIFzTCCBckCAQExADALBgkqhkiG9w0BBwGgggWxMIID  
  3. EDCCAnmgAwIBAgIQA/mx/pKoaB+KGX2hveFU9zANBgkqhkiG9w0BAQUFADCBhzEL  
  4. MAkGA1UEBhMCWkExIjAgBgNVBAgTGUZPUiBURVNUSU5HIFBVUlBPU0VTIE9OTFkx  
  5. HTAbBgNVBAoTFFRoYXd0ZSBDZXJ0aWZpY2F0aW9uMRcwFQYDVQQLEw5URVNUIFRF  
  6. U1QgVEVTVDEcMBoGA1UEAxMTVGhhd3RlIFRlc3QgQ0EgUm9vdDAeFw0wOTA1Mjgw  
  7. MDIxMzlaFw0wOTA2MTgwMDIxMzlaMFwxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJC  
  8. SjELMAkGA1UEBxMCQkoxDTALBgNVBAoTBHpsZXgxDTALBgNVBAsTBHpsZXgxFTAT  
  9. BgNVBAMTDHd3dy56bGV4Lm9yZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA  
  10. keg11PTKfpgiju3L/ST7I9J9RHPxqTNPar4aYF9xTWnf2Vkg3L1O+2F2UqBfYUKd  
  11. 8NSwvnnioHdjJVaV0721cATVXdh4kD6d+4nOgVFI0ml/Cx7Qpe5dASBMnvakNo/B  
  12. tJGpO3vGQNLO5292JqvyvAVe79DECHYSHNW4nyvWQ3sCAwEAAaOBpjCBozAMBgNV  
  13. HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBABgNVHR8E  
  14. OTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNl  
  15. cnZlckNBLmNybDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v  
  16. Y3NwLnRoYXd0ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEATPuxZbtJJSPmXvfrr1yz  
  17. xqM06IwTZ6UU0lZRG7I0WufMjNMKdpn8hklUhE17mxAhGSpewLVVeLR7uzBLFkuC  
  18. X7wMXxhoYdJZtNai72izU6Rd1oknao7diahvRxPK4IuQ7y2oZ511/4T4vgY6iRAj  
  19. q4q76HhPJrVRL/sduaiu+gYwggKZMIICAqADAgECAgEAMA0GCSqGSIb3DQEBBAUA  
  20. MIGHMQswCQYDVQQGEwJaQTEiMCAGA1UECBMZRk9SIFRFU1RJTkcgUFVSUE9TRVMg  
  21. T05MWTEdMBsGA1UEChMUVGhhd3RlIENlcnRpZmljYXRpb24xFzAVBgNVBAsTDlRF  
  22. U1QgVEVTVCBURVNUMRwwGgYDVQQDExNUaGF3dGUgVGVzdCBDQSBSb290MB4XDTk2  
  23. MDgwMTAwMDAwMFoXDTIwMTIzMTIxNTk1OVowgYcxCzAJBgNVBAYTAlpBMSIwIAYD  
  24. VQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRUaGF3dGUg  
  25. Q2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAaBgNVBAMT  
  26. E1RoYXd0ZSBUZXN0IENBIFJvb3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB  
  27. ALV9kG+Os6x/DOhm+tKUQfzVMWGhE95sFmEtkMMTX2Zi4n6i6BvzoReJ5njzt1LF  
  28. cqu4EUk9Ji20egKKfmqRzmQFLP7+1niSdfJEUE7cKY40QoI99270PTrLjJeaMcCl  
  29. +AYl+kD+RL5BtuKKU3PurYcsCsre6aTvjMcqpTJOGeSPAgMBAAGjEzARMA8GA1Ud  
  30. EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAgozj7BkD9O8si2V0v+EZ/t7E  
  31. fz/LC8y6mD7IBUziHy5/53ymGAGLtyhXHvX+UIE6UWbHro3IqVkrmY5uC93Z2Wew  
  32. A/6edK3KFUcUikrLeewM7gmqsiASEKx2mKRKlu12jXyNS5tXrPWRDvUKtFC1uL9a  
  33. 12rFAQS2BkIk7aU+ghYxAA==  
  34. -----END PKCS7-----  
將其存儲為zlex.p7b 
    6.將由CA簽發的證書導入密鑰庫。 
   
Shell代碼    收藏代碼
  1. keytool -import -trustcacerts -alias www.zlex.org -file d:\zlex.p7b -keystore d:\zlex.keystore -v  


在這里我使用的密碼為  123456 

    控制台輸出: 
Console代碼    收藏代碼
  1. 輸入keystore密碼:  
  2.   
  3. 回復中的最高級認證:  
  4.   
  5. 所有者:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR  
  6.  TESTING PURPOSES ONLY, C=ZA  
  7. 簽發人:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR  
  8.  TESTING PURPOSES ONLY, C=ZA  
  9. 序列號:0  
  10. 有效期: Thu Aug 01 08:00:00 CST 1996 至Fri Jan 01 05:59:59 CST 2021  
  11. 證書指紋:  
  12.          MD5:5E:E0:0E:1D:17:B7:CA:A5:7D:36:D6:02:DF:4D:26:A4  
  13.          SHA1:39:C6:9D:27:AF:DC:EB:47:D6:33:36:6A:B2:05:F1:47:A9:B4:DA:EA  
  14.          簽名算法名稱:MD5withRSA  
  15.          版本: 3  
  16.   
  17. 擴展:  
  18.   
  19. #1: ObjectId: 2.5.29.19 Criticality=true  
  20. BasicConstraints:[  
  21.   CA:true  
  22.   PathLen:2147483647  
  23. ]  
  24.   
  25.   
  26. ... 是不可信的。 還是要安裝回復? [否]:  Y  
  27. 認證回復已安裝在 keystore中  
  28. [正在存儲 d:\zlex.keystore]  


    7.域名定位 
    將域名www.zlex.org定位到本機上。打開C:\Windows\System32\drivers\etc\hosts文件,將www.zlex.org綁定在本機上。在文件末尾追加127.0.0.1       www.zlex.org。現在通過地址欄訪問http://www.zlex.org,或者通過ping命令,如果能夠定位到本機,域名映射就搞定了。 

    8.配置server.xml 
Xml代碼    收藏代碼
  1. <Connector  
  2.     keystoreFile="conf/zlex.keystore"  
  3.     keystorePass="123456"   
  4.     truststoreFile="conf/zlex.keystore"      
  5.     truststorePass="123456"       
  6.     SSLEnabled="true"  
  7.     URIEncoding="UTF-8"  
  8.     clientAuth="false"            
  9.     maxThreads="150"  
  10.     port="443"  
  11.     protocol="HTTP/1.1"  
  12.     scheme="https"  
  13.     secure="true"  
  14.     sslProtocol="TLS" />  


將文件 zlex.keystore拷貝到tomcat的 conf目錄下,重新啟動tomcat。訪問 https://www.zlex.org/,我們發現聯網有些遲鈍。大約5秒鍾后,網頁正常顯示,同時有如下圖所示: 

 
瀏覽器驗證了該CA機構的有效性。 

打開證書,如下圖所示: 

 

調整測試類: 
Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import java.io.DataInputStream;  
  4. import java.io.InputStream;  
  5. import java.net.URL;  
  6.   
  7. import javax.net.ssl.HttpsURLConnection;  
  8.   
  9. import org.junit.Test;  
  10.   
  11. /** 
  12.  *  
  13.  * @author 梁棟 
  14.  * @version 1.0 
  15.  * @since 1.0 
  16.  */  
  17. public class CertificateCoderTest {  
  18.     private String password = "123456";  
  19.     private String alias = "www.zlex.org";  
  20.     private String certificatePath = "d:/zlex.cer";  
  21.     private String keyStorePath = "d:/zlex.keystore";  
  22.   
  23.     @Test  
  24.     public void test() throws Exception {  
  25.         System.err.println("公鑰加密——私鑰解密");  
  26.         String inputStr = "Ceritifcate";  
  27.         byte[] data = inputStr.getBytes();  
  28.   
  29.         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
  30.                 certificatePath);  
  31.   
  32.         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
  33.                 keyStorePath, alias, password);  
  34.         String outputStr = new String(decrypt);  
  35.   
  36.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  37.   
  38.         // 驗證數據一致  
  39.         assertArrayEquals(data, decrypt);  
  40.   
  41.         // 驗證證書有效  
  42.         assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
  43.   
  44.     }  
  45.   
  46.     @Test  
  47.     public void testSign() throws Exception {  
  48.         System.err.println("私鑰加密——公鑰解密");  
  49.   
  50.         String inputStr = "sign";  
  51.         byte[] data = inputStr.getBytes();  
  52.   
  53.         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
  54.                 keyStorePath, alias, password);  
  55.   
  56.         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
  57.                 certificatePath);  
  58.   
  59.         String outputStr = new String(decodedData);  
  60.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  61.         assertEquals(inputStr, outputStr);  
  62.   
  63.         System.err.println("私鑰簽名——公鑰驗證簽名");  
  64.         // 產生簽名  
  65.         String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
  66.                 password);  
  67.         System.err.println("簽名:\r" + sign);  
  68.   
  69.         // 驗證簽名  
  70.         boolean status = CertificateCoder.verify(encodedData, sign,  
  71.                 certificatePath);  
  72.         System.err.println("狀態:\r" + status);  
  73.         assertTrue(status);  
  74.   
  75.     }  
  76.   
  77.     @Test  
  78.     public void testHttps() throws Exception {  
  79.         URL url = new URL("https://www.zlex.org/examples/");  
  80.         HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();  
  81.   
  82.         conn.setDoInput(true);  
  83.         conn.setDoOutput(true);  
  84.   
  85.         CertificateCoder.configSSLSocketFactory(conn, password, keyStorePath,  
  86.                 keyStorePath);  
  87.   
  88.         InputStream is = conn.getInputStream();  
  89.   
  90.         int length = conn.getContentLength();  
  91.   
  92.         DataInputStream dis = new DataInputStream(is);  
  93.         byte[] data = new byte[length];  
  94.         dis.readFully(data);  
  95.   
  96.         dis.close();  
  97.         conn.disconnect();  
  98.         System.err.println(new String(data));  
  99.     }  
  100. }  


再次執行,驗證通過!  
由此,我們了基於SSL協議的認證過程。測試類的testHttps方法模擬了一次瀏覽器的HTTPS訪問。
對於雙向認證,做一個簡單的描述。 
服務器端下發證書,客戶端接受證書。證書帶有公鑰信息,用於驗證服務器端、對數據加密/解密,起到OSI五類服務的認證(鑒別)服務和保密性服務。 

這只是單向認證,為什么?因為客戶端可以驗證服務器端,但服務器端不能驗證客戶端! 
如果客戶端也有這樣一個證書,服務器端也就能夠驗證客戶端,這就是雙向認證了! 

換言之,當你用銀行的“U盾”之類的U盤與銀行賬戶交互時,在你驗證銀行服務器的同時,服務器也在驗證你!這種雙重驗證,正是網銀系統的安全關鍵! 

單向認證見 Java加密技術(十) 
雙向認證需要一個CA機構簽發這樣的客戶端、服務器端證書,首先需要CA機構構建一個根證書。keytool可以構建證書但不能構建我們需要的根證書,openssl則可以! 

根證書簽發客戶端證書,根私鑰簽發服務器端證書! 

我們直接使用linux下的openssl來完成CA,需要修改openssl.cnf文件,在ubuntu下的 /etc/ssl/目錄下,找到[ CA_default ]修改dir變量。 
原文 

引用
[ CA_default ] 

#dir = ./demoCA # Where everything is kept 

我們把c盤的ca目錄作為CA認證的根目錄,文件修改后如下所示: 
引用
[ CA_default ] 

dir = $ENV::HOME/ca # Where everything is kept 


我們需要在用戶目錄下構建一個ca目錄,以及子目錄,如下所下: 
ca 
|__certs 
|__newcerts 
|__private 
|__crl 

執行如下操作: 
Shell代碼    收藏代碼
  1. #!/bin/bash     
  2.   
  3. ca_path=ca  
  4. certs_path=$ca_path/certs  
  5. newcerts_path=$ca_path/newcerts  
  6. private_path=$ca_path/private  
  7. crl_path=$ca_path/crl  
  8.   
  9. echo 移除CA根目錄     
  10. rm -rf ca     
  11.     
  12. echo 構建CA根目錄     
  13. mkdir ca     
  14.    
  15. echo 構建子目錄     
  16. mkdir certs     
  17. mkdir newcerts     
  18. mkdir private     
  19. mkdir crl    
  20.   
  21. #構建文件     
  22. touch $ca_path/index.txt  
  23. echo 01 > $ca_path/serial  
  24. echo      
  25.   
  26. #構建隨機數     
  27. openssl rand -out $private_path/.rand 1000  
  28. echo      
  29.   
  30. echo 生成根證書私鑰     
  31. openssl genrsa -des3 -out $private_path/ca.pem 2048  
  32. echo     
  33.   
  34. echo 查看私鑰信息  
  35. openssl rsa -noout -text -in $private_path/ca.pem  
  36. echo  
  37.   
  38. echo 生成根證書請求      
  39. openssl req -new -key $private_path/ca.pem -out $certs_path/ca.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=ca.zlex.org"  
  40. echo     
  41.   
  42. echo 查看證書請求   
  43. openssl req -in $certs_path/ca.csr -text -noout  
  44. echo   
  45.   
  46. echo 簽發根證書     
  47. openssl ca -create_serial -out $certs_path/ca.crt -days 3650 -batch -keyfile $private_path/ca.pem -selfsign -extensions v3_ca -infiles $certs_path/ca.csr   
  48. #openssl x509 -req -sha1 -extensions v3_ca -signkey $private_path/ca.pem -in $certs_path/ca.csr -out $certs_path/ca.crt -days 3650  
  49. echo     
  50.   
  51. echo 查看證書詳情  
  52. openssl x509 -in $certs_path/ca.crt -text -noout  
  53. echo  
  54.   
  55. echo 證書轉換——根證書     
  56. openssl pkcs12 -export -clcerts -in $certs_path/ca.crt -inkey $private_path/ca.pem -out $certs_path/ca.p12  
  57. echo    
  58.   
  59. echo 生成服務器端私鑰     
  60. openssl genrsa -des3 -out $private_path/server.pem 1024  
  61. echo     
  62.   
  63. echo 查看私鑰信息  
  64. openssl rsa -noout -text -in $private_path/server.pem  
  65. echo  
  66.   
  67. echo 生成服務器端證書請求     
  68. openssl req -new -key $private_path/server.pem -out $certs_path/server.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=www.zlex.org"  
  69. echo     
  70.   
  71. echo 查看證書請求   
  72. openssl req -in $certs_path/server.csr -text -noout  
  73. echo   
  74.   
  75. echo 簽發服務器端證書  
  76. openssl ca -in $certs_path/server.csr -out $certs_path/server.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem -days 365 -notext  
  77. #openssl x509 -req -days 365 -sha1 -extensions v3_req -CA $certs_path/ca.crt -CAkey $private_path/ca.pem -CAserial $ca_path/serial -CAcreateserial -in $certs_path/server.csr -out $certs_path/server.crt  
  78. echo  
  79.   
  80. echo 查看證書詳情  
  81. openssl x509 -in $certs_path/server.crt -text -noout  
  82. echo  
  83.   
  84. echo 證書轉換——服務器端     
  85. openssl pkcs12 -export -clcerts -in $certs_path/server.crt -inkey $private_path/server.pem -out $certs_path/server.p12  
  86. echo      
  87.   
  88. echo 生成客戶端私鑰     
  89. openssl genrsa -des3 -out $private_path/client.pem 1024  
  90. echo     
  91.   
  92. echo 生成客戶端私鑰     
  93. openssl genrsa -des3 -out $private_path/client.pem 1024  
  94. echo     
  95.   
  96. echo 查看私鑰信息  
  97. openssl rsa -noout -text -in $private_path/client.pem  
  98. echo  
  99.   
  100. echo 生成客戶端證書請求     
  101. openssl req -new -key $private_path/client.pem -out $certs_path/client.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=zlex"  
  102. echo     
  103.   
  104. echo 查看證書請求   
  105. openssl req -in $certs_path/client.csr -text -noout  
  106. echo   
  107.   
  108. echo 簽發客戶端證書     
  109. openssl ca -in $certs_path/client.csr -out $certs_path/client.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem -days 365 -notext  
  110. #openssl x509 -req -days 365 -sha1 -extensions dir_sect -CA $certs_path/ca.crt -CAkey $private_path/ca.pem -CAserial $ca_path/serial -in $certs_path/client.csr -out $certs_path/client.crt  
  111. echo     
  112.   
  113. echo 查看證書詳情  
  114. openssl x509 -in $certs_path/client.crt -text -noout  
  115. echo  
  116.   
  117. echo 證書轉換——客戶端     
  118. openssl pkcs12 -export -clcerts -in $certs_path/client.crt -inkey $private_path/client.pem -out $certs_path/client.p12  
  119. echo   
  120.   
  121. echo 生成證書鏈PKCS#7  
  122. openssl crl2pkcs7 -nocrl -certfile $certs_path/server.crt -certfile $certs_path/ca.crt -certfile $certs_path/client.crt -out  
  123. form PEM -out $certs_path/zlex.p7b  
  124. echo  
  125.   
  126. echo 查看證書鏈  
  127. openssl pkcs7 -in $certs_path/zlex.p7b -print_certs -noout  


這個腳本就是最重要的結晶了!  

執行結果,如下: 
引用

生成根證書私鑰 
Generating RSA private key, 2048 bit long modulus 
..................................+++ 
.............................................................+++ 
e is 65537 (0x10001) 
Enter pass phrase for ca/private/ca.pem: 
Verifying - Enter pass phrase for ca/private/ca.pem: 

查看私鑰信息 
Enter pass phrase for ca/private/ca.pem: 
Private-Key: (2048 bit) 
modulus: 
    00:d4:18:ab:5f:ad:b7:d0:09:d4:68:63:b5:db:8a: 
    d1:a1:db:7e:f3:bb:bb:c2:be:a7:35:17:9e:bb:20: 
    d3:1f:ed:63:e7:7d:29:6d:d2:7c:60:06:47:53:a6: 
    23:b0:bd:94:65:3f:57:1e:00:51:f3:a1:9a:1b:83: 
    14:a5:53:72:86:21:a2:57:22:2f:6a:a9:46:50:8c: 
    f0:51:cf:e6:83:5b:23:dc:f9:ea:6c:2e:51:20:61: 
    d1:84:9f:28:e8:01:89:b5:cb:55:68:4a:11:b1:06: 
    56:31:21:16:c8:ac:2b:68:31:e1:de:12:d3:21:12: 
    83:36:4c:ca:a8:b5:7e:b9:a7:63:4e:8e:e0:79:0f: 
    0e:91:36:28:7c:dd:9a:e2:e0:98:8b:91:7f:09:7d: 
    20:bb:37:f2:ab:aa:f0:ef:ae:68:7e:db:ca:db:33: 
    84:48:5a:e3:ff:0b:08:0e:96:6d:01:c8:12:35:ec: 
    9f:31:55:7f:53:7e:bd:fb:c4:16:b8:1f:17:29:42: 
    0f:0e:04:57:14:18:fd:e5:d6:3f:40:04:cd:85:dd: 
    d3:eb:2f:9a:bf:3c:8a:60:01:88:2f:43:0a:8b:bb: 
    50:13:f8:cc:68:f9:10:eb:f9:7e:63:de:62:55:32: 
    a8:fe:ce:51:67:79:c9:a6:3b:a3:c9:d7:81:7c:48: 
    f3:d1 
publicExponent: 65537 (0x10001) 
privateExponent: 
    00:b0:8a:e4:43:1c:df:6e:bc:6f:e0:80:76:c4:8a: 
    75:5a:0b:d1:4d:61:cb:b5:1b:6b:24:c7:47:69:ad: 
    b5:ee:d2:73:a1:21:4e:95:ca:69:9a:a8:3f:40:c2: 
    7e:dc:c3:c0:bc:d2:0f:5a:ba:9b:7c:76:dc:46:e0: 
    42:14:27:34:a1:af:67:68:ad:dc:d8:24:94:91:c1: 
    ee:db:ba:78:be:87:e3:7f:31:4b:4e:c6:f2:e2:48: 
    69:d4:c1:82:94:33:8b:84:15:ff:3e:72:c0:ed:20: 
    40:28:5e:c9:8f:39:b8:5b:df:81:89:8f:13:cc:68: 
    93:6d:64:58:20:3c:0a:82:ce:ec:2f:9b:b2:9d:ca: 
    e7:19:22:98:29:6e:7c:4d:85:45:17:50:8f:5d:b1: 
    45:be:42:af:1a:7f:84:26:b4:5d:a6:22:8a:07:e8: 
    b3:b4:5a:59:45:20:b5:ef:1c:81:25:9e:73:74:04: 
    d6:57:30:2c:a7:25:50:7c:d7:87:73:b3:d0:c2:8b: 
    c9:02:8e:15:9e:40:41:a5:7a:a9:d8:85:fb:5b:9a: 
    59:83:bc:80:fa:74:e6:88:14:70:33:61:d7:f5:51: 
    47:8f:60:51:cb:c4:97:66:65:94:f0:ed:58:ca:80: 
    c1:89:e0:55:68:4c:69:21:0f:08:27:e0:87:11:df: 
    b7:bd 
prime1: 
    00:f7:ff:b0:40:de:62:b6:a2:e5:d0:f5:fa:28:3d: 
    d3:30:30:89:8f:d1:ae:df:e9:09:ee:a0:b0:a5:a5: 
    a4:e5:93:97:7e:e6:0b:09:70:4c:62:99:5e:7d:45: 
    2f:fd:21:5a:31:d9:26:7f:39:5f:6e:eb:36:02:4e: 
    18:99:1b:38:13:99:f5:f3:a3:6b:93:83:67:fb:58: 
    67:d4:07:eb:e3:2f:31:b3:97:8f:f6:86:1f:15:08: 
    1a:4b:b5:a8:06:97:72:9c:74:ab:53:1f:ac:ee:fb: 
    59:03:39:a6:5c:a8:77:43:c0:2c:14:60:0e:71:3d: 
    70:b6:59:09:40:86:04:54:bf 
prime2: 
    00:da:f0:73:2c:bd:52:a5:0d:9a:40:c4:34:fc:c9: 
    cf:0f:67:8a:02:01:ca:e7:b8:4e:57:da:0c:0d:b2: 
    f9:f3:f2:e4:4c:82:61:aa:04:2c:88:39:18:bd:86: 
    d6:dc:d0:e9:6c:c6:6f:d9:87:59:57:9b:1a:6b:c9: 
    56:c1:4d:33:ce:3e:15:b9:42:4e:e0:f8:14:91:c3: 
    fe:63:b2:13:29:99:a7:a6:13:cc:f8:9c:38:29:28: 
    dd:ed:d1:a3:7c:05:2c:26:a0:84:c6:09:9e:42:ef: 
    7b:5e:50:c7:57:e3:bc:02:93:0b:74:a1:b5:0b:6e: 
    23:18:8b:82:6f:ac:3c:0b:6f 
exponent1: 
    7c:a1:23:4b:46:37:27:7f:6f:ac:f6:a0:93:ae:96: 
    3e:46:76:2b:2f:7e:09:8a:8c:72:3e:90:e7:7d:fa: 
    03:61:8b:a5:bb:27:da:c3:73:af:ad:51:9d:f4:b2: 
    2c:2c:a1:ae:21:69:c6:4f:e7:d4:cf:21:a2:40:ea: 
    fd:ae:7f:1c:e2:a7:86:9c:1e:c8:d0:25:e6:5b:44: 
    3a:7b:0c:a1:6c:2b:37:0c:b8:cd:74:13:94:b7:30: 
    b7:d1:7f:b2:68:53:b1:aa:b4:1a:9e:f5:82:58:10: 
    20:9d:cd:2c:0d:81:7a:2b:ce:3b:23:16:be:f3:d8: 
    7b:da:fc:da:4f:3f:47:f3 
exponent2: 
    66:c9:5c:49:34:d9:08:04:4a:d6:fd:46:a3:27:5b: 
    be:af:ad:6b:23:cc:4e:dd:88:6a:56:44:32:6a:44: 
    4e:f3:49:9b:61:da:d8:26:fd:81:36:cd:16:ad:a7: 
    52:24:02:72:be:f6:e3:f9:57:48:79:d8:fd:a1:98: 
    c9:47:a5:7a:be:4b:14:9e:bc:c9:81:ae:a6:80:8d: 
    7d:e0:ac:7e:6b:54:f9:f3:71:d7:86:00:17:d2:c7: 
    de:4e:fd:a1:cc:0b:de:56:9d:ff:1b:a4:e1:67:ed: 
    53:6a:39:2c:5a:0e:7a:66:ee:89:e3:21:4c:2c:78: 
    ed:9d:11:af:bb:fc:b4:a1 
coefficient: 
    00:b1:23:a8:cc:b1:5e:2e:38:09:0c:b5:df:2c:c6: 
    15:e8:08:48:45:b9:9d:ec:6f:27:45:5b:a7:bc:b6: 
    b1:ec:a5:39:b4:40:8e:bc:40:1f:b9:4d:14:2e:18: 
    fb:87:1e:20:91:34:58:e3:ac:c3:4a:dc:a8:2a:97: 
    ce:aa:8d:62:0e:91:af:1f:53:d6:37:55:1d:14:9c: 
    01:98:34:77:28:d7:cf:f7:a0:2d:73:40:48:5e:ed: 
    ae:9b:15:42:06:e6:a3:5a:2b:b0:bc:ee:7a:bb:52: 
    e6:28:19:c2:e5:de:6f:4d:fa:fb:69:81:7b:13:2b: 
    01:87:bf:bf:66:8f:24:a1:8f 

生成根證書請求 
Enter pass phrase for ca/private/ca.pem: 

查看證書請求 
Certificate Request: 
    Data: 
        Version: 0 (0x0) 
        Subject: C=CN, ST=BJ, L=BJ, O=zlex, OU=zlex, CN=ca.zlex.org 
        Subject Public Key Info: 
            Public Key Algorithm: rsaEncryption 
            RSA Public Key: (2048 bit) 
                Modulus (2048 bit): 
                    00:d4:18:ab:5f:ad:b7:d0:09:d4:68:63:b5:db:8a: 
                    d1:a1:db:7e:f3:bb:bb:c2:be:a7:35:17:9e:bb:20: 
                    d3:1f:ed:63:e7:7d:29:6d:d2:7c:60:06:47:53:a6: 
                    23:b0:bd:94:65:3f:57:1e:00:51:f3:a1:9a:1b:83: 
                    14:a5:53:72:86:21:a2:57:22:2f:6a:a9:46:50:8c: 
                    f0:51:cf:e6:83:5b:23:dc:f9:ea:6c:2e:51:20:61: 
                    d1:84:9f:28:e8:01:89:b5:cb:55:68:4a:11:b1:06: 
                    56:31:21:16:c8:ac:2b:68:31:e1:de:12:d3:21:12: 
                    83:36:4c:ca:a8:b5:7e:b9:a7:63:4e:8e:e0:79:0f: 
                    0e:91:36:28:7c:dd:9a:e2:e0:98:8b:91:7f:09:7d: 
                    20:bb:37:f2:ab:aa:f0:ef:ae:68:7e:db:ca:db:33: 
                    84:48:5a:e3:ff:0b:08:0e:96:6d:01:c8:12:35:ec: 
                    9f:31:55:7f:53:7e:bd:fb:c4:16:b8:1f:17:29:42: 
                    0f:0e:04:57:14:18:fd:e5:d6:3f:40:04:cd:85:dd: 
                    d3:eb:2f:9a:bf:3c:8a:60:01:88:2f:43:0a:8b:bb: 
                    50:13:f8:cc:68:f9:10:eb:f9:7e:63:de:62:55:32: 
                    a8:fe:ce:51:67:79:c9:a6:3b:a3:c9:d7:81:7c:48: 
                    f3:d1 
                Exponent: 65537 (0x10001) 
        Attributes: 
            a0:00 
    Signature Algorithm: sha1WithRSAEncryption 
        af:91:f8:56:6f:db:de:cb:df:2c:87:93:99:ac:4b:51:12:a2: 
        c1:2b:09:d2:58:7c:e1:07:5c:53:9f:f3:e1:b6:3a:e9:08:e7: 
        65:89:3b:0a:01:83:24:a3:b5:74:65:50:a5:77:bc:30:1b:7d: 
        80:8b:4c:92:ec:81:91:6e:b7:8f:05:e7:1d:b2:89:84:18:8c: 
        5f:66:be:19:15:ba:ba:c3:f7:0d:c3:7d:7a:11:47:17:e5:cf: 
        87:69:2e:15:91:d7:db:9d:8e:c9:0f:81:71:fa:00:93:33:2c: 
        99:e1:be:76:06:f1:8a:e6:8b:1d:9b:07:70:f0:f2:44:91:ed: 
        a2:ed:28:91:5f:6a:8a:f3:cf:ab:0d:b3:05:30:72:19:86:ae: 
        c6:2d:a4:22:9f:21:cf:55:0c:b7:79:44:01:6e:36:43:a5:dc: 
        a0:ea:46:2a:b0:9d:b3:53:4a:57:fc:72:1b:4c:52:cc:a3:39: 
        d6:49:d6:f4:8c:e2:bf:5a:a6:6e:69:7c:f2:bc:7b:02:b7:f5: 
        91:7f:94:2b:8c:58:0f:aa:a3:72:93:46:fe:08:29:08:51:eb: 
        c6:a0:4e:7a:e1:bd:c6:0b:11:9d:63:96:af:22:ee:7b:79:84: 
        cd:e7:f0:23:17:e7:9f:a2:73:c5:15:e1:f5:a1:af:8d:58:f5: 
        e0:eb:57:fd 

簽發根證書 
Using configuration from /etc/pki/tls/openssl.cnf 
Enter pass phrase for ca/private/ca.pem: 
Check that the request matches the signature 
Signature ok 
Certificate Details: 
        Serial Number: 1 (0x1) 
        Validity 
            Not Before: Jul 24 08:15:59 2012 GMT 
            Not After : Jul 22 08:15:59 2022 GMT 
        Subject: 
            countryName               = CN 
            stateOrProvinceName       = BJ 
            organizationName          = zlex 
            organizationalUnitName    = zlex 
            commonName                = ca.zlex.org 
        X509v3 extensions: 
            X509v3 Subject Key Identifier: 
                7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 
            X509v3 Authority Key Identifier: 
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 
                DirName:/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org 
                serial:01 

            X509v3 Basic Constraints: 
                CA:TRUE 
Certificate is to be certified until Jul 22 08:15:59 2022 GMT (3650 days) 

Write out database with 1 new entries 
Data Base Updated 

查看證書詳情 
Certificate: 
    Data: 
        Version: 3 (0x2) 
        Serial Number: 1 (0x1) 
        Signature Algorithm: sha1WithRSAEncryption 
        Issuer: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org 
        Validity 
            Not Before: Jul 24 08:15:59 2012 GMT 
            Not After : Jul 22 08:15:59 2022 GMT 
        Subject: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org 
        Subject Public Key Info: 
            Public Key Algorithm: rsaEncryption 
            RSA Public Key: (2048 bit) 
                Modulus (2048 bit): 
                    00:d4:18:ab:5f:ad:b7:d0:09:d4:68:63:b5:db:8a: 
                    d1:a1:db:7e:f3:bb:bb:c2:be:a7:35:17:9e:bb:20: 
                    d3:1f:ed:63:e7:7d:29:6d:d2:7c:60:06:47:53:a6: 
                    23:b0:bd:94:65:3f:57:1e:00:51:f3:a1:9a:1b:83: 
                    14:a5:53:72:86:21:a2:57:22:2f:6a:a9:46:50:8c: 
                    f0:51:cf:e6:83:5b:23:dc:f9:ea:6c:2e:51:20:61: 
                    d1:84:9f:28:e8:01:89:b5:cb:55:68:4a:11:b1:06: 
                    56:31:21:16:c8:ac:2b:68:31:e1:de:12:d3:21:12: 
                    83:36:4c:ca:a8:b5:7e:b9:a7:63:4e:8e:e0:79:0f: 
                    0e:91:36:28:7c:dd:9a:e2:e0:98:8b:91:7f:09:7d: 
                    20:bb:37:f2:ab:aa:f0:ef:ae:68:7e:db:ca:db:33: 
                    84:48:5a:e3:ff:0b:08:0e:96:6d:01:c8:12:35:ec: 
                    9f:31:55:7f:53:7e:bd:fb:c4:16:b8:1f:17:29:42: 
                    0f:0e:04:57:14:18:fd:e5:d6:3f:40:04:cd:85:dd: 
                    d3:eb:2f:9a:bf:3c:8a:60:01:88:2f:43:0a:8b:bb: 
                    50:13:f8:cc:68:f9:10:eb:f9:7e:63:de:62:55:32: 
                    a8:fe:ce:51:67:79:c9:a6:3b:a3:c9:d7:81:7c:48: 
                    f3:d1 
                Exponent: 65537 (0x10001) 
        X509v3 extensions: 
            X509v3 Subject Key Identifier: 
                7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 
            X509v3 Authority Key Identifier: 
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 
                DirName:/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org 
                serial:01 

            X509v3 Basic Constraints: 
                CA:TRUE 
    Signature Algorithm: sha1WithRSAEncryption 
        8a:99:b8:17:fc:64:7b:88:9c:1b:91:23:60:f4:5c:51:16:9a: 
        9f:42:b4:d3:a5:bb:79:ca:78:e3:fc:a7:af:66:da:ec:5a:8c: 
        81:c1:aa:04:32:a9:59:e0:d6:6a:f2:37:38:97:70:a5:27:5d: 
        14:73:2e:2d:73:78:1d:37:2c:04:f7:c3:99:9d:be:0c:dd:2a: 
        27:2c:0f:6e:95:96:01:c7:4c:99:f7:49:69:f9:ba:cb:62:b8: 
        c6:43:6c:5b:b5:cd:25:42:a7:fb:81:27:bc:d8:e4:95:26:7d: 
        da:50:f8:b8:be:0a:3d:54:35:d0:9d:22:e7:f0:f0:4c:7d:b4: 
        57:2e:98:91:1a:1d:49:e5:8e:48:f6:2b:54:7e:04:fc:1c:e3: 
        52:f7:04:f6:9b:bb:84:25:31:f7:31:6e:7f:fa:4c:e4:15:a2: 
        86:0a:1a:56:8c:ad:07:49:fb:bc:28:27:a3:95:ba:eb:b3:28: 
        db:11:78:ef:84:fc:3c:16:df:58:39:2e:14:8d:89:fe:7a:d2: 
        24:eb:a7:66:11:8c:88:55:40:e1:c3:3b:95:b2:bc:af:36:0e: 
        92:a8:cd:62:d5:57:9c:11:1b:f6:a1:36:5f:25:6c:16:c5:e2: 
        68:19:e7:12:3d:4b:07:24:81:e6:71:f9:59:c5:f9:1c:62:6d: 
        b3:24:b9:8a 

證書轉換——根證書 
Enter pass phrase for ca/private/ca.pem: 
Enter Export Password: 
Verifying - Enter Export Password: 

生成服務器端私鑰 
Generating RSA private key, 1024 bit long modulus 
......................................................++++++ 
................++++++ 
e is 65537 (0x10001) 
Enter pass phrase for ca/private/server.pem: 
Verifying - Enter pass phrase for ca/private/server.pem: 

查看私鑰信息 
Enter pass phrase for ca/private/server.pem: 
Private-Key: (1024 bit) 
modulus: 
    00:d8:f9:bd:0a:a8:d3:97:98:b2:22:af:29:a9:31: 
    76:50:52:77:c8:3b:7c:91:75:db:b3:63:88:cc:00: 
    be:1a:6c:e6:80:23:90:37:5f:1a:d3:80:f2:7f:b5: 
    77:01:ec:85:3e:4e:c0:af:0d:77:c0:a5:8b:bc:c3: 
    fe:70:91:66:17:a4:ec:23:08:5b:e3:df:a3:40:2f: 
    e6:83:bd:3f:d0:62:9c:c0:36:ad:e7:cb:13:e8:34: 
    d7:6a:66:57:f5:bb:94:2f:7c:d5:27:7b:ee:e6:4f: 
    fc:ff:c1:a4:01:96:d6:a0:b8:46:1d:93:02:a6:c5: 
    00:bd:d9:e9:4e:2d:87:d5:95 
publicExponent: 65537 (0x10001) 
privateExponent: 
    4d:da:15:fd:6c:24:37:c1:bf:30:f8:be:af:09:a3: 
    55:20:b1:ff:f3:70:37:d5:1d:16:99:c1:2c:c9:9b: 
    6c:69:e4:ae:d7:93:d8:7a:54:6a:cd:5a:b5:7e:0c: 
    0c:71:ac:41:76:0a:67:05:23:11:c9:94:81:0f:a6: 
    0d:07:ee:a4:26:0e:20:ff:36:6c:f7:2d:fa:8e:39: 
    85:f8:b8:1a:e0:be:26:f8:24:3c:d4:d0:a0:89:9c: 
    48:15:d9:28:de:51:dd:14:3f:ca:c9:63:ed:5d:e4: 
    50:b0:06:5e:1b:f8:99:b4:49:f6:d6:cb:60:8a:7b: 
    fa:f8:6e:86:44:55:e5:45 
prime1: 
    00:ef:cc:38:ab:e6:98:71:09:32:5c:69:b3:e0:59: 
    9d:d7:7a:f9:e3:b9:cd:a8:84:74:1a:91:2a:db:2c: 
    96:40:5a:28:0b:99:6c:da:fa:ca:83:54:e0:59:06: 
    84:df:55:9a:04:9c:1c:6b:54:52:d5:31:d7:f9:0e: 
    9a:13:b0:ed:03 
prime2: 
    00:e7:a2:c3:03:55:d7:54:7c:3a:38:40:f1:ac:9a: 
    e8:dd:3a:5c:24:a6:78:34:c4:ce:24:c8:31:de:5a: 
    0e:df:09:df:7c:ad:36:14:e0:be:6d:2c:58:89:c6: 
    7e:ec:51:82:68:81:91:ed:b5:04:ff:c0:61:8e:aa: 
    5b:ee:6b:f3:87 
exponent1: 
    2a:22:0c:d7:0f:56:3b:8e:2d:1e:15:a8:78:43:e6: 
    ba:e4:ad:a1:78:95:0d:05:f0:cc:76:33:3c:7d:52: 
    0d:0e:8a:38:b7:85:6b:d8:62:da:be:80:08:c4:5f: 
    76:4a:39:1c:94:3d:5e:12:5b:d7:7f:c1:7d:ce:35: 
    fe:3d:b8:f7 
exponent2: 
    00:94:0b:ec:36:52:84:19:04:79:35:81:14:b5:ec: 
    20:8f:5d:00:8d:90:34:5e:0d:b7:6f:bc:e0:5a:ac: 
    16:bb:29:15:45:1b:73:e8:6e:28:67:a0:a3:4a:13: 
    ab:05:a1:a7:06:e2:61:81:9b:64:01:8e:55:0c:19: 
    08:3e:df:92:3b 
coefficient: 
    00:8e:4e:ee:04:55:cc:4f:0f:c0:02:a4:9d:08:a8: 
    4b:ec:72:7c:86:27:a9:0a:5e:1c:94:65:9e:c6:8a: 
    6a:5c:9b:76:5d:c0:ae:f8:36:61:15:3b:67:fb:15: 
    b3:cf:f4:2c:9b:56:66:13:89:89:69:01:d9:6e:b0: 
    f7:02:d4:06:c9 

生成服務器端證書請求 
Enter pass phrase for ca/private/server.pem: 

查看證書請求 
Certificate Request: 
    Data: 
        Version: 0 (0x0) 
        Subject: C=CN, ST=BJ, L=BJ, O=zlex, OU=zlex, CN=www.zlex.org 
        Subject Public Key Info: 
            Public Key Algorithm: rsaEncryption 
            RSA Public Key: (1024 bit) 
                Modulus (1024 bit): 
                    00:d8:f9:bd:0a:a8:d3:97:98:b2:22:af:29:a9:31: 
                    76:50:52:77:c8:3b:7c:91:75:db:b3:63:88:cc:00: 
                    be:1a:6c:e6:80:23:90:37:5f:1a:d3:80:f2:7f:b5: 
                    77:01:ec:85:3e:4e:c0:af:0d:77:c0:a5:8b:bc:c3: 
                    fe:70:91:66:17:a4:ec:23:08:5b:e3:df:a3:40:2f: 
                    e6:83:bd:3f:d0:62:9c:c0:36:ad:e7:cb:13:e8:34: 
                    d7:6a:66:57:f5:bb:94:2f:7c:d5:27:7b:ee:e6:4f: 
                    fc:ff:c1:a4:01:96:d6:a0:b8:46:1d:93:02:a6:c5: 
                    00:bd:d9:e9:4e:2d:87:d5:95 
                Exponent: 65537 (0x10001) 
        Attributes: 
            a0:00 
    Signature Algorithm: sha1WithRSAEncryption 
        2b:e9:b9:0b:e0:94:56:95:dd:59:1e:19:16:e0:f9:73:db:50: 
        63:d3:d4:4d:5c:9b:98:9f:a7:6d:9b:4d:ae:67:52:18:e1:42: 
        b0:66:7c:75:6a:db:98:bc:e6:47:08:aa:55:ca:ce:35:5c:5a: 
        60:8b:7b:c8:f0:10:8a:bd:5f:d7:c8:b8:48:03:18:7e:68:6e: 
        69:35:9c:c8:b0:c8:65:43:43:25:35:d7:d2:70:45:55:ab:78: 
        51:4d:22:c3:68:b2:97:b5:3c:86:e8:2b:43:de:5d:e4:b0:b5: 
        0e:eb:84:9d:42:81:ee:e0:0a:48:40:6a:93:a4:bd:3a:45:6f: 
        20:24 

簽發服務器端證書 
Using configuration from /etc/pki/tls/openssl.cnf 
Enter pass phrase for ca/private/ca.pem: 
Check that the request matches the signature 
Signature ok 
Certificate Details: 
        Serial Number: 2 (0x2) 
        Validity 
            Not Before: Jul 24 08:16:15 2012 GMT 
            Not After : Jul 24 08:16:15 2013 GMT 
        Subject: 
            countryName               = CN 
            stateOrProvinceName       = BJ 
            organizationName          = zlex 
            organizationalUnitName    = zlex 
            commonName                = www.zlex.org 
        X509v3 extensions: 
            X509v3 Basic Constraints: 
                CA:FALSE 
            Netscape Comment: 
                OpenSSL Generated Certificate 
            X509v3 Subject Key Identifier: 
                CF:79:10:96:42:84:0C:51:DE:6E:DB:3C:5B:08:F1:E1:EB:0C:26:B9 
            X509v3 Authority Key Identifier: 
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 

Certificate is to be certified until Jul 24 08:16:15 2013 GMT (365 days) 
Sign the certificate? [y/n]:y 


1 out of 1 certificate requests certified, commit? [y/n]y 
Write out database with 1 new entries 
Data Base Updated 

查看證書詳情 
Certificate: 
    Data: 
        Version: 3 (0x2) 
        Serial Number: 2 (0x2) 
        Signature Algorithm: sha1WithRSAEncryption 
        Issuer: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org 
        Validity 
            Not Before: Jul 24 08:16:15 2012 GMT 
            Not After : Jul 24 08:16:15 2013 GMT 
        Subject: C=CN, ST=BJ, O=zlex, OU=zlex, CN=www.zlex.org 
        Subject Public Key Info: 
            Public Key Algorithm: rsaEncryption 
            RSA Public Key: (1024 bit) 
                Modulus (1024 bit): 
                    00:d8:f9:bd:0a:a8:d3:97:98:b2:22:af:29:a9:31: 
                    76:50:52:77:c8:3b:7c:91:75:db:b3:63:88:cc:00: 
                    be:1a:6c:e6:80:23:90:37:5f:1a:d3:80:f2:7f:b5: 
                    77:01:ec:85:3e:4e:c0:af:0d:77:c0:a5:8b:bc:c3: 
                    fe:70:91:66:17:a4:ec:23:08:5b:e3:df:a3:40:2f: 
                    e6:83:bd:3f:d0:62:9c:c0:36:ad:e7:cb:13:e8:34: 
                    d7:6a:66:57:f5:bb:94:2f:7c:d5:27:7b:ee:e6:4f: 
                    fc:ff:c1:a4:01:96:d6:a0:b8:46:1d:93:02:a6:c5: 
                    00:bd:d9:e9:4e:2d:87:d5:95 
                Exponent: 65537 (0x10001) 
        X509v3 extensions: 
            X509v3 Basic Constraints: 
                CA:FALSE 
            Netscape Comment: 
                OpenSSL Generated Certificate 
            X509v3 Subject Key Identifier: 
                CF:79:10:96:42:84:0C:51:DE:6E:DB:3C:5B:08:F1:E1:EB:0C:26:B9 
            X509v3 Authority Key Identifier: 
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 

    Signature Algorithm: sha1WithRSAEncryption 
        3d:85:0a:f5:a6:8e:f5:13:1b:fc:74:b6:50:8f:fe:0c:e6:32: 
        0e:0c:5a:0a:75:2d:e8:15:39:2f:93:46:29:c6:cc:27:5a:36: 
        a0:93:f8:bc:38:d5:2d:55:b9:19:de:81:6f:b6:5f:1f:07:65: 
        81:c5:12:4e:ea:3e:09:d0:d5:b8:66:c1:cd:4d:5d:51:19:a1: 
        7f:7b:cb:dc:bf:b0:be:3e:f8:8b:74:d3:31:a9:95:a3:ef:25: 
        a3:1e:98:65:0f:d4:40:51:ef:42:02:72:f0:59:26:8a:e7:d6: 
        ca:34:ad:fb:3d:a8:e7:05:93:a6:78:bd:b5:90:51:83:06:2b: 
        95:db:01:0c:89:9f:74:a4:32:89:c5:15:c6:ec:e2:61:10:29: 
        70:da:c5:ea:d6:9c:be:c3:4c:a1:42:6a:26:2f:23:7c:90:51: 
        8f:51:ee:49:c9:6b:9c:0c:15:a2:d3:dc:90:19:db:4d:d1:ad: 
        ca:06:d1:e1:60:20:18:b1:6d:0b:17:f7:06:e6:e8:d1:b0:0c: 
        6d:55:16:f1:63:54:da:c2:3f:6c:e5:99:68:7a:a0:fa:29:5c: 
        dc:cf:34:90:fb:91:7b:e0:5d:bb:a0:9d:91:f3:17:bd:0b:5a: 
        69:d7:0c:24:75:ca:b2:08:da:bf:67:35:ce:01:d0:4e:45:81: 
        97:bd:fb:87 

證書轉換——服務器端 
Enter pass phrase for ca/private/server.pem: 
Enter Export Password: 
Verifying - Enter Export Password: 

生成客戶端私鑰 
Generating RSA private key, 1024 bit long modulus 
..++++++ 
...........++++++ 
e is 65537 (0x10001) 
Enter pass phrase for ca/private/client.pem: 
Verifying - Enter pass phrase for ca/private/client.pem: 

查看私鑰信息 
Enter pass phrase for ca/private/client.pem: 
Private-Key: (1024 bit) 
modulus: 
    00:b4:e9:7d:3d:6b:8b:07:94:7d:47:51:56:3e:0e: 
    92:2f:87:8c:60:0f:b8:cb:eb:90:6d:13:76:51:75: 
    e4:3e:b7:6e:1f:f0:63:5b:f7:ba:51:c0:04:1e:f1: 
    d0:ef:58:4a:35:47:4a:1a:11:72:fc:e9:10:82:ec: 
    3e:0d:ef:7d:17:a0:5e:93:b4:01:8f:a5:27:3c:3e: 
    a9:26:f0:00:ba:ca:24:98:92:51:3e:4b:d0:81:a7: 
    fc:14:e2:98:f5:27:f2:51:4c:a8:ae:b4:5f:e7:cc: 
    70:7e:23:57:92:6a:cf:d4:1d:6f:b3:52:8a:4a:1a: 
    1b:65:f0:4d:1c:0b:1f:50:eb 
publicExponent: 65537 (0x10001) 
privateExponent: 
    3a:35:b2:8d:73:af:fd:55:62:e5:f2:9e:dc:42:d5: 
    f8:a3:15:a0:c7:0e:3f:d6:e0:d6:a7:df:77:20:86: 
    bb:43:4c:14:cc:c5:3b:8f:3f:0d:14:ca:7e:a6:72: 
    02:c1:16:c7:83:d3:ad:05:96:49:18:38:ae:d7:92: 
    b3:eb:2e:05:43:d6:3d:04:3c:0b:fc:15:79:c5:85: 
    10:ed:21:6e:30:73:0b:a6:4f:9a:fe:db:4a:98:bc: 
    ec:03:7b:7f:e6:16:2f:a5:f3:5e:0d:cf:ce:eb:4a: 
    3e:c5:b9:7f:fc:4c:60:9e:0e:d4:aa:91:5a:46:f7: 
    b3:77:fc:0b:1b:62:70:b9 
prime1: 
    00:ef:6d:7f:92:6a:af:21:59:ed:fe:49:a8:7c:4a: 
    1d:4d:7c:f9:38:bf:e7:dc:42:41:e1:33:f9:e1:c7: 
    74:45:2e:1c:e4:40:8d:5f:1a:ac:11:9e:a4:6c:1d: 
    00:6d:4e:aa:4d:58:e9:92:84:ac:d9:29:67:e0:79: 
    a8:a3:15:e3:2d 
prime2: 
    00:c1:6f:21:c5:62:48:78:3a:0f:25:98:00:46:d6: 
    c2:2d:0f:96:fb:20:4b:f4:03:81:71:3f:6f:30:c0: 
    f3:a6:e6:f4:00:a4:fa:0b:97:e6:2a:21:8c:cb:c1: 
    28:eb:5f:f6:01:62:85:9a:37:98:e7:53:a4:8b:3f: 
    bd:77:eb:f3:77 
exponent1: 
    00:e3:71:e0:9b:85:af:22:7e:9c:a0:50:f6:b6:43: 
    6d:bc:bb:b8:c0:d9:44:f8:2f:15:08:4b:68:d8:bb: 
    b1:cf:3a:34:05:fc:f0:8f:64:f6:0a:b2:ea:bd:2d: 
    7b:c7:5a:d0:5b:33:d8:86:f0:74:86:c3:57:c3:9d: 
    ae:be:66:3f:6d 
exponent2: 
    00:82:4a:c9:04:9b:5f:15:1c:86:77:5c:1b:53:9b: 
    f4:cf:45:60:fd:66:93:c2:99:59:e7:5e:43:17:23: 
    e0:fa:db:36:1f:f9:00:34:2e:ec:ea:14:0f:32:6f: 
    b9:90:51:e2:f2:ab:da:32:36:a0:d7:b0:8f:74:fc: 
    4a:33:2c:cb:a1 
coefficient: 
    51:c1:7e:d7:0d:98:86:cb:ca:41:ea:aa:54:6c:00: 
    49:c3:18:12:c4:5b:75:fe:0d:0c:e2:2f:0f:93:8e: 
    8e:01:c5:9d:ff:40:2b:20:08:24:7f:a5:f2:da:67: 
    96:5e:e6:7e:1e:52:32:2f:88:ef:df:20:6a:75:ec: 
    28:cd:fa:a0 

生成客戶端證書請求 
Enter pass phrase for ca/private/client.pem: 

查看證書請求 
Certificate Request: 
    Data: 
        Version: 0 (0x0) 
        Subject: C=CN, ST=BJ, L=BJ, O=zlex, OU=zlex, CN=zlex 
        Subject Public Key Info: 
            Public Key Algorithm: rsaEncryption 
            RSA Public Key: (1024 bit) 
                Modulus (1024 bit): 
                    00:b4:e9:7d:3d:6b:8b:07:94:7d:47:51:56:3e:0e: 
                    92:2f:87:8c:60:0f:b8:cb:eb:90:6d:13:76:51:75: 
                    e4:3e:b7:6e:1f:f0:63:5b:f7:ba:51:c0:04:1e:f1: 
                    d0:ef:58:4a:35:47:4a:1a:11:72:fc:e9:10:82:ec: 
                    3e:0d:ef:7d:17:a0:5e:93:b4:01:8f:a5:27:3c:3e: 
                    a9:26:f0:00:ba:ca:24:98:92:51:3e:4b:d0:81:a7: 
                    fc:14:e2:98:f5:27:f2:51:4c:a8:ae:b4:5f:e7:cc: 
                    70:7e:23:57:92:6a:cf:d4:1d:6f:b3:52:8a:4a:1a: 
                    1b:65:f0:4d:1c:0b:1f:50:eb 
                Exponent: 65537 (0x10001) 
        Attributes: 
            a0:00 
    Signature Algorithm: sha1WithRSAEncryption 
        91:5b:b2:2e:b3:54:14:92:7a:44:c0:59:11:0f:fe:08:50:33: 
        09:0f:73:d3:9d:15:43:07:66:4a:9e:7c:de:12:4d:bc:b6:3a: 
        7a:6b:36:40:3a:4b:ea:db:f7:2e:a1:de:ce:4f:a6:98:14:3b: 
        c0:f6:3d:fe:db:82:fa:c7:f1:1e:9a:6c:2b:ff:e6:a4:91:b1: 
        ab:20:44:91:a8:d9:1b:13:8f:9e:24:68:16:f3:c1:66:7b:3b: 
        29:b5:61:3d:be:88:00:d8:0a:1c:63:f0:25:6c:33:7d:86:80: 
        54:d5:75:db:6f:7e:9c:52:4c:70:0d:5a:88:ae:b5:1a:12:41: 
        e4:47 

簽發客戶端證書 
Using configuration from /etc/pki/tls/openssl.cnf 
Enter pass phrase for ca/private/ca.pem: 
Check that the request matches the signature 
Signature ok 
Certificate Details: 
        Serial Number: 3 (0x3) 
        Validity 
            Not Before: Jul 24 08:16:35 2012 GMT 
            Not After : Jul 24 08:16:35 2013 GMT 
        Subject: 
            countryName               = CN 
            stateOrProvinceName       = BJ 
            organizationName          = zlex 
            organizationalUnitName    = zlex 
            commonName                = zlex 
        X509v3 extensions: 
            X509v3 Basic Constraints: 
                CA:FALSE 
            Netscape Comment: 
                OpenSSL Generated Certificate 
            X509v3 Subject Key Identifier: 
                FD:85:1C:BA:E0:C4:81:F5:F4:92:F1:FC:8A:59:77:33:60:6F:47:F7 
            X509v3 Authority Key Identifier: 
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 

Certificate is to be certified until Jul 24 08:16:35 2013 GMT (365 days) 
Sign the certificate? [y/n]:y 


1 out of 1 certificate requests certified, commit? [y/n]y 
Write out database with 1 new entries 
Data Base Updated 

查看證書詳情 
Certificate: 
    Data: 
        Version: 3 (0x2) 
        Serial Number: 3 (0x3) 
        Signature Algorithm: sha1WithRSAEncryption 
        Issuer: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org 
        Validity 
            Not Before: Jul 24 08:16:35 2012 GMT 
            Not After : Jul 24 08:16:35 2013 GMT 
        Subject: C=CN, ST=BJ, O=zlex, OU=zlex, CN=zlex 
        Subject Public Key Info: 
            Public Key Algorithm: rsaEncryption 
            RSA Public Key: (1024 bit) 
                Modulus (1024 bit): 
                    00:b4:e9:7d:3d:6b:8b:07:94:7d:47:51:56:3e:0e: 
                    92:2f:87:8c:60:0f:b8:cb:eb:90:6d:13:76:51:75: 
                    e4:3e:b7:6e:1f:f0:63:5b:f7:ba:51:c0:04:1e:f1: 
                    d0:ef:58:4a:35:47:4a:1a:11:72:fc:e9:10:82:ec: 
                    3e:0d:ef:7d:17:a0:5e:93:b4:01:8f:a5:27:3c:3e: 
                    a9:26:f0:00:ba:ca:24:98:92:51:3e:4b:d0:81:a7: 
                    fc:14:e2:98:f5:27:f2:51:4c:a8:ae:b4:5f:e7:cc: 
                    70:7e:23:57:92:6a:cf:d4:1d:6f:b3:52:8a:4a:1a: 
                    1b:65:f0:4d:1c:0b:1f:50:eb 
                Exponent: 65537 (0x10001) 
        X509v3 extensions: 
            X509v3 Basic Constraints: 
                CA:FALSE 
            Netscape Comment: 
                OpenSSL Generated Certificate 
            X509v3 Subject Key Identifier: 
                FD:85:1C:BA:E0:C4:81:F5:F4:92:F1:FC:8A:59:77:33:60:6F:47:F7 
            X509v3 Authority Key Identifier: 
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9 

    Signature Algorithm: sha1WithRSAEncryption 
        b2:31:c0:15:a1:8f:2c:6d:61:0c:4f:6e:c1:fe:7a:88:e0:60: 
        ce:6d:43:b4:29:d8:4d:83:4d:ea:ce:f0:8e:c1:c7:3b:bd:30: 
        cb:92:71:11:7d:19:04:11:58:25:5d:1b:ed:6f:22:13:91:ea: 
        13:7f:0e:99:00:ec:fb:b3:a5:e2:b9:ea:ea:bb:35:09:3b:ca: 
        f5:49:ac:a1:d3:d5:ae:ff:ce:11:a9:2f:53:74:88:24:9f:f8: 
        b2:bc:02:4d:1a:bb:c1:53:3e:6e:31:52:4d:ac:f8:14:bd:b1: 
        0d:31:1d:aa:94:43:38:5e:fb:c2:26:3e:43:ba:25:3b:23:27: 
        a8:7d:5d:3d:f9:97:28:71:51:1d:a4:56:44:b4:f6:51:4a:2b: 
        8b:47:d3:10:49:04:cd:c3:58:62:75:bc:c7:6a:4c:d5:9a:a8: 
        e9:9c:23:ec:f8:26:e5:de:43:4e:f2:8d:c2:75:40:70:3f:03: 
        0f:74:78:7a:bc:ca:6f:90:a0:3e:3a:d2:92:16:d5:ca:af:93: 
        28:1f:24:3a:7e:2c:b9:db:87:10:68:e0:c9:6c:0b:5d:9f:15: 
        be:bc:13:22:af:7b:8f:e9:14:51:04:65:7a:69:18:c2:ca:4f: 
        cb:e5:4c:62:41:88:b1:ee:ac:43:14:34:6d:58:af:52:b1:25: 
        76:f3:0e:8f 

證書轉換——客戶端 
Enter pass phrase for ca/private/client.pem: 
Enter Export Password: 
Verifying - Enter Export Password: 

生成證書鏈 

查看證書鏈 
subject=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=www.zlex.org 
issuer=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org 

subject=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org 
issuer=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org 

subject=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=zlex 
issuer=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org 



來看一下這3套證書,如下兩幅圖所示: 

CA證書 

 

 

 

服務器證書 

 

 

 

客戶證書 

 

 

 

證書鏈 

 

"ca.zlex.org"證書充當了CA根證書,"www.zlex.org"充當服務器端證書,"zlex"充當客戶端證書 

使用keytool將其導入本地密鑰庫 
導入CA證書 
Shell代碼    收藏代碼
  1. keytool -import -v -trustcacerts -alias ca.zlex.org -file ca.crt -storepass 123456 -keystore ca.keystore          

控制台輸出 
引用

所有者:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 
簽發人:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 
序列號:989b27ef00e53a99 
有效期: Wed Jul 18 17:53:51 CST 2012 至Sat Jul 16 17:53:51 CST 2022 
證書指紋: 
         MD5:BA:14:1F:89:3A:1E:63:7B:20:AC:5A:50:FE:65:7E:16 
         SHA1:E0:A4:0E:6F:09:7E:01:27:C0:FC:62:26:1A:0C:C6:7B:BF:6A:18:B3 
         簽名算法名稱:SHA1withRSA 
         版本: 1 
信任這個認證? [否]:  y 
認證已添加至keystore中 
[正在存儲 ca.keystore] 


導入服務器端證書 
Shell代碼    收藏代碼
  1. keytool -import -v -trustcacerts -alias www.zlex.org -file server.crt -storepass 123456 -keystore server.keystore  

控制台輸出 
引用

所有者:CN=www.zlex.org, OU=zlex, O=zlex, ST=BJ, C=CN 
簽發人:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 
序列號:1 
有效期: Wed Jul 18 17:54:25 CST 2012 至Thu Jul 18 17:54:25 CST 2013 
證書指紋: 
         MD5:7E:5E:66:56:AF:E7:F5:72:0F:FC:95:85:97:07:4E:2A 
         SHA1:B1:E7:E8:AC:AB:C9:72:69:D8:E2:25:D5:16:A9:AF:C1:B7:4A:74:5D 
         簽名算法名稱:SHA1withRSA 
         版本: 3 

擴展: 

#1: ObjectId: 2.5.29.14 Criticality=false 
SubjectKeyIdentifier [ 
KeyIdentifier [ 
0000: A8 49 2F E2 2D 15 9F 42   BD 76 2B 20 D3 EB A5 EE  .I/.-..B.v+ .... 
0010: 31 CA E7 63                                        1..c 



#2: ObjectId: 2.5.29.19 Criticality=false 
BasicConstraints:[ 
  CA:false 
  PathLen: undefined 


#3: ObjectId: 2.5.29.35 Criticality=false 
AuthorityKeyIdentifier [ 
[CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN] 
SerialNumber: [    989b27ef 00e53a99] 


#4: ObjectId: 2.16.840.1.113730.1.13 Criticality=false 

信任這個認證? [否]:  y 
認證已添加至keystore中 
[正在存儲 server.keystore] 


導入客戶端證書 
Shell代碼    收藏代碼
  1. keytool -import -v -trustcacerts -alias client -file client.crt -storepass 123456 -keystore client.keystore  

以下是輸出內容: 
引用

所有者:CN=zlex, OU=zlex, O=zlex, ST=BJ, C=CN 
簽發人:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 
序列號:2 
有效期: Wed Jul 18 17:54:49 CST 2012 至Thu Jul 18 17:54:49 CST 2013 
證書指紋: 
         MD5:81:16:ED:92:9E:17:DB:E3:BE:DE:CD:8D:F8:E0:EE:C4 
         SHA1:C0:E0:42:81:79:70:4C:F8:44:D4:76:2D:C5:62:7C:67:B2:41:B3:AC 
         簽名算法名稱:SHA1withRSA 
         版本: 3 

擴展: 

#1: ObjectId: 2.5.29.14 Criticality=false 
SubjectKeyIdentifier [ 
KeyIdentifier [ 
0000: 0C 2C 25 86 C6 8D 04 88   F5 63 19 DC 09 B1 3C 5D  .,%......c....<] 
0010: 59 C9 72 1B                                        Y.r. 



#2: ObjectId: 2.5.29.19 Criticality=false 
BasicConstraints:[ 
  CA:false 
  PathLen: undefined 


#3: ObjectId: 2.5.29.35 Criticality=false 
AuthorityKeyIdentifier [ 
[CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN] 
SerialNumber: [    989b27ef 00e53a99] 


#4: ObjectId: 2.16.840.1.113730.1.13 Criticality=false 

信任這個認證? [否]:  y 
認證已添加至keystore中 
[正在存儲 client.keystore] 


PS 吊銷證書: 
Shell代碼    收藏代碼
  1. echo 吊銷客戶端證書  
  2. openssl ca -revoke $certs_path/client.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem   


引用
Using configuration from /etc/pki/tls/openssl.cnf 
Enter pass phrase for private/ca.pem: 
Revoking Certificate 02. 
Data Base Updated



生成證書吊銷列表文件(CRL) 
執行命令如下: 
Shell代碼    收藏代碼
  1. openssl ca -gencrl -out ca.crl -config "$HOME/testca/conf/testca.conf"  

-crldays和-crlhours參數,說明下一個吊銷列表將在多少天后(或多少小時候)發布。 

可以用以下命令檢查testca.crl的內容: 
Shell代碼    收藏代碼
  1. openssl crl -in testca.crl -text -noout  


引用 
http://blog.csdn.net/gothicane/articles/2865818.aspx 
http://www.5dlinux.com/article/7/2009/linux_35291.html 
http://www.tc.umn.edu/~brams006/selfsign_ubuntu.html 
http://www.tc.umn.edu/~brams006/selfsign.html 
http://zhouzhk.iteye.com/blog/136943 
http://bbs.cfan.com.cn/thread-743287-1-1.html 
http://www.iteye.com/problems/4072 
http://blog.csdn.net/jasonhwang/archive/2008/04/26/2329589.aspx 
http://blog.csdn.net/jasonhwang/archive/2008/04/29/2344768.aspx 

 

 

今天來點實際工作中的硬通貨! 
與計費系統打交道,少不了用到加密/解密實現。為了安全起見,通過非對稱加密交換對稱加密密鑰更是不可或缺。那么需要通過什么載體傳遞非對稱算法公鑰/私鑰信息?數字證書是公鑰的載體,而密鑰庫可以包含公鑰、私鑰信息。 
JKSPKCS#12都是比較常用的兩種密鑰庫格式/標准。對於前者,搞Java開發,尤其是接觸過HTTPS平台的朋友,並不陌生。 JKS文件(通常為*.jks或*.keystore,擴展名無關)可以通過Java原生工具——KeyTool生成;而后者 PKCS#12文件(通常為*.p12或*.pfx,意味個人信息交換文件),則是通過更為常用的OpenSSL工具產生。 
當然,這兩者之間是可以通過導入/導出的方式進行轉換的!當然,這種轉換需要通過KeyTool工具進行! 
回歸正題,計費同事遇到一個難題:合作方交給他們一個*.pfx文件,需要他們從中提取密鑰,然后進行加密交互。其實,通過Java直接操作密鑰庫文件(或個人信息交換文件)對於一般Java開發人員來說,這都是個冷門。不接觸數字安全,根本不知所雲。況且,Java原生的密鑰庫文件格式為JKS,如何操作*.pfx文件?密鑰庫操作需要獲知密鑰庫別名,*.pfx別名是什么?!接下來就解決這些問題!  

方案: 
  1. 通過keytool密鑰庫導入命令importkeystore,將密鑰庫格式由PKCS#12轉換為JKS。
  2. 檢索新生成的密鑰庫文件,提取別名信息。
  3. 由密鑰庫文件導出數字證書(這里將用到別名)。
  4. 通過代碼提取公鑰/私鑰、簽名算法等

先看格式轉換: 
Cmd代碼    收藏代碼
  1. echo 格式轉換  
  2. keytool -importkeystore -v  -srckeystore zlex.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore zlex.keystore -deststoretype jks -deststorepass 123456  

-importkeystore導入密鑰庫,通過格式設定,我們可以將PKCS#12文件轉換為JKS格式。 
-v顯示詳情 
-srckeystore源密鑰庫,這里是zlex.pfx 
-srcstoretype源密鑰庫格式,這里為pkcs12 
-srcstorepass源密鑰庫密碼,這里為123456 
-destkeystore目標密鑰庫,這里為zlex.keystore 
-deststoretype目標密鑰庫格式,這里為jks,默認值也如此 
-deststorepass目標密鑰庫密碼,這里為123456 
通過這個操作,我們能夠獲得所需的密鑰庫文件zlex.keystore。 

 
這時,我們已經獲得了密鑰庫文件,只要確定對應的別名信息,就可以提取公鑰/私鑰,以及數字證書,進行加密交互了!  
Cmd代碼    收藏代碼
  1. echo 查看證書  
  2. keytool -list -keystore zlex.keystore -storepass 123456 -v  

-list列舉密鑰庫 
-keystore密鑰庫,這里是zlex.keystore 
-storepass密鑰庫密碼,這里是123456 
-v顯示詳情 

 
這里需要細致觀察一下別名信息!!!就是紅框中的數字1!!! 


現在,我們把證書導出! 
Cmd代碼    收藏代碼
  1. echo 導出證書  
  2. keytool -exportcert -alias 1 -keystore zlex.keystore -file zlex.crt -storepass 123456  

-exportcert導出證書 
-alias別名,這里是1 
-keystore密鑰庫,這里是zlex.keystore 
-file證書文件,這里是zlex.crt 
-storepass密鑰庫密碼,這里是123456 

 
現在證書也導出了,我們可以提取公鑰/私鑰,進行加密/解密,簽名/驗證操作了!當然,即便沒有證書,我們也能夠通過密鑰庫(JKS格式)文件獲得證書,以及公鑰/私鑰、簽名算法等。 
補充代碼, 其實就是對 Java加密技術(八)的修改!  
Java代碼    收藏代碼
  1. /** 
  2.  * 2010-8-11 
  3.  */  
  4.   
  5. import java.io.FileInputStream;  
  6. import java.security.KeyStore;  
  7. import java.security.PrivateKey;  
  8. import java.security.PublicKey;  
  9. import java.security.Signature;  
  10. import java.security.cert.Certificate;  
  11. import java.security.cert.CertificateFactory;  
  12. import java.security.cert.X509Certificate;  
  13. import java.util.Date;  
  14.   
  15. import javax.crypto.Cipher;  
  16.   
  17. /** 
  18.  * 證書操作類 
  19.  *  
  20.  * @author <a href="mailto:zlex.dongliang@gmail.com">梁棟</a> 
  21.  * @since 1.0 
  22.  */  
  23. public class CertificateCoder {  
  24.     /** 
  25.      * Java密鑰庫(Java Key Store,JKS)KEY_STORE 
  26.      */  
  27.     public static final String KEY_STORE = "JKS";  
  28.   
  29.     public static final String X509 = "X.509";  
  30.   
  31.     /** 
  32.      * 由 KeyStore獲得私鑰 
  33.      *  
  34.      * @param keyStorePath 
  35.      * @param keyStorePassword 
  36.      * @param alias 
  37.      * @param aliasPassword 
  38.      * @return 
  39.      * @throws Exception 
  40.      */  
  41.     private static PrivateKey getPrivateKey(String keyStorePath,  
  42.             String keyStorePassword, String alias, String aliasPassword)  
  43.             throws Exception {  
  44.         KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);  
  45.         PrivateKey key = (PrivateKey) ks.getKey(alias,  
  46.                 aliasPassword.toCharArray());  
  47.         return key;  
  48.     }  
  49.   
  50.     /** 
  51.      * 由 Certificate獲得公鑰 
  52.      *  
  53.      * @param certificatePath 
  54.      * @return 
  55.      * @throws Exception 
  56.      */  
  57.     private static PublicKey getPublicKey(String certificatePath)  
  58.             throws Exception {  
  59.         Certificate certificate = getCertificate(certificatePath);  
  60.         PublicKey key = certificate.getPublicKey();  
  61.         return key;  
  62.     }  
  63.   
  64.     /** 
  65.      * 獲得Certificate 
  66.      *  
  67.      * @param certificatePath 
  68.      * @return 
  69.      * @throws Exception 
  70.      */  
  71.     private static Certificate getCertificate(String certificatePath)  
  72.             throws Exception {  
  73.         CertificateFactory certificateFactory = CertificateFactory  
  74.                 .getInstance(X509);  
  75.         FileInputStream in = new FileInputStream(certificatePath);  
  76.   
  77.         Certificate certificate = certificateFactory.generateCertificate(in);  
  78.         in.close();  
  79.   
  80.         return certificate;  
  81.     }  
  82.   
  83.     /** 
  84.      * 獲得Certificate 
  85.      *  
  86.      * @param keyStorePath 
  87.      * @param keyStorePassword 
  88.      * @param alias 
  89.      * @return 
  90.      * @throws Exception 
  91.      */  
  92.     private static Certificate getCertificate(String keyStorePath,  
  93.             String keyStorePassword, String alias) throws Exception {  
  94.         KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);  
  95.         Certificate certificate = ks.getCertificate(alias);  
  96.   
  97.         return certificate;  
  98.     }  
  99.   
  100.     /** 
  101.      * 獲得KeyStore 
  102.      *  
  103.      * @param keyStorePath 
  104.      * @param password 
  105.      * @return 
  106.      * @throws Exception 
  107.      */  
  108.     private static KeyStore getKeyStore(String keyStorePath, String password)  
  109.             throws Exception {  
  110.         FileInputStream is = new FileInputStream(keyStorePath);  
  111.         KeyStore ks = KeyStore.getInstance(KEY_STORE);  
  112.         ks.load(is, password.toCharArray());  
  113.         is.close();  
  114.         return ks;  
  115.     }  
  116.   
  117.     /** 
  118.      * 私鑰加密 
  119.      *  
  120.      * @param data 
  121.      * @param keyStorePath 
  122.      * @param keyStorePassword 
  123.      * @param alias 
  124.      * @param aliasPassword 
  125.      * @return 
  126.      * @throws Exception 
  127.      */  
  128.     public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
  129.             String keyStorePassword, String alias, String aliasPassword)  
  130.             throws Exception {  
  131.         // 取得私鑰  
  132.         PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
  133.                 alias, aliasPassword);  
  134.   
  135.         // 對數據加密  
  136.         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
  137.         cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
  138.   
  139.         return cipher.doFinal(data);  
  140.   
  141.     }  
  142.   
  143.     /** 
  144.      * 私鑰解密 
  145.      *  
  146.      * @param data 
  147.      * @param keyStorePath 
  148.      * @param alias 
  149.      * @param keyStorePassword 
  150.      * @param aliasPassword 
  151.      * @return 
  152.      * @throws Exception 
  153.      */  
  154.     public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
  155.             String alias, String keyStorePassword, String aliasPassword)  
  156.             throws Exception {  
  157.         // 取得私鑰  
  158.         PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
  159.                 alias, aliasPassword);  
  160.   
  161.         // 對數據加密  
  162.         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
  163.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  164.   
  165.         return cipher.doFinal(data);  
  166.   
  167.     }  
  168.   
  169.     /** 
  170.      * 公鑰加密 
  171.      *  
  172.      * @param data 
  173.      * @param certificatePath 
  174.      * @return 
  175.      * @throws Exception 
  176.      */  
  177.     public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
  178.             throws Exception {  
  179.   
  180.         // 取得公鑰  
  181.         PublicKey publicKey = getPublicKey(certificatePath);  
  182.         // 對數據加密  
  183.         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
  184.         cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  185.   
  186.         return cipher.doFinal(data);  
  187.   
  188.     }  
  189.   
  190.     /** 
  191.      * 公鑰解密 
  192.      *  
  193.      * @param data 
  194.      * @param certificatePath 
  195.      * @return 
  196.      * @throws Exception 
  197.      */  
  198.     public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
  199.             throws Exception {  
  200.         // 取得公鑰  
  201.         PublicKey publicKey = getPublicKey(certificatePath);  
  202.   
  203.         // 對數據加密  
  204.         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
  205.         cipher.init(Cipher.DECRYPT_MODE, publicKey);  
  206.   
  207.         return cipher.doFinal(data);  
  208.   
  209.     }  
  210.   
  211.     /** 
  212.      * 驗證Certificate 
  213.      *  
  214.      * @param certificatePath 
  215.      * @return 
  216.      */  
  217.     public static boolean verifyCertificate(String certificatePath) {  
  218.         return verifyCertificate(new Date(), certificatePath);  
  219.     }  
  220.   
  221.     /** 
  222.      * 驗證Certificate是否過期或無效 
  223.      *  
  224.      * @param date 
  225.      * @param certificatePath 
  226.      * @return 
  227.      */  
  228.     public static boolean verifyCertificate(Date date, String certificatePath) {  
  229.         boolean status = true;  
  230.         try {  
  231.             // 取得證書  
  232.             Certificate certificate = getCertificate(certificatePath);  
  233.             // 驗證證書是否過期或無效  
  234.             status = verifyCertificate(date, certificate);  
  235.         } catch (Exception e) {  
  236.             status = false;  
  237.         }  
  238.         return status;  
  239.     }  
  240.   
  241.     /** 
  242.      * 驗證證書是否過期或無效 
  243.      *  
  244.      * @param date 
  245.      * @param certificate 
  246.      * @return 
  247.      */  
  248.     private static boolean verifyCertificate(Date date, Certificate certificate) {  
  249.         boolean status = true;  
  250.         try {  
  251.             X509Certificate x509Certificate = (X509Certificate) certificate;  
  252.             x509Certificate.checkValidity(date);  
  253.         } catch (Exception e) {  
  254.             status = false;  
  255.         }  
  256.         return status;  
  257.     }  
  258.   
  259.     /** 
  260.      * 簽名 
  261.      *  
  262.      * @param keyStorePath 
  263.      * @param alias 
  264.      * @param keyStorePassword 
  265.      * @param aliasPassword 
  266.      * @return 
  267.      * @throws Exception 
  268.      */  
  269.     public static byte[] sign(byte[] sign, String keyStorePath, String alias,  
  270.             String keyStorePassword, String aliasPassword) throws Exception {  
  271.         // 獲得證書  
  272.         X509Certificate x509Certificate = (X509Certificate) getCertificate(  
  273.                 keyStorePath, keyStorePassword, alias);  
  274.   
  275.         // 取得私鑰  
  276.         PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
  277.                 alias, aliasPassword);  
  278.   
  279.         // 構建簽名  
  280.         Signature signature = Signature.getInstance(x509Certificate  
  281.                 .getSigAlgName());  
  282.         signature.initSign(privateKey);  
  283.         signature.update(sign);  
  284.         return signature.sign();  
  285.     }  
  286.   
  287.     /** 
  288.      * 驗證簽名 
  289.      *  
  290.      * @param data 
  291.      * @param sign 
  292.      * @param certificatePath 
  293.      * @return 
  294.      * @throws Exception 
  295.      */  
  296.     public static boolean verify(byte[] data, byte[] sign,  
  297.             String certificatePath) throws Exception {  
  298.         // 獲得證書  
  299.         X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
  300.         // 獲得公鑰  
  301.         PublicKey publicKey = x509Certificate.getPublicKey();  
  302.         // 構建簽名  
  303.         Signature signature = Signature.getInstance(x509Certificate  
  304.                 .getSigAlgName());  
  305.         signature.initVerify(publicKey);  
  306.         signature.update(data);  
  307.   
  308.         return signature.verify(sign);  
  309.   
  310.     }  
  311.   
  312.     /** 
  313.      * 驗證Certificate 
  314.      *  
  315.      * @param keyStorePath 
  316.      * @param keyStorePassword 
  317.      * @param alias 
  318.      * @return 
  319.      */  
  320.     public static boolean verifyCertificate(Date date, String keyStorePath,  
  321.             String keyStorePassword, String alias) {  
  322.         boolean status = true;  
  323.         try {  
  324.             Certificate certificate = getCertificate(keyStorePath,  
  325.                     keyStorePassword, alias);  
  326.             status = verifyCertificate(date, certificate);  
  327.         } catch (Exception e) {  
  328.             status = false;  
  329.         }  
  330.         return status;  
  331.     }  
  332.   
  333.     /** 
  334.      * 驗證Certificate 
  335.      *  
  336.      * @param keyStorePath 
  337.      * @param keyStorePassword 
  338.      * @param alias 
  339.      * @return 
  340.      */  
  341.     public static boolean verifyCertificate(String keyStorePath,  
  342.             String keyStorePassword, String alias) {  
  343.         return verifyCertificate(new Date(), keyStorePath, keyStorePassword,  
  344.                 alias);  
  345.     }  
  346. }  

相信上述代碼已經幫朋友們解決了相當多的問題!  
給出測試類: 
Java代碼    收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.apache.commons.codec.binary.Hex;  
  6. import org.junit.Test;  
  7.   
  8. /** 
  9.  * 證書操作驗證類 
  10.  *  
  11.  * @author <a href="mailto:zlex.dongliang@gmail.com">梁棟</a> 
  12.  * @version 1.0 
  13.  * @since 1.0 
  14.  */  
  15. public class CertificateCoderTest {  
  16.     private String certificatePath = "zlex.crt";  
  17.     private String keyStorePath = "zlex.keystore";  
  18.     private String keyStorePassword = "123456";  
  19.     private String aliasPassword = "123456";  
  20.     private String alias = "1";  
  21.   
  22.     @Test  
  23.     public void test() throws Exception {  
  24.         System.err.println("公鑰加密——私鑰解密");  
  25.         String inputStr = "Ceritifcate";  
  26.         byte[] data = inputStr.getBytes();  
  27.   
  28.         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
  29.                 certificatePath);  
  30.   
  31.         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
  32.                 keyStorePath, alias, keyStorePassword, aliasPassword);  
  33.         String outputStr = new String(decrypt);  
  34.   
  35.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  36.   
  37.         // 驗證數據一致  
  38.         assertArrayEquals(data, decrypt);  
  39.   
  40.         // 驗證證書有效  
  41.         assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
  42.   
  43.     }  
  44.   
  45.     @Test  
  46.     public void testSign() throws Exception {  
  47.         System.err.println("私鑰加密——公鑰解密");  
  48.   
  49.         String inputStr = "sign";  
  50.         byte[] data = inputStr.getBytes();  
  51.   
  52.         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
  53.                 keyStorePath, keyStorePassword, alias, aliasPassword);  
  54.   
  55.         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
  56.                 certificatePath);  
  57.   
  58.         String outputStr = new String(decodedData);  
  59.         System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
  60.         assertEquals(inputStr, outputStr);  
  61.   
  62.         System.err.println("私鑰簽名——公鑰驗證簽名");  
  63.         // 產生簽名  
  64.         byte[] sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
  65.                 keyStorePassword, aliasPassword);  
  66.         System.err.println("簽名:\r" + Hex.encodeHexString(sign));  
  67.   
  68.         // 驗證簽名  
  69.         boolean status = CertificateCoder.verify(encodedData, sign,  
  70.                 certificatePath);  
  71.         System.err.println("狀態:\r" + status);  
  72.         assertTrue(status);  
  73.     }  
  74.   
  75.     @Test  
  76.     public void testVerify() throws Exception {  
  77.         System.err.println("密鑰庫證書有效期驗證");  
  78.         boolean status = CertificateCoder.verifyCertificate(new Date(),  
  79.                 keyStorePath, keyStorePassword, alias);  
  80.         System.err.println("證書狀態:\r" + status);  
  81.         assertTrue(status);  
  82.     }  
  83. }  

第一個測試方法,用於提取公鑰/私鑰進行加密/解密操作。 
第二個測試方法,用於提取簽名算法進行簽名/驗證操作。 
第三個測試方法,用於測試密鑰庫該別名對應的證書,當前日期下,是否有效。 
 


OK,任務完成,密鑰成功提取,剩下的都是代碼基本功了!


免責聲明!

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



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