RSA-前台加密 后台解密
一.对称与非对称加密
1.对称加密
对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key),这种方法在密码学中叫做对称加密算法。对称加密有很多种算法,由于它效率很高,所以被广泛使用在很多加密协议的核心当中。
2.非对称加密
非对称加密为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人--银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。
二.使用情景说明
由于业务的需要,用户提交的数据需要在用户端进行加密,我们的系统是web形式,所以需要在网页中对用户填写的数据进行加密传输至服务器端,最简单的方式采用对称加密方式,但是不安全,因为加密解密密码一致,在传输过程中密码存在被截取的可能性,于是决定使用非对称加密方式,主要实现思路为:服务器会自动产生公钥-私钥对,用户填写的数据采用公钥加密,加密后的数据传输至服务器端时,在必要时使用对应的私钥进行解密。公钥暴露向客户端,私钥在服务器端,因此整个加密解密的过程是安全可靠的。
这篇文章主要介绍整个加密解密实现过程,采用最具安全性的非对称加密方式-RSA。
三.具体实现-demo
由于代码不可泄露因素,这里放上demo案例,可以实现整个加密解密过程,并支持中文加密解密,两种存放密钥对方式
。
需要下载jar包:
bcprov-jdk14-145.jar(必要)
commons-codec-1.9.jar
commons-io-2.0.1.jar
commons-lang3-3.1.jar
gson-2.3.1.jar (不必要)
commons-codec-1.9.jar
commons-io-2.0.1.jar
commons-lang3-3.1.jar
gson-2.3.1.jar (不必要)
前台加密借用了js
Barrett.js
BigInt.js
RSA.js(本项目对此文件进行了修改,源文件直接在本项目中使用会出现问题)
BigInt.js
RSA.js(本项目对此文件进行了修改,源文件直接在本项目中使用会出现问题)
下载地址为:http://www.ohdave.com/rsa/
整个工程目录如下图所示:
1.后台RSA工具类代码:
package cn.nuohy.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* RSA 加解密 工具类
* @author yangnuo
* 创建时间:2017年7月3日
*/
public class RSAUtil {
/** 算法名称 */
private static final String ALGORITHOM = "RSA";
/**保存生成的密钥对的文件名称。 */
private static final String RSA_PAIR_FILENAME = "/_RSA_ENCRYPTION.txt";
/** 密钥大小 */
private static final int KEY_SIZE = 1024;
/** 默认的安全服务提供者 */
private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
private static KeyPairGenerator keyPairGen = null;
private static KeyFactory keyFactory = null;
/** 缓存的密钥对。 */
private static KeyPair oneKeyPair = null;
private static File rsaPairFile = null;
static {
try {
keyPairGen = KeyPairGenerator.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
keyFactory = KeyFactory.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
} catch (NoSuchAlgorithmException ex) {
}
rsaPairFile = new File(getRSAPairFilePath());
}
private RSAUtil() { }
/**
* 生成并返回RSA密钥对。
*/
private static synchronized KeyPair generateKeyPair() {
try {
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
oneKeyPair = keyPairGen.generateKeyPair();
saveKeyPair(oneKeyPair);
return oneKeyPair;
} catch (InvalidParameterException ex) {
} catch (NullPointerException ex) {
}
return null;
}
/**
* 返回生成/读取的密钥对文件的路径。
*/
private static String getRSAPairFilePath() {
String urlPath = RSAUtil.class.getResource("/").getPath();
return (new File(urlPath).getParent() + RSA_PAIR_FILENAME);
}
/**
* 若需要创建新的密钥对文件,则返回 {@code true},否则 {@code false}。
*/
private static boolean isCreateKeyPairFile() {
// 是否创建新的密钥对文件
boolean createNewKeyPair = false;
if (!rsaPairFile.exists() || rsaPairFile.isDirectory()) {
createNewKeyPair = true;
}
return createNewKeyPair;
}
/**
* 将指定的RSA密钥对以文件形式保存。
*
* @param keyPair 要保存的密钥对。
*/
private static void saveKeyPair(KeyPair keyPair) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = FileUtils.openOutputStream(rsaPairFile);
oos = new ObjectOutputStream(fos);
oos.writeObject(keyPair);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
IOUtils.closeQuietly(oos);
IOUtils.closeQuietly(fos);
}
}
/**
* 返回RSA密钥对。
*/
public static KeyPair getKeyPair() {
// 首先判断是否需要重新生成新的密钥对文件
if (isCreateKeyPairFile()) {
// 直接强制生成密钥对文件,并存入缓存。
return generateKeyPair();
}
if (oneKeyPair != null) {
return oneKeyPair;
}
return readKeyPair();
}
// 同步读出保存的密钥对
private static KeyPair readKeyPair() {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = FileUtils.openInputStream(rsaPairFile);
ois = new ObjectInputStream(fis);
oneKeyPair = (KeyPair) ois.readObject();
return oneKeyPair;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
IOUtils.closeQuietly(ois);
IOUtils.closeQuietly(fis);
}
return null;
}
/**
* 根据给定的系数和专用指数构造一个RSA专用的公钥对象。
*
* @param modulus 系数。
* @param publicExponent 专用指数。
* @return RSA专用公钥对象。
*/
public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) {
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus),
new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
} catch (InvalidKeySpecException ex) {
} catch (NullPointerException ex) {
}
return null;
}
/**
* 根据给定的系数和专用指数构造一个RSA专用的私钥对象。
*
* @param modulus 系数。
* @param privateExponent 专用指数。
* @return RSA专用私钥对象。
*/
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) {
RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus),
new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException ex) {
} catch (NullPointerException ex) {
}
return null;
}
/**
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的私钥对象。
*
* @param modulus 系数。
* @param privateExponent 专用指数。
* @return RSA专用私钥对象。
*/
public static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent) {
if(StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPrivateExponent)) {
return null;
}
byte[] modulus = null;
byte[] privateExponent = null;
try {
modulus = Hex.decodeHex(hexModulus.toCharArray());
privateExponent = Hex.decodeHex(hexPrivateExponent.toCharArray());
} catch(DecoderException ex) {
}
if(modulus != null && privateExponent != null) {
return generateRSAPrivateKey(modulus, privateExponent);
}
return null;
}
/**
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的公钥对象。
*
* @param modulus 系数。
* @param publicExponent 专用指数。
* @return RSA专用公钥对象。
*/
public static RSAPublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent) {
if(StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPublicExponent)) {
return null;
}
byte[] modulus = null;
byte[] publicExponent = null;
try {
modulus = Hex.decodeHex(hexModulus.toCharArray());
publicExponent = Hex.decodeHex(hexPublicExponent.toCharArray());
} catch(DecoderException ex) {
}
if(modulus != null && publicExponent != null) {
return generateRSAPublicKey(modulus, publicExponent);
}
return null;
}
/**
* 使用指定的公钥加密数据。
*
* @param publicKey 给定的公钥。
* @param data 要加密的数据。
* @return 加密后的数据。
*/
public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
ci.init(Cipher.ENCRYPT_MODE, publicKey);
return ci.doFinal(data);
}
/**
* 使用指定的私钥解密数据。
*
* @param privateKey 给定的私钥。
* @param data 要解密的数据。
* @return 原数据。
*/
public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
ci.init(Cipher.DECRYPT_MODE, privateKey);
return ci.doFinal(data);
}
/**
* 使用给定的公钥加密给定的字符串。
* 若 {@code publicKey} 为 {@code null},或者 {@code plaintext} 为 {@code null} 则返回 {@code
* null}。
*
* @param publicKey 给定的公钥。
* @param plaintext 字符串。
* @return 给定字符串的密文。
*/
public static String encryptString(PublicKey publicKey, String plaintext) {
if (publicKey == null || plaintext == null) {
return null;
}
byte[] data = plaintext.getBytes();
try {
byte[] en_data = encrypt(publicKey, data);
return new String(Hex.encodeHex(en_data));
} catch (Exception ex) {
}
return null;
}
/**
* 使用默认的公钥加密给定的字符串。
* 若{@code plaintext} 为 {@code null} 则返回 {@code null}。
*
* @param plaintext 字符串。
* @return 给定字符串的密文。
*/
public static String encryptString(String plaintext) {
if(plaintext == null) {
return null;
}
byte[] data = plaintext.getBytes();
KeyPair keyPair = getKeyPair();
try {
byte[] en_data = encrypt((RSAPublicKey)keyPair.getPublic(), data);
return new String(Hex.encodeHex(en_data));
} catch(NullPointerException ex) {
} catch(Exception ex) {
}
return null;
}
/**
* 使用给定的私钥解密给定的字符串。
* 若私钥为 {@code null},或者 {@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
* 私钥不匹配时,返回 {@code null}。
*
* @param privateKey 给定的私钥。
* @param encrypttext 密文。
* @return 原文字符串。
*/
public static String decryptString(PrivateKey privateKey, String encrypttext) {
if (privateKey == null || StringUtils.isBlank(encrypttext)) {
return null;
}
try {
byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
byte[] data = decrypt(privateKey, en_data);
return new String(data);
} catch (Exception ex) {
}
return null;
}
/**
* 使用默认的私钥解密给定的字符串。
* 若{@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
* 私钥不匹配时,返回 {@code null}。
*
* @param encrypttext 密文。
* @return 原文字符串。
*/
public static String decryptString(String encrypttext) {
if(StringUtils.isBlank(encrypttext)) {
return null;
}
KeyPair keyPair = getKeyPair();
try {
byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
byte[] data = decrypt((RSAPrivateKey)keyPair.getPrivate(), en_data);
return new String(data);
} catch(NullPointerException ex) {
} catch (Exception ex) {
}
return null;
}
/**
* 使用默认的私钥解密由JS加密(使用此类提供的公钥加密)的字符串。
*
* @param encrypttext 密文。
* @return {@code encrypttext} 的原文字符串。
*/
public static String decryptStringByJs(String encrypttext) {
String text = decryptString(encrypttext);
if(text == null) {
return null;
}
return StringUtils.reverse(text);
}
/**
* 与文中参考的博客不同,添加了该方法
* @param privateKey
* @param encrypttext
* @return
*/
public static String decryptStringByJs(RSAPrivateKey privateKey, String encrypttext) {
String text = decryptString(privateKey, encrypttext);
if(text == null) {
return null;
}
return StringUtils.reverse(text);
}
/** 返回已初始化的默认的公钥。*/
public static RSAPublicKey getDefaultPublicKey() {
KeyPair keyPair = getKeyPair();
if(keyPair != null) {
return (RSAPublicKey)keyPair.getPublic();
}
return null;
}
/** 返回已初始化的默认的私钥。*/
public static RSAPrivateKey getDefaultPrivateKey() {
KeyPair keyPair = getKeyPair();
if(keyPair != null) {
return (RSAPrivateKey)keyPair.getPrivate();
}
return null;
}
public static void main(String[] args) throws DecoderException {
RSAPublicKey publicKey = getDefaultPublicKey();
RSAPrivateKey privateKey = getDefaultPrivateKey();
System.out.println("publicKey-->"+publicKey);
System.out.println("privateKey-->"+privateKey);
String data = "123456";
//加密
String codeString = encryptString(publicKey, data);
System.out.println(codeString);
//解密
System.out.println(decryptString(privateKey, codeString));
}
}
2.controller层代码:demo中使用了servlet
package cn.nuohy.demo;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/rsaser")
public class RsaServlet extends HttpServlet {
public RsaServlet() {
super();
}
public void destroy() {
super.destroy();
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
String worktype = request.getParameter("worktype");
if(worktype!=null){
//获取加密所需的参数信息
if(worktype.equals("turn")){
RSAPublicKey publicKey = RSAUtil.getDefaultPublicKey();
RSAPrivateKey privateKey = RSAUtil.getDefaultPrivateKey();
Rsaobj rsaobj = new Rsaobj(publicKey, privateKey);
String publicExponent_16 = new BigInteger(publicKey.getPublicExponent().toString(), 10).toString(16);
String modulus_16 = new BigInteger(publicKey.getModulus().toString(), 10).toString(16);
System.out.println("公钥 16 getModulus:"+modulus_16);
System.out.println("公钥 16 getPublicExponent:"+publicExponent_16);
System.out.println("私钥 getModulus :"+privateKey.getModulus());
System.out.println("私钥 getPrivateExponent :"+privateKey.getPrivateExponent());
//将私钥的 Exponent Modulus存放至变量中,以便后来构造私钥
//MyUtil.PrivateExponent = privateKey.getPrivateExponent();
//MyUtil.PrivateModulus = privateKey.getModulus();
request.getSession().setAttribute("modulus_16",modulus_16);
request.getSession().setAttribute("publicExponent_16",publicExponent_16);
request.getSession().setAttribute("rsaobj",rsaobj);
request.getRequestDispatcher("./demo.jsp").forward(request, response);
//提交加密后的内容 进行解密
}else if(worktype.equals("decrypt")){
System.out.println("----------decrypt------------");
String encrypt_password = request.getParameter("password");
String unencrypt_password = request.getParameter("unpassword");
System.out.println("未经过加密的数据为:unencrypt_password:"+unencrypt_password);
System.out.println("前台加密后的数据为:encrypt_password:"+encrypt_password);
Object object = request.getSession().getAttribute("rsaobj");
if(object!=null){
Rsaobj rsaobj = (Rsaobj) object;
//从 session 中获取私钥
//RSAPrivateKey privateKey = rsaobj.getPrivateKey();
//从变量中获取数据 构造私钥
/*RSAPrivateKey privateKey = new RSAPrivateKey() {
@Override
public BigInteger getModulus() {
return MyUtil.PrivateModulus;
}
@Override
public String getFormat() {
return null;
}
@Override
public byte[] getEncoded() {
return null;
}
@Override
public String getAlgorithm() {
return null;
}
@Override
public BigInteger getPrivateExponent() {
return MyUtil.PrivateExponent;
}
};*/
String decrypt_password = RSAUtil.decryptString(privateKey, encrypt_password);
//对字符串进行URL解码的编码处理
decrypt_password = java.net.URLDecoder.decode(new String(decrypt_password) ,"UTF-8");
System.out.println("解密后的数据为:"+ decrypt_password);
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("content", new String(decrypt_password));
MyUtil.writeToJson(map, response);
}
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
public void init() throws ServletException {
}
}
3.前台页面代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>前端加密-后端解密</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript" src="./js/rsa/RSA.js?<%=new Date() %>"></script>
<script type="text/javascript" src="./js/rsa/BigInt.js?<%=new Date() %>"></script>
<script type="text/javascript" src="./js/rsa/Barrett.js?<%=new Date() %>>"></script>
<script type="text/javascript" src="./js/jquery-2.1.1.min.js"></script>
</head>
<body>
<input id="modulus" type="hidden" value="${modulus_16 }" />
<input id="publicExponent" type="hidden" value="${publicExponent_16 }" />
<script type="text/javascript">
var key ;
function descptcontent() {
var thisPwd = $("#password").val();
var publicExponent = $("#publicExponent").val();
var modulus = $("#modulus").val();
setMaxDigits(130);
var key = new RSAKeyPair(publicExponent, "", modulus);
//对密码进行加密
var password = encryptedString(key, encodeURIComponent(thisPwd));
$.ajax({
url : '<%=basePath%>rsaser',
type : 'POST',
data : {"worktype":"decrypt","password":password,"unpassword":thisPwd},
success : function (returnData){
$("#showcontent").val(returnData.content);
},
error : function(returndata) {
alert("请检查网络!");
}
});
}
</script>
需要加密的数据:<input type='text' name="password" id="password" style='width:400px' value="123456" /> <input id="submitcontent" type="button" value="提交" οnclick="descptcontent()" />
<br/>
<br/>
解密后的内容:<input readonly="readonly" id="showcontent" type="text" value="">
</body>
</html>
四.代码说明
1. 前台代码中:
在new RSAKeyPair之前,必须先调用setMaxDigits()函数,setMaxDigits()的参数值根据选用的RSA的Key大小
不同1024位的Key,
应该设置setMaxDigits(130);如果是2048位的则应该设置为260
中文乱码问题通过原生js 方法 encodeURIComponent();可以对中文进行url编码后再进行加密传输。
通过servlet来访问页面,公钥会放在session中,页面会直接获取到session中存放的公钥,拿到公钥后,调用
encryptedString(key, encodeURIComponent(thisPwd)); 对内容进行加密。
2.servlet中存放密钥对有两种,最简单的就是自定义一个密钥对类Rsaobj(有两个属性 RSAPublicKey RSAPrivateKey),产生密钥对后,将密钥对放置到session等中,前台需要解密调用后台时,后台直接获取session中密钥的私钥后即可进行对数据进行解密。当然这种方式只能测试用,因为对于大部分的密钥可能不知能够什么时候需要解密,如果存放至变量或内存中显然不行,需要我们存储至另外一个地方永久存储住,那么最通俗的就直接用数据库存放,问题来了,数据库怎样存放密钥对呢,代码中已经给出了,公钥只需要记录两个属性即可:
publicKey.getPublicExponent();
publicKey.getModulus();
这里提一下:这两个方法获取的数据是10进制的,需要转化为16进制,
String publicExponent_16 = new BigInteger(publicKey.getPublicExponent().toString(), 10).toString(16);
String modulus_16 = new BigInteger(publicKey.getModulus().toString(), 10).toString(16);
String modulus_16 = new BigInteger(publicKey.getModulus().toString(), 10).toString(16);
私钥也只需要存放两个属性:
privateKey.getModulus();
privateKey.getPrivateExponent();
privateKey.getPrivateExponent();
这四个属性就构成了一个密钥组
私钥通过构造方法可以产生,代码也给出了,为了简便这里没有存放至数据库,用静态变量代替了数据库。
RSAPrivateKey privateKey = new RSAPrivateKey() {
@Override
public BigInteger getModulus() {
return MyUtil.PrivateModulus;
}
@Override
public String getFormat() {
return null;
}
@Override
public byte[] getEncoded() {
return null;
}
@Override
public String getAlgorithm() {
return null;
}
@Override
public BigInteger getPrivateExponent() {
return MyUtil.PrivateExponent;
}
};
由于前台防止中文乱码,对提交的数据进行url编码,所以解密后的数据需要解码:
decrypt_password = java.net.URLDecoder.decode(new String(decrypt_password) ,"UTF-8");
效果如下图所示:
针对实时产生密钥对进行补充:!!!
上述使用的RSA工具类使用了文本进行缓存,如果不需要缓存,如何实时进行产生呢?
附上代码:
RsaEncryptionUtil.java
package cn.nuohy.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import cn.nuohy.entity.KeyTwin;
/**
* RSA 加解密 工具类
* @author yangnuo
* 创建时间:2017年7月3日
*/
public class RsaEncryptionUtil {
/** 算法名称 */
private static final String ALGORITHOM = "RSA";
/** 密钥大小 */
private static final int KEY_SIZE = 1024;
/** 默认的安全服务提供者 */
private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
private static KeyPairGenerator keyPairGen = null;
private static KeyFactory keyFactory = null;
static {
try {
keyPairGen = KeyPairGenerator.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
keyFactory = KeyFactory.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
} catch (NoSuchAlgorithmException ex) {
}
}
private RsaEncryptionUtil() {
}
/**
* 生成并返回RSA密钥对。
*/
private static synchronized KeyPair generateKeyPair() {
try {
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
return keyPairGen.generateKeyPair();
} catch (InvalidParameterException ex) {
} catch (NullPointerException ex) {
}
return null;
}
/**
* 返回RSA密钥对。
*/
public static KeyPair getKeyPair() {
return generateKeyPair();
}
/*********************************************************************** 加 密 *******************************************************************************************/
/**
* 使用指定的公钥加密数据。
*
* @param publicKey 给定的公钥。
* @param data 要加密的数据。
* @return 加密后的数据。
*/
public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
ci.init(Cipher.ENCRYPT_MODE, publicKey);
return ci.doFinal(data);
}
/**
* 使用给定的公钥加密给定的字符串。
* 若 {@code publicKey} 为 {@code null},或者 {@code plaintext} 为 {@code null} 则返回 {@code
* null}。
*
* @param publicKey 给定的公钥。
* @param plaintext 字符串。
* @return 给定字符串的密文。
*/
public static String encryptString(PublicKey publicKey, String plaintext) {
if (publicKey == null || plaintext == null) {
return null;
}
byte[] data = plaintext.getBytes();
try {
byte[] en_data = encrypt(publicKey, data);
return new String(Hex.encodeHex(en_data));
} catch (Exception ex) {
}
return null;
}
/*********************************************************************** 解 密 *******************************************************************************************/
/**
* 使用指定的私钥解密数据。
*
* @param privateKey 给定的私钥。
* @param data 要解密的数据。
* @return 原数据。
*/
public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
ci.init(Cipher.DECRYPT_MODE, privateKey);
return ci.doFinal(data);
}
/**
* 使用给定的私钥解密给定的字符串。
* 若私钥为 {@code null},或者 {@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
* 私钥不匹配时,返回 {@code null}。
*
* @param privateKey 给定的私钥。
* @param encrypttext 密文。
* @return 原文字符串。
*/
public static String decryptString(PrivateKey privateKey, String encrypttext) {
if (privateKey == null || StringUtils.isBlank(encrypttext)) {
return null;
}
try {
byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
byte[] data = decrypt(privateKey, en_data);
return new String(data);
} catch (Exception ex) {
}
return null;
}
/**
* 通过密钥 解密经过js加密的内容
* @param privateKey
* @param encrypttext
* @return
*/
public static String decryptStringByJs(RSAPrivateKey privateKey, String encrypttext) {
String text = decryptString(privateKey, encrypttext);
if(text == null) {
return null;
}
return StringUtils.reverse(text);
}
/******************************************************************* 构造 RSA ***********************************************************************************************/
/**
* 根据给定的系数和专用指数构造一个RSA专用的公钥对象。
*
* @param modulus 系数。
* @param publicExponent 专用指数。
* @return RSA专用公钥对象。
*/
public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) {
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus),
new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
} catch (InvalidKeySpecException ex) {
} catch (NullPointerException ex) {
}
return null;
}
/**
* 根据给定的系数和专用指数构造一个RSA专用的私钥对象。
*
* @param modulus 系数。
* @param privateExponent 专用指数。
* @return RSA专用私钥对象。
*/
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) {
RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus),
new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException ex) {
} catch (NullPointerException ex) {
}
return null;
}
/**
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的私钥对象。
*
* @param modulus 系数。
* @param privateExponent 专用指数。
* @return RSA专用私钥对象。
*/
public static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent) {
if(StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPrivateExponent)) {
return null;
}
byte[] modulus = null;
byte[] privateExponent = null;
try {
modulus = Hex.decodeHex(hexModulus.toCharArray());
privateExponent = Hex.decodeHex(hexPrivateExponent.toCharArray());
} catch(DecoderException ex) {
}
if(modulus != null && privateExponent != null) {
return generateRSAPrivateKey(modulus, privateExponent);
}
return null;
}
/**
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的公钥对象。
*
* @param modulus 系数。
* @param publicExponent 专用指数。
* @return RSA专用公钥对象。
*/
public static RSAPublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent) {
if(StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPublicExponent)) {
return null;
}
byte[] modulus = null;
byte[] publicExponent = null;
try {
modulus = Hex.decodeHex(hexModulus.toCharArray());
publicExponent = Hex.decodeHex(hexPublicExponent.toCharArray());
} catch(DecoderException ex) {
}
if(modulus != null && publicExponent != null) {
return generateRSAPublicKey(modulus, publicExponent);
}
return null;
}
/*********************************************************************** 测 试 *******************************************************************************************/
public static void main(String[] args) throws DecoderException {
KeyPair keyPair = generateKeyPair();
RSAPublicKey rsapublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaprivateKey = (RSAPrivateKey) keyPair.getPrivate();
final KeyTwin keyTwin = new KeyTwin(rsapublicKey.getModulus().toString(), rsapublicKey.getPublicExponent().toString(), rsaprivateKey.getModulus().toString(), rsaprivateKey.getPrivateExponent().toString());
System.out.println(keyTwin.toString());
String data = "落花雨abc123456";
//构造 公钥
RSAPublicKey publicKey= new RSAPublicKey() {
@Override
public BigInteger getModulus() {
return new BigInteger(keyTwin.getPulicmodulus());
}
@Override
public String getFormat() {
return null;
}
@Override
public byte[] getEncoded() {
return null;
}
@Override
public String getAlgorithm() {
return null;
}
@Override
public BigInteger getPublicExponent() {
return new BigInteger(keyTwin.getPublicexponent());
}
};
String encryptString = encryptString(publicKey, data);
System.out.println("加密后的字符串为:"+encryptString);
//构造私钥
RSAPrivateKey privateKey = new RSAPrivateKey() {
@Override
public BigInteger getModulus() {
return new BigInteger(keyTwin.getPrivatemodulus());
}
@Override
public String getFormat() {
return null;
}
@Override
public byte[] getEncoded() {
return null;
}
@Override
public String getAlgorithm() {
return null;
}
@Override
public BigInteger getPrivateExponent() {
return new BigInteger(keyTwin.getPrivateexponent());
}
};
String decryptString = decryptString(privateKey, encryptString);
System.out.println("解密后的字符串为:"+decryptString);
/* KeyPair keyPair = generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
System.out.println("publicKey-->"+publicKey);
System.out.println("privateKey-->"+privateKey);
String data = "落花雨abc123456";
//加密
String codeString = encryptString(publicKey, data);
System.out.println(codeString);
//解密
System.out.println(decryptString(privateKey, codeString));*/
}
}
实体类:KeyTwin.java
package cn.nuohy.entity;
/**
* 密钥对
* @author yangnuo
* 创建时间:2017年7月4日
*/
public class KeyTwin {
/** 主键 */
private int id;
/** 公钥 modules */
private String pulicmodulus;
/** 公钥exponent */
private String publicexponent;
/** 私钥 modules */
private String privatemodulus;
/** 私钥exponent */
private String privateexponent;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPulicmodulus() {
return pulicmodulus;
}
public void setPulicmodulus(String pulicmodulus) {
this.pulicmodulus = pulicmodulus;
}
public String getPublicexponent() {
return publicexponent;
}
public void setPublicexponent(String publicexponent) {
this.publicexponent = publicexponent;
}
public String getPrivatemodulus() {
return privatemodulus;
}
public void setPrivatemodulus(String privatemodulus) {
this.privatemodulus = privatemodulus;
}
public String getPrivateexponent() {
return privateexponent;
}
public void setPrivateexponent(String privateexponent) {
this.privateexponent = privateexponent;
}
public KeyTwin(int id, String pulicmodulus, String publicexponent,
String privatemodulus, String privateexponent) {
super();
this.id = id;
this.pulicmodulus = pulicmodulus;
this.publicexponent = publicexponent;
this.privatemodulus = privatemodulus;
this.privateexponent = privateexponent;
}
public KeyTwin(String pulicmodulus, String publicexponent,
String privatemodulus, String privateexponent) {
super();
this.pulicmodulus = pulicmodulus;
this.publicexponent = publicexponent;
this.privatemodulus = privatemodulus;
this.privateexponent = privateexponent;
}
public KeyTwin() {
super();
}
@Override
public String toString() {
return "KeyTwin [id=" + id + ", pulicmodulus=" + pulicmodulus
+ ", publicexponent=" + publicexponent + ", privatemodulus="
+ privatemodulus + ", privateexponent=" + privateexponent + "]";
}
}
最后附上源码下载链接: http://download.csdn.net/detail/qq_27063119/9887901
(整个网上关于RSA前台加密后台解密的文章也有不少,不过都是有问题的,本人也花了不少时间才弄出来了,8个金币支持下,无金币可以至邮箱:nuohy@qq.com )
--感谢您的阅读