首页 > 编程 > Java > 正文

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

2019-11-08 18:42:25
字体:
来源:转载
供稿:网友

本文出处:http://blog.csdn.net/chaijunkun/article/details/7275632,,我使用的是bcPRov-jdk16-146.jar,这是在JDK1.6环境下使用的。如果需要其他JDK版本下的实现,可以在之前的下载页面中找到对应版本。 下面来看一下我实现的代码: [java] view plain copy 在CODE上查看代码片派生到我的代码片 package net.csdn.blog.chaijunkun;

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.misc.BASE64Decoder;

public class RSAEncrypt {

private static final String DEFAULT_PUBLIC_KEY= "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w" + "/r" + "/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht" + "/r" + "Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg" + "/r" + "XIlk3gdhnzh+uoEQywIDAQAB" + "/r"; private static final String DEFAULT_PRIVATE_KEY= "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG" + "/r" + "6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4" + "/r" + "1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA" + "/r" + "ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm" + "/r" + "u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j" + "/r" + "DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo" + "/r" + "MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ" + "/r" + "DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM" + "/r" + "6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ" + "/r" + "FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA" + "/r" + "2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX" + "/r" + "TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh" + "/r" + "Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo" + "/r" + "1NMLzI2ZfUoX" + "/r"; /** * 私钥 */ private RSAPrivateKey privateKey; /** * 公钥 */ private RSAPublicKey publicKey; /** * 字节数据转字符串专用集合 */ private static final char[] HEX_CHAR= {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * 获取私钥 * @return 当前的私钥对象 */ public RSAPrivateKey getPrivateKey() { return privateKey; } /** * 获取公钥 * @return 当前的公钥对象 */ public RSAPublicKey getPublicKey() { return publicKey; } /** * 随机生成密钥对 */ public void genKeyPair(){ KeyPairGenerator keyPairGen= null; try { keyPairGen= KeyPairGenerator.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } keyPairGen.initialize(1024, new SecureRandom()); KeyPair keyPair= keyPairGen.generateKeyPair(); this.privateKey= (RSAPrivateKey) keyPair.getPrivate(); this.publicKey= (RSAPublicKey) keyPair.getPublic(); } /** * 从文件中输入流中加载公钥 * @param in 公钥输入流 * @throws Exception 加载公钥时产生的异常 */ public void loadPublicKey(InputStream in) throws Exception{ try { BufferedReader br= new BufferedReader(new InputStreamReader(in)); String readLine= null; StringBuilder sb= new StringBuilder(); while((readLine= br.readLine())!=null){ if(readLine.charAt(0)=='-'){ continue; }else{ sb.append(readLine); sb.append('/r'); } } loadPublicKey(sb.toString()); } catch (IOException e) { throw new Exception("公钥数据流读取错误"); } catch (NullPointerException e) { throw new Exception("公钥输入流为空"); } } /** * 从字符串中加载公钥 * @param publicKeyStr 公钥数据字符串 * @throws Exception 加载公钥时产生的异常 */ public void loadPublicKey(String publicKeyStr) throws Exception{ try { BASE64Decoder base64Decoder= new BASE64Decoder(); byte[] buffer= base64Decoder.decodeBuffer(publicKeyStr); KeyFactory keyFactory= KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec= new X509EncodedKeySpec(buffer); this.publicKey= (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("公钥非法"); } catch (IOException e) { throw new Exception("公钥数据内容读取错误"); } catch (NullPointerException e) { throw new Exception("公钥数据为空"); } } /** * 从文件中加载私钥 * @param keyFileName 私钥文件名 * @return 是否成功 * @throws Exception */ public void loadPrivateKey(InputStream in) throws Exception{ try { BufferedReader br= new BufferedReader(new InputStreamReader(in)); String readLine= null; StringBuilder sb= new StringBuilder(); while((readLine= br.readLine())!=null){ if(readLine.charAt(0)=='-'){ continue; }else{ sb.append(readLine); sb.append('/r'); } } loadPrivateKey(sb.toString()); } catch (IOException e) { throw new Exception("私钥数据读取错误"); } catch (NullPointerException e) { throw new Exception("私钥输入流为空"); } } public void loadPrivateKey(String privateKeyStr) throws Exception{ try { BASE64Decoder base64Decoder= new BASE64Decoder(); byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr); PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer); KeyFactory keyFactory= KeyFactory.getInstance("RSA"); this.privateKey= (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("私钥非法"); } catch (IOException e) { throw new Exception("私钥数据内容读取错误"); } catch (NullPointerException e) { throw new Exception("私钥数据为空"); } } /** * 加密过程 * @param publicKey 公钥 * @param plainTextData 明文数据 * @return * @throws Exception 加密过程中的异常信息 */ public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception{ if(publicKey== null){ throw new Exception("加密公钥为空, 请设置"); } Cipher cipher= null; try { cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] output= cipher.doFinal(plainTextData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此加密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; }catch (InvalidKeyException e) { throw new Exception("加密公钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("明文长度非法"); } catch (BadPaddingException e) { throw new Exception("明文数据已损坏"); } } /** * 解密过程 * @param privateKey 私钥 * @param cipherData 密文数据 * @return 明文 * @throws Exception 解密过程中的异常信息 */ public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception{ if (privateKey== null){ throw new Exception("解密私钥为空, 请设置"); } Cipher cipher= null; try { cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] output= cipher.doFinal(cipherData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此解密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; }catch (InvalidKeyException e) { throw new Exception("解密私钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("密文长度非法"); } catch (BadPaddingException e) { throw new Exception("密文数据已损坏"); } } /** * 字节数据转十六进制字符串 * @param data 输入数据 * @return 十六进制内容 */ public static String byteArrayToString(byte[] data){ StringBuilder stringBuilder= new StringBuilder(); for (int i=0; i<data.length; i++){ //取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移 stringBuilder.append(HEX_CHAR[(data[i] & 0xf0)>>> 4]); //取出字节的低四位 作为索引得到相应的十六进制标识符 stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]); if (i<data.length-1){ stringBuilder.append(' '); } } return stringBuilder.toString(); } public static void main(String[] args){ RSAEncrypt rsaEncrypt= new RSAEncrypt(); //rsaEncrypt.genKeyPair(); //加载公钥 try { rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY); System.out.println("加载公钥成功"); } catch (Exception e) { System.err.println(e.getMessage()); System.err.println("加载公钥失败"); } //加载私钥 try { rsaEncrypt.loadPrivateKey(RSAEncrypt.DEFAULT_PRIVATE_KEY); System.out.println("加载私钥成功"); } catch (Exception e) { System.err.println(e.getMessage()); System.err.println("加载私钥失败"); } //测试字符串 String encryptStr= "Test String chaijunkun"; try { //加密 byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), encryptStr.getBytes()); //解密 byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), cipher); System.out.println("密文长度:"+ cipher.length); System.out.println(RSAEncrypt.byteArrayToString(cipher)); System.out.println("明文长度:"+ plainText.length); System.out.println(RSAEncrypt.byteArrayToString(plainText)); System.out.println(new String(plainText)); } catch (Exception e) { System.err.println(e.getMessage()); } }

}

代码中我提供了两种加载公钥和私钥的方式。 按流来读取:适合在Android应用中按ID索引资源得到InputStream的方式; 按字符串来读取:就像代码中展示的那样,将密钥内容按行存储到静态常量中,按String类型导入密钥。

运行上面的代码,会显示如下信息: [plain] view plain copy 在CODE上查看代码片派生到我的代码片 加载公钥成功 加载私钥成功 密文长度:128 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 明文长度:22 54 65 73 74 20 53 74 72 69 6e 67 20 63 68 61 69 6a 75 6e 6b 75 6e 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


上一篇:java实现spark

下一篇:java 泛型基础概念

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表