`
m635674608
  • 浏览: 5032312 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java中使用OpenSSL生成的RSA公私钥进行数据加解密

    博客分类:
  • java
 
阅读更多

本文出处:http://blog.csdn.net/chaijunkun/article/details/7275632,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

 

RSA 是什么:RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够 抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。目前该加密方式广泛用于网上银行、数字签名等场合。RSA算法基于一个十分简单的 数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

OpenSSL是什 么:众多的密码算法、公钥基础设施标准以及SSL协议,或许这些有趣的功能会让你产生实现所有这些算法和标准的想法。果真如此,在对你表示敬佩的同时,还 是忍不住提醒你:这是一个令人望而生畏的过程。这个工作不再是简单的读懂几本密码学专著和协议文档那么简单,而是要理解所有这些算法、标准和协议文档的每 一个细节,并用你可能很熟悉的C语言字符一个一个去实现这些定义和过程。我们不知道你将需要多少时间来完成这项有趣而可怕的工作,但肯定不是一年两年的问 题。OpenSSL就是由Eric A. Young和Tim J. Hudson两位绝世大好人自1995年就开始编写的集合众多安全算法的算法集合。通过命令或者开发库,我们可以轻松实现标准的公开算法应用。

 

我的一个假设应用背景:

随 着移动互联网的普及,为移动设备开发的应用也层出不穷。这些应用往往伴随着用户注册与密码验证的功能。”网络传输“、”应用程序日志访问“中的安全性都存 在着隐患。密码作为用户的敏感数据,特别需要开发者在应用上线之前做好安全防范。处理不当,可能会造成诸如商业竞争对手的恶意攻击、第三方合作商的诉讼等 问题。

 

RSA算法虽然有这么多好处,但是在网上找不到一个完整的例子来说明如何操作。下面我就来介绍一下:

一、使用OpenSSL来生成私钥和公钥

我使用的是Linux系统,已经安装了OpenSSL软件包,此时请验证你的机器上已经安装了OpenSSL,运行命令应当出现如下信息:

[plain] view plain copy在CODE上查看代码片派生到我的代码片
  1. [root@chaijunkun ~]# openssl version -a  
  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 plain copy在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 plain copy在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 plain copy在CODE上查看代码片派生到我的代码片
  1. [root@chaijunkun ~]# openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout  
  2. writing RSA key  
再来看一下公钥的内容:
[plain] view plain copy在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 plain copy在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 plain copy在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 plain copy在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

 

二、编写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 plain copy在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 plain copy在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后,果然每次加密的结果都相同了。

 

参考文献:

RSA介绍:http://baike.baidu.com/view/7520.htm

OpenSSL介绍:http://baike.baidu.com/view/300712.htm

密钥对生成:http://www.howforge.com/how-to-generate-key-pair-using-openssl

私钥编码格式转换:http://shuany.iteye.com/blog/730910

JCE介绍:http://baike.baidu.com/view/1855103.htm

 

http://blog.csdn.net/chaijunkun/article/details/7275632/

分享到:
评论

相关推荐

    Java OpenSSL生成的RSA公私钥进行数据加解密详细介绍

    在Java中,结合OpenSSL库,可以方便地生成RSA公钥和私钥对,用于数据的加密和解密。这对于保护敏感信息,如用户密码,尤其重要,尤其是在移动应用和网络传输中。 OpenSSL是一个强大的安全套接字层密码库,包含各种...

    RSA公私钥生成工具 RSA秘钥生成工具

    使用OpenSSL,用户可以生成RSA公私钥对,例如通过命令行工具`openssl genpkey`或`openssl rsa`。生成的密钥通常以PEM格式存储,以`.pem`或`.key`为扩展名,内容以Base64编码,并用BEGIN/END标识包裹。 总结一下,...

    javaRSA加密C++RSA解密

    Java OpenSSL生成的RSA公私钥进行数据加解密详细介绍 项目: JAVA生成的RSA的密文,通过C++来解密。 RSA这里就不多介绍了大家自己去看。 JAVA也是通过包来实现加密和解密的,那么我的C++是通过OPENSSL的库来实现的...

    RSA公私钥各种格式(包括加密)转换以及验签过程

    功能如下: 1、转换各种PEM(XML)格式公私钥,可以根据私钥获取公钥(pkcs8一般java用,xml...4、生成RSA公私钥对 软件调用OpenSSL相关函数,ui使用Qt5.9(不支持xp),遵循LGPL协议。 支持win7以上的32或64位系统

    C++使用Openssl进行RSA加密解密及签名验签功能(SHA256)

    本文将深入探讨如何使用OpenSSL库在C++中实现RSA加密、解密以及签名和验签功能,特别关注SHA256WithRSA这一安全强度更高的签名方法。 首先,RSA的核心原理是基于大整数因子分解的困难性。它包含一对密钥,即公钥和...

    基于OpenSSL的RSA加解密的C语言实现

    1.该程序是基于OpenSSL的使用纯C语言来实现RSA加解密的,在Linux环境下开发完成,建议在Linux环境下使用(在Windows环境下需要自行修改); 2.该程序具有生成RSA密钥对、RSA公钥加密和RSA私钥解密的功能,支持手动...

    支付宝生成RSA公私钥一键操作

    本教程将详细介绍如何在支付宝环境中生成RSA公私钥,并提供一键操作的指导。 RSA算法基于大数因子分解的困难性,其工作原理是:一个密钥对由一对密钥组成,一个是公开的公钥,另一个是保密的私钥。发送者使用接收者...

    使用OpenSSL进行RSA加解密.rar

    文件“使用OpenSSL进行RSA加解密.e”是一个易语言编写的源代码文件,它展示了如何在易语言中使用“UseOpensslRSA.dll”进行RSA操作。开发者可以通过阅读和分析这段代码来学习如何创建密钥对、加密和解密数据等基本...

    PHP 公私钥加解密

    标签中的“数据加密”指的是整个过程中数据的安全处理,“RSA公约”则特指采用RSA算法进行公私钥加密。在实际项目中,这种技术常用于保护敏感信息,如用户密码、支付信息等,确保它们在网络传输时不被非法获取。 总...

    OpenSSL 中RSA16进制密钥生成方法

    而`openssl中RSA密钥生成.pdf`可能是一个指南或教程,详细解释了如何在OpenSSL中生成RSA密钥,你可以参考这个文档来获取更深入的理解。 了解并熟练掌握RSA密钥生成和OpenSSL的使用对于任何涉及网络安全的开发人员来...

    openssl RSA加解密例子

    基于openssl写的RSA的公钥加密,私钥解密,私钥加密,公钥解密的测试例子,密钥类型是rsa1024的PEM格式。

    c++通过openssl实现rsa加密解密【windows版】

    c++通过使用openssl实现rsa加密解密算法,网上有很多文章和例子,但是大部分都是linux版的,并且内容不全、代码老旧等各种问题,导致最后无法调试,这里提供的源码是用code::blocks编写的c++源码,可以直接运行...

    基于openssl的RSA的加密,解密,签名和验证签名

    5. **RsaPro可能的使用场景**:`RsaPro`可能是一个包含示例代码或者工具的压缩包,用于演示如何在实际应用中使用上述OpenSSL的RSA功能。可能包括了生成密钥对、加密解密数据以及签名验证的示例程序,帮助开发者理解...

    使用openssl生成RSA秘钥对(用这个!)

    本教程将详细解释如何使用开源工具openssl生成RSA密钥对,并探讨其在实际应用中的重要性。 首先,我们需要了解RSA的基本原理。RSA算法由Ron Rivest、Adi Shamir和Leonard Adleman在1977年提出,它使用一对密钥——...

    使用openssl生成RSA密钥对

    ### 使用OpenSSL生成RSA密钥对 在网络安全与加密领域,使用公钥基础设施(Public Key Infrastructure,PKI)是常见的做法。其中,OpenSSL作为一款强大的工具库,提供了丰富的功能来支持各种加密算法,包括RSA算法。...

    java调用openssl生成证书

    1. **生成私钥**:使用`openssl genpkey`命令生成RSA私钥,如`openssl genpkey -algorithm RSA -out private.key`。私钥是保密的,不应被他人获取。 2. **生成证书签名请求(CSR)**:使用`openssl req`命令创建CSR...

    Delphi RSA加密与解密OpenSSL

    **Delphi RSA 加密与解密使用OpenSSL详解** RSA是一种非对称加密算法,由Ron Rivest、Adi Shamir和Leonard Adleman在1977年提出,因其三位发明者的名字首字母而得名。在Delphi编程环境中,我们可以利用OpenSSL库来...

    易语言调用openssl实现RSA加解密

    为需要的内容不多,主要是RSA加解密部分,所以就删除了不必要的,公钥私钥pem需要就用原来大佬的软件生成吧。(当然附件也打包了一份)。做了一些小修改,主要是演示 RSA有关 加密和解密 安全性的部分。公钥加密,...

    Qt实现RSA加解密

    在本文中,我们将深入探讨如何使用Qt框架来实现RSA加解密,并结合开源库openssl进行数字签名。Qt是一个跨平台的应用程序开发框架,广泛应用于GUI应用程序的开发,同时支持非图形化应用程序。RSA是一种公开密钥加密...

    OpenSSL RSA AES加密解密C++源码

    在C++中使用OpenSSL库进行RSA加密解密,需要完成以下步骤: 1. **生成RSA密钥对**:首先,我们需要创建一个RSA结构体,并生成一对密钥。这通常包括调用`RSA_generate_key_ex()`函数,指定密钥长度(如2048位)和...

Global site tag (gtag.js) - Google Analytics