Java中使用OpenSSL生成的RSA公私鑰進行數據加解密

RSA是什麼:RSA公鑰加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美國麻省理工學院)開發的。RSA取名來自開發他們三者的名字。RSA是目前最有影響力的公鑰加密算法,它可以 抵抗到目前爲止已知的全部密碼攻擊,已被ISO推薦爲公鑰數據加密標準。目前該加密方式普遍用於網上銀行、數字簽名等場合。RSA算法基於一個十分簡單的 數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,所以能夠將乘積公開做爲加密密鑰。html

OpenSSL是什 麼:衆多的密碼算法、公鑰基礎設施標準以及SSL協議,或許這些有趣的功能會讓你產生實現全部這些算法和標準的想法。果然如此,在對你表示敬佩的同時,還 是忍不住提醒你:這是一個使人望而生畏的過程。這個工做再也不是簡單的讀懂幾本密碼學專著和協議文檔那麼簡單,而是要理解全部這些算法、標準和協議文檔的每 一個細節,並用你可能很熟悉的C語言字符一個一個去實現這些定義和過程。咱們不知道你將須要多少時間來完成這項有趣而可怕的工做,但確定不是一年兩年的問 題。OpenSSL就是由Eric A. Young和Tim J. Hudson兩位絕世大好人自1995年就開始編寫的集合衆多安全算法的算法集合。經過命令或者開發庫,咱們能夠輕鬆實現標準的公開算法應用。
java


個人一個假設應用背景:linux

隨 着移動互聯網的普及,爲移動設備開發的應用也層出不窮。這些應用每每伴隨着用戶註冊與密碼驗證的功能。」網絡傳輸「、」應用程序日誌訪問「中的安全性都存 在着隱患。密碼做爲用戶的敏感數據,特別須要開發者在應用上線以前作好安全防範。處理不當,可能會形成諸如商業競爭對手的惡意攻擊、第三方合做商的訴訟等 問題。android


RSA算法雖然有這麼多好處,可是在網上找不到一個完整的例子來講明如何操做。下面我就來介紹一下:算法

1、使用OpenSSL來生成私鑰和公鑰安全

我使用的是Linux系統,已經安裝了OpenSSL軟件包,此時請驗證你的機器上已經安裝了OpenSSL,運行命令應當出現以下信息:網絡

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片oracle

  1. [root@chaijunkun ~]# openssl version -a  app

  2. OpenSSL 1.0.0-fips 29 Mar 2010  框架

  3. built on: Wed Jan 25 02:17:15 GMT 2012  

  4. platform: linux-x86_64  

  5. options:  bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx)   

  6. compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -DTERMIO -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -Wa,--noexecstack -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DWHIRLPOOL_ASM  

  7. OPENSSLDIR: "/etc/pki/tls"  

  8. engines:  aesni dynamic   

先來生成私鑰:

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. [root@chaijunkun ~]# openssl genrsa -out rsa_private_key.pem 1024  

  2. Generating RSA private key, 1024 bit long modulus  

  3. .......................++++++  

  4. ..++++++  

  5. e is 65537 (0x10001)  

這條命令讓openssl隨機生成了一份私鑰,加密長度是1024位。加密長度是指理論上最大容許」被加密的信息「長度的限制,也就是明文的長度限制。隨着這個參數的增大(比方說2048),容許的明文長度也會增長,但同時也會形成計算複雜度的極速增加。通常推薦的長度就是1024位(128字節)。

咱們來看一下私鑰的內容:

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. [root@chaijunkun ~]# cat rsa_private_key.pem   

  2. -----BEGIN RSA PRIVATE KEY-----  

  3. MIICWwIBAAKBgQChDzcjw/rWgFwnxunbKp7/4e8w/UmXx2jk6qEEn69t6N2R1i/L  

  4. mcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRhtVx1uOH/2U378fscEESEG8XDq  

  5. ll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNgXIlk3gdhnzh+uoEQywIDAQAB  

  6. AoGAaeKk76CSsp7k90mwyWP18GhLZru+vEhfT9BpV67cGLg1owFbntFYQSPVsTFm  

  7. U2lWn5HD/IcV+EGaj4fOLXdM43Kt4wyznoABSZCKKxs6uRciu8nQaFNUy4xVeOfX  

  8. PHU2TE7vi4LDkw9df1fya+DScSLnaDAUN3OHB5jqGL+Ls5ECQQDUfuxXN3uqGYKk  

  9. znrKj0j6pY27HRfROMeHgxbjnnApCQ71SzjqAM77R3wIlKfh935OIV0aQC4jQRB4  

  10. iHYSLl9lAkEAwgh4jxxXeIAufMsgjOi3qpJqGvumKX0W96McpCwV3Fsew7W1/msi  

  11. suTkJp5BBvjFvFwfMAHYlJdP7W+nEBWkbwJAYbz/eB5NAzA4pxVR5VmCd8cuKaJ4  

  12. EgPLwsjI/mkhrb484xZ2VyuICIwYwNmfXpA3yDgQWsKqdgy3Rrl9lV8/AQJAcjLi  

  13. IfigUr++nJxA8C4Xy0CZSoBJ76k710wdE1MPGr5WgQF1t+P+bCPjVAdYZm4Mkyv0  

  14. /yBXBD16QVixjvnt6QJABli6Zx9GYRWnu6AKpDAHd8QjWOnnNfNLQHue4WepEvkm  

  15. CysG+IBs2GgsXNtrzLWJLFx7VHmpqNTTC8yNmX1KFw==  

  16. -----END RSA PRIVATE KEY-----  

內容都是標準的ASCII字符,開頭一行和結尾一行有明顯的標記,真正的私鑰數據是中間的不規則字符。

2015 年3月24日補充:密鑰文件最終將數據經過Base64編碼進行存儲。能夠看到上述密鑰文件內容每一行的長度都很規律。這是因爲RFC2045中規 定:The encoded output stream must be represented in lines of no more than 76 characters each。也就是說Base64編碼的數據每行最多不超過76字符,對於超長數據須要按行分割。

接下來根據私鑰生成公鑰:

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. [root@chaijunkun ~]# openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout  

  2. writing RSA key  

再來看一下公鑰的內容:

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. [root@chaijunkun ~]# cat rsa_public_ley.pem   

  2. -----BEGIN PUBLIC KEY-----  

  3. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w  

  4. /UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht  

  5. Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg  

  6. XIlk3gdhnzh+uoEQywIDAQAB  

  7. -----END PUBLIC KEY-----  

這時候的私鑰還不能直接被使用,須要進行PKCS#8編碼

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. [root@chaijunkun ~]# openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt  

命令中指明瞭輸入私鑰文件爲rsa_private_key.pem,輸出私鑰文件爲pkcs8_rsa_private_key.pem,不採用任何二次加密(-nocrypt)

再來看一下,編碼後的私鑰文件是否是和以前的私鑰文件不一樣了:

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. [root@chaijunkun ~]# cat pkcs8_rsa_private_key.pem   

  2. -----BEGIN PRIVATE KEY-----  

  3. MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG  

  4. 6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4  

  5. 1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA  

  6. ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm  

  7. u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j  

  8. DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo  

  9. MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ  

  10. DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM  

  11. 6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ  

  12. FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA  

  13. 2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX  

  14. TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh  

  15. Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo  

  16. 1NMLzI2ZfUoX  

  17. -----END PRIVATE KEY-----  

至此,可用的密鑰對已經生成好了,私鑰使用pkcs8_rsa_private_key.pem,公鑰採用rsa_public_key.pem。

2014年5月20日補充:最近又遇到RSA加密的需求了,並且對方要求只能使用第一步生成的未通過PKCS#8編碼的私鑰文件。後來查看相關文獻得知第一步生成的私鑰文件編碼是PKCS#1格式,這種格式Java實際上是支持的,只不過多寫兩行代碼而已:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(priKeyData));  

  2. RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());  

  3. KeyFactory keyFactory= KeyFactory.getInstance("RSA");  

  4. PrivateKey priKey= keyFactory.generatePrivate(rsaPrivKeySpec);  

首先將PKCS#1的私鑰文件讀取出來(注意去掉減號開頭的註釋內容),而後使用Base64解碼讀出的字符串,便獲得priKeyData,也就是第一行代碼中的參數。最後一行獲得了私鑰。接下來的用法就沒什麼區別了。

參考文獻:https://community.oracle.com/thread/1529240?start=0&tstart=0


2、編寫Java代碼實際測試

2012 年2月23日補充:在標準JDK中只是規定了JCE(JCE (Java Cryptography Extension) 是一組包,它們提供用於加密、密鑰生成和協商以及 Message Authentication Code(MAC)算法的框架和實現。它提供對對稱、不對稱、塊和流密碼的加密支持,它還支持安全流和密封的對象。)接口,可是內部實現須要本身或者第三 方提供。所以咱們這裏使用bouncycastle的開源的JCE實現包,下載地址:http://bouncycastle.org /latest_releases.html,我使用的是bcprov-jdk16-146.jar,這是在JDK1.6環境下使用的。若是須要其餘 JDK版本下的實現,能夠在以前的下載頁面中找到對應版本。

下面來看一下我實現的代碼:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. package net.csdn.blog.chaijunkun;  

  2.   

  3. import java.io.BufferedReader;  

  4. import java.io.IOException;  

  5. import java.io.InputStream;  

  6. import java.io.InputStreamReader;  

  7. import java.security.InvalidKeyException;  

  8. import java.security.KeyFactory;  

  9. import java.security.KeyPair;  

  10. import java.security.KeyPairGenerator;  

  11. import java.security.NoSuchAlgorithmException;  

  12. import java.security.SecureRandom;  

  13. import java.security.interfaces.RSAPrivateKey;  

  14. import java.security.interfaces.RSAPublicKey;  

  15. import java.security.spec.InvalidKeySpecException;  

  16. import java.security.spec.PKCS8EncodedKeySpec;  

  17. import java.security.spec.X509EncodedKeySpec;  

  18.   

  19. import javax.crypto.BadPaddingException;  

  20. import javax.crypto.Cipher;  

  21. import javax.crypto.IllegalBlockSizeException;  

  22. import javax.crypto.NoSuchPaddingException;  

  23.   

  24. import org.bouncycastle.jce.provider.BouncyCastleProvider;  

  25.   

  26. import sun.misc.BASE64Decoder;  

  27.   

  28. public class RSAEncrypt {  

  29.       

  30.     private static final String DEFAULT_PUBLIC_KEY=   

  31.         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w" + "\r" +  

  32.         "/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht" + "\r" +  

  33.         "Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg" + "\r" +  

  34.         "XIlk3gdhnzh+uoEQywIDAQAB" + "\r";  

  35.       

  36.     private static final String DEFAULT_PRIVATE_KEY=  

  37.         "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG" + "\r" +  

  38.         "6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4" + "\r" +  

  39.         "1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA" + "\r" +  

  40.         "ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm" + "\r" +  

  41.         "u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j" + "\r" +  

  42.         "DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo" + "\r" +  

  43.         "MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ" + "\r" +  

  44.         "DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM" + "\r" +  

  45.         "6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ" + "\r" +  

  46.         "FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA" + "\r" +  

  47.         "2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX" + "\r" +  

  48.         "TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh" + "\r" +  

  49.         "Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo" + "\r" +  

  50.         "1NMLzI2ZfUoX" + "\r";  

  51.   

  52.     /** 

  53.      * 私鑰 

  54.      */  

  55.     private RSAPrivateKey privateKey;  

  56.   

  57.     /** 

  58.      * 公鑰 

  59.      */  

  60.     private RSAPublicKey publicKey;  

  61.       

  62.     /** 

  63.      * 字節數據轉字符串專用集合 

  64.      */  

  65.     private static final char[] HEX_CHAR= {'0''1''2''3''4''5''6''7''8''9''a''b''c''d''e''f'};  

  66.       

  67.   

  68.     /** 

  69.      * 獲取私鑰 

  70.      * @return 當前的私鑰對象 

  71.      */  

  72.     public RSAPrivateKey getPrivateKey() {  

  73.         return privateKey;  

  74.     }  

  75.   

  76.     /** 

  77.      * 獲取公鑰 

  78.      * @return 當前的公鑰對象 

  79.      */  

  80.     public RSAPublicKey getPublicKey() {  

  81.         return publicKey;  

  82.     }  

  83.   

  84.     /** 

  85.      * 隨機生成密鑰對 

  86.      */  

  87.     public void genKeyPair(){  

  88.         KeyPairGenerator keyPairGen= null;  

  89.         try {  

  90.             keyPairGen= KeyPairGenerator.getInstance("RSA");  

  91.         } catch (NoSuchAlgorithmException e) {  

  92.             e.printStackTrace();  

  93.         }  

  94.         keyPairGen.initialize(1024new SecureRandom());  

  95.         KeyPair keyPair= keyPairGen.generateKeyPair();  

  96.         this.privateKey= (RSAPrivateKey) keyPair.getPrivate();  

  97.         this.publicKey= (RSAPublicKey) keyPair.getPublic();  

  98.     }  

  99.   

  100.     /** 

  101.      * 從文件中輸入流中加載公鑰 

  102.      * @param in 公鑰輸入流 

  103.      * @throws Exception 加載公鑰時產生的異常 

  104.      */  

  105.     public void loadPublicKey(InputStream in) throws Exception{  

  106.         try {  

  107.             BufferedReader br= new BufferedReader(new InputStreamReader(in));  

  108.             String readLine= null;  

  109.             StringBuilder sb= new StringBuilder();  

  110.             while((readLine= br.readLine())!=null){  

  111.                 if(readLine.charAt(0)=='-'){  

  112.                     continue;  

  113.                 }else{  

  114.                     sb.append(readLine);  

  115.                     sb.append('\r');  

  116.                 }  

  117.             }  

  118.             loadPublicKey(sb.toString());  

  119.         } catch (IOException e) {  

  120.             throw new Exception("公鑰數據流讀取錯誤");  

  121.         } catch (NullPointerException e) {  

  122.             throw new Exception("公鑰輸入流爲空");  

  123.         }  

  124.     }  

  125.   

  126.   

  127.     /** 

  128.      * 從字符串中加載公鑰 

  129.      * @param publicKeyStr 公鑰數據字符串 

  130.      * @throws Exception 加載公鑰時產生的異常 

  131.      */  

  132.     public void loadPublicKey(String publicKeyStr) throws Exception{  

  133.         try {  

  134.             BASE64Decoder base64Decoder= new BASE64Decoder();  

  135.             byte[] buffer= base64Decoder.decodeBuffer(publicKeyStr);  

  136.             KeyFactory keyFactory= KeyFactory.getInstance("RSA");  

  137.             X509EncodedKeySpec keySpec= new X509EncodedKeySpec(buffer);  

  138.             this.publicKey= (RSAPublicKey) keyFactory.generatePublic(keySpec);  

  139.         } catch (NoSuchAlgorithmException e) {  

  140.             throw new Exception("無此算法");  

  141.         } catch (InvalidKeySpecException e) {  

  142.             throw new Exception("公鑰非法");  

  143.         } catch (IOException e) {  

  144.             throw new Exception("公鑰數據內容讀取錯誤");  

  145.         } catch (NullPointerException e) {  

  146.             throw new Exception("公鑰數據爲空");  

  147.         }  

  148.     }  

  149.   

  150.     /** 

  151.      * 從文件中加載私鑰 

  152.      * @param keyFileName 私鑰文件名 

  153.      * @return 是否成功 

  154.      * @throws Exception  

  155.      */  

  156.     public void loadPrivateKey(InputStream in) throws Exception{  

  157.         try {  

  158.             BufferedReader br= new BufferedReader(new InputStreamReader(in));  

  159.             String readLine= null;  

  160.             StringBuilder sb= new StringBuilder();  

  161.             while((readLine= br.readLine())!=null){  

  162.                 if(readLine.charAt(0)=='-'){  

  163.                     continue;  

  164.                 }else{  

  165.                     sb.append(readLine);  

  166.                     sb.append('\r');  

  167.                 }  

  168.             }  

  169.             loadPrivateKey(sb.toString());  

  170.         } catch (IOException e) {  

  171.             throw new Exception("私鑰數據讀取錯誤");  

  172.         } catch (NullPointerException e) {  

  173.             throw new Exception("私鑰輸入流爲空");  

  174.         }  

  175.     }  

  176.   

  177.     public void loadPrivateKey(String privateKeyStr) throws Exception{  

  178.         try {  

  179.             BASE64Decoder base64Decoder= new BASE64Decoder();  

  180.             byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr);  

  181.             PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);  

  182.             KeyFactory keyFactory= KeyFactory.getInstance("RSA");  

  183.             this.privateKey= (RSAPrivateKey) keyFactory.generatePrivate(keySpec);  

  184.         } catch (NoSuchAlgorithmException e) {  

  185.             throw new Exception("無此算法");  

  186.         } catch (InvalidKeySpecException e) {  

  187.             throw new Exception("私鑰非法");  

  188.         } catch (IOException e) {  

  189.             throw new Exception("私鑰數據內容讀取錯誤");  

  190.         } catch (NullPointerException e) {  

  191.             throw new Exception("私鑰數據爲空");  

  192.         }  

  193.     }  

  194.   

  195.     /** 

  196.      * 加密過程 

  197.      * @param publicKey 公鑰 

  198.      * @param plainTextData 明文數據 

  199.      * @return 

  200.      * @throws Exception 加密過程當中的異常信息 

  201.      */  

  202.     public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception{  

  203.         if(publicKey== null){  

  204.             throw new Exception("加密公鑰爲空, 請設置");  

  205.         }  

  206.         Cipher cipher= null;  

  207.         try {  

  208.             cipher= Cipher.getInstance("RSA"new BouncyCastleProvider());  

  209.             cipher.init(Cipher.ENCRYPT_MODE, publicKey);  

  210.             byte[] output= cipher.doFinal(plainTextData);  

  211.             return output;  

  212.         } catch (NoSuchAlgorithmException e) {  

  213.             throw new Exception("無此加密算法");  

  214.         } catch (NoSuchPaddingException e) {  

  215.             e.printStackTrace();  

  216.             return null;  

  217.         }catch (InvalidKeyException e) {  

  218.             throw new Exception("加密公鑰非法,請檢查");  

  219.         } catch (IllegalBlockSizeException e) {  

  220.             throw new Exception("明文長度非法");  

  221.         } catch (BadPaddingException e) {  

  222.             throw new Exception("明文數據已損壞");  

  223.         }  

  224.     }  

  225.   

  226.     /** 

  227.      * 解密過程 

  228.      * @param privateKey 私鑰 

  229.      * @param cipherData 密文數據 

  230.      * @return 明文 

  231.      * @throws Exception 解密過程當中的異常信息 

  232.      */  

  233.     public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception{  

  234.         if (privateKey== null){  

  235.             throw new Exception("解密私鑰爲空, 請設置");  

  236.         }  

  237.         Cipher cipher= null;  

  238.         try {  

  239.             cipher= Cipher.getInstance("RSA"new BouncyCastleProvider());  

  240.             cipher.init(Cipher.DECRYPT_MODE, privateKey);  

  241.             byte[] output= cipher.doFinal(cipherData);  

  242.             return output;  

  243.         } catch (NoSuchAlgorithmException e) {  

  244.             throw new Exception("無此解密算法");  

  245.         } catch (NoSuchPaddingException e) {  

  246.             e.printStackTrace();  

  247.             return null;  

  248.         }catch (InvalidKeyException e) {  

  249.             throw new Exception("解密私鑰非法,請檢查");  

  250.         } catch (IllegalBlockSizeException e) {  

  251.             throw new Exception("密文長度非法");  

  252.         } catch (BadPaddingException e) {  

  253.             throw new Exception("密文數據已損壞");  

  254.         }         

  255.     }  

  256.   

  257.       

  258.     /** 

  259.      * 字節數據轉十六進制字符串 

  260.      * @param data 輸入數據 

  261.      * @return 十六進制內容 

  262.      */  

  263.     public static String byteArrayToString(byte[] data){  

  264.         StringBuilder stringBuilder= new StringBuilder();  

  265.         for (int i=0; i<data.length; i++){  

  266.             //取出字節的高四位 做爲索引獲得相應的十六進制標識符 注意無符號右移  

  267.             stringBuilder.append(HEX_CHAR[(data[i] & 0xf0)>>> 4]);  

  268.             //取出字節的低四位 做爲索引獲得相應的十六進制標識符  

  269.             stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);  

  270.             if (i<data.length-1){  

  271.                 stringBuilder.append(' ');  

  272.             }  

  273.         }  

  274.         return stringBuilder.toString();  

  275.     }  

  276.   

  277.   

  278.     public static void main(String[] args){  

  279.         RSAEncrypt rsaEncrypt= new RSAEncrypt();  

  280.         //rsaEncrypt.genKeyPair();  

  281.   

  282.         //加載公鑰  

  283.         try {  

  284.             rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY);  

  285.             System.out.println("加載公鑰成功");  

  286.         } catch (Exception e) {  

  287.             System.err.println(e.getMessage());  

  288.             System.err.println("加載公鑰失敗");  

  289.         }  

  290.   

  291.         //加載私鑰  

  292.         try {  

  293.             rsaEncrypt.loadPrivateKey(RSAEncrypt.DEFAULT_PRIVATE_KEY);  

  294.             System.out.println("加載私鑰成功");  

  295.         } catch (Exception e) {  

  296.             System.err.println(e.getMessage());  

  297.             System.err.println("加載私鑰失敗");  

  298.         }  

  299.   

  300.         //測試字符串  

  301.         String encryptStr= "Test String chaijunkun";  

  302.   

  303.         try {  

  304.             //加密  

  305.             byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), encryptStr.getBytes());  

  306.             //解密  

  307.             byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), cipher);  

  308.             System.out.println("密文長度:"+ cipher.length);  

  309.             System.out.println(RSAEncrypt.byteArrayToString(cipher));  

  310.             System.out.println("明文長度:"+ plainText.length);  

  311.             System.out.println(RSAEncrypt.byteArrayToString(plainText));  

  312.             System.out.println(new String(plainText));  

  313.         } catch (Exception e) {  

  314.             System.err.println(e.getMessage());  

  315.         }  

  316.     }  

  317. }  


代碼中我提供了兩種加載公鑰和私鑰的方式。

按流來讀取:適合在android應用中按ID索引資源獲得InputStream的方式;

按字符串來讀取:就像代碼中展現的那樣,將密鑰內容按行存儲到靜態常量中,按String類型導入密鑰。


運行上面的代碼,會顯示以下信息:

[plain] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. 加載公鑰成功  

  2. 加載私鑰成功  

  3. 密文長度:128  

  4. 35 b4 6f 49 69 ae a3 85 a2 a5 0d 45 75 00 23 23 e6 70 69 b4 59 ae 72 6f 6d d3 43 e1 d3 44 85 eb 04 57 2c 46 3e 70 09 4d e6 4c 83 50 c7 56 75 80 c7 e1 31 64 57 c8 e3 46 a7 ce 57 31 ac cd 21 89 89 8f c1 24 c1 22 0c cb 70 6a 0d fa c9 38 80 ba 2e e1 29 02 ed 45 9e 88 e9 23 09 87 af ad ab ac cb 61 03 3c a1 81 56 a5 de c4 79 aa 3e 48 ee 30 3d bc 5b 47 50 75 9f fd 22 87 9e de b1 f4 e8 b2  

  5. 明文長度:22  

  6. 54 65 73 74 20 53 74 72 69 6e 67 20 63 68 61 69 6a 75 6e 6b 75 6e  

  7. Test String chaijunkun  


在main函數中我註釋掉了」rsaEncrypt.genKeyPair()「,這個方法是用來隨機生成密鑰對的(只生成、使用,不存儲)。當不使用文件密鑰時,能夠將載入密鑰的代碼註釋,啓用本方法,也能夠跑通代碼。

加載公鑰與加載私鑰的不一樣點在於公鑰加載時使用的是X509EncodedKeySpec(X509編碼的Key指令),私鑰加載時使用的是PKCS8EncodedKeySpec(PKCS#8編碼的Key指令)。


2012 年2月22日補充:在android軟件開發的過程當中,發現上述代碼不能正常工做,主要緣由在於sun.misc.BASE64Decoder類在 android開發包中不存在。所以須要特別在網上尋找rt.jar的源代碼,至於JDK的src.zip中的源代碼,這個只是JDK中的部分源代碼,上 述的幾個類的代碼都沒有。通過尋找並添加,上述代碼在android應用中可以很好地工做。其中就包含這個類的對應代碼。另外此類還依賴於 CEFormatException、CEStreamExhausted、CharacterDecoder和CharacterEncoder類和異 常定義。


2012 年2月23日補充:起初,我寫這篇文章是想不依賴於任何第三方包來實現RSA的加密與解密,然然後續遇到了問題。因爲在加密方法encrypt和解密方法 decrypt中都要創建一個Cipher對象,這個對象只能經過getInstance來獲取實例。它有兩種:第一個是隻指定算法,不指定提供者 Provider的;第二個是兩個都要指定的。起初沒有指定,代碼依然可以跑通,可是你會發現,每次加密的結果都不同。後來分析才知道Cipher對象 使用的公私鑰是內部本身隨機生成的,不是代碼中指定的公私鑰。奇怪的是,這種不指定Provider的代碼可以在android應用中跑通,並且每次加密 的結果都相同。我想,android的SDK中除了系統的一些開發函數外,本身也實現了JDK的功能,可能在它本身的JDK中已經提供了相應的 Provider,才使得每次加密結果相同。當我像網上的示例代碼那樣加入了bouncycastle的Provider後,果真每次加密的結果都相同了。

相關文章
相關標籤/搜索