package org.boyz.rsa.rsajs;
/**
* Created by on 2015/1/6.
*/
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
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;
/**
* RSA 工具类。提供加密,解密,生成密钥对等方法。
* 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
* RSA工具类,主要针对RSA.js使用
* 用法:
* 1、使用generateKeyPair()方法生成密钥文件
* 2、公钥信息给客户端使用
* 3、私钥信息给服务端使用
* 4、客户端公钥加密传给服务端,服务端私钥解密。
* 5、服务端私钥加密传给客户端,客户端公钥解密。
* 6、私钥在任何情况下不能暴露于客户端。
*/
public class RSAUtil {
// 生成密钥文件
private static String RSAKeyStore = "C:/RSAKey.txt";
/**
* 生成密钥
* @return
* @throws Exception
*/
public static KeyPair generateKeyPair() throws Exception {
try {
KeyPairGenerator keyPairGen =
KeyPairGenerator.getInstance("RSA",new BouncyCastleProvider());
final int KEY_SIZE = 512; // 密钥大小
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
System.out.println(keyPair.getPrivate());
System.out.println(keyPair.getPublic());
saveKeyPair(keyPair);
return keyPair;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 实际使用中不会经常生成密钥,而是获取密钥。密钥可以定期更换。
* @return
* @throws Exception
*/
public static KeyPair getKeyPair() throws Exception {
FileInputStream fis = new FileInputStream(RSAKeyStore);
ObjectInputStream oos = new ObjectInputStream(fis);
KeyPair kp = (KeyPair) oos.readObject();
oos.close();
fis.close();
return kp;
}
/**
*
* @param kp
* @throws Exception
*/
public static void saveKeyPair(KeyPair kp) throws Exception {
FileOutputStream fos = new FileOutputStream(RSAKeyStore);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(kp);
oos.close();
fos.close();
}
/**
* 生成公钥
* @param modulus
* @param publicExponent
* @return
* @throws Exception
*/
public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
byte[] publicExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA",new BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPublicKeySpec pubKeySpec =
new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* 生成私钥
* @param modulus
* @param privateExponent
* @return
* @throws Exception
*/
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
byte[] privateExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA",
new BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPrivateKeySpec priKeySpec =
new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* 公钥加密
* @param pk
* @param data
* @return
* @throws Exception
*/
public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA",
new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
// 加密块大小为127
// byte,加密后为128个byte;因此共有2个加密块,第一个127
// byte第二个为1个byte
int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize)
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
else
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
// OutputSize所以只好用dofinal方法。
i++;
}
return raw;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 私钥解密
* @param pk
* @param raw
* @return
* @throws Exception
*/
public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
try {
Cipher cipher =
Cipher.getInstance("RSA",new BouncyCastleProvider());
cipher.init(cipher.DECRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();
ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
int j = 0;
while (raw.length - j * blockSize > 0) {
bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
j++;
}
return bout.toByteArray();
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 私钥加密
* @param pk
* @param data
* @return
* @throws Exception
*/
public static byte[] encrypt(PrivateKey pk, byte[] data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA",
new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
// 加密块大小为127
// byte,加密后为128个byte;因此共有2个加密块,第一个127
// byte第二个为1个byte
int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize)
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
else
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
// OutputSize所以只好用dofinal方法。
i++;
}
return raw;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 公钥解密
* @param pk
* @param raw
* @return
* @throws Exception
*/
public static byte[] decrypt(PublicKey pk, byte[] raw) throws Exception {
try {
Cipher cipher =
Cipher.getInstance("RSA",new BouncyCastleProvider());
cipher.init(cipher.DECRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();
ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
int j = 0;
while (raw.length - j * blockSize > 0) {
bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
j++;
}
return bout.toByteArray();
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 测试
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair().getPublic();
String test = "hello world";
byte[] en_test = encrypt(getKeyPair().getPublic(), test.getBytes());
byte[] de_test = decrypt(getKeyPair().getPrivate(), en_test);
System.out.println(new String(de_test));
}
/* 密钥信息
RSA Private CRT Key
modulus: c1d48e176abd94cd1c575771f89d60932afaa98f6c0ba90667a199f5ca6306ece35695b55cac25583ce9161bcc996f0b329a6c7885c92377f119220fbe9d6c2f
public exponent: 10001
private exponent: 252361da50c464576c7fbbac85b339c6d8ec5042bfb3f83dd6eb5ac18276b8e3a2d0e87c6d5eb8c4dcbffc4c98be1a18d4796ed13191a9964dc364c136286f41
primeP: f78c87c6a7127354f8dcd182e9ae0265ac51fc96a78629352de32a96f4c4e8e1
primeQ: c8728c0f69c1efc0d05ac705872f220d433449e8ab6b5739f2bd395eeb49a70f
primeExponentP: 137e8c9eb73f7bb7a0557b664cd2b83b9b836559d3dd7bd74542d372c9d9cbe1
primeExponentQ: ec0574c5f1515a6d3ee8a4cfed8da21adbb7060fe148533cf885b6b7fd748c3
crtCoefficient: a5a57bbd95a9b8fec4dcb53102accb4f531980499d9499e9b66b965a050db345
RSA Public Key
modulus: c1d48e176abd94cd1c575771f89d60932afaa98f6c0ba90667a199f5ca6306ece35695b55cac25583ce9161bcc996f0b329a6c7885c92377f119220fbe9d6c2f
public exponent: 10001
hello world
*/
}
package org.boyz.rsa.rsajs;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
* Created by [email protected] on 2015/1/6.
* 针对 /rsa/Barrett.js
* /rsa/BigInt.js
* /rsa/RSA.js
* 使用的RSA工具类
*/
public class RSAUtilForJS {
public static void main(String[] args) throws Exception {
String msg = "你好测试你好测试你好测试1111你好测试你好测试你好测试1111111111你好测试你好测试你好测试1111你好测试你好测试你好测试1111111111";
String demsg = RSAUtilForJS.encryptToRsaJS(msg);
System.out.println(demsg);
}
/**
* 私钥解密RSA.js用公钥加密的数据
* 应用于服务器端
* @param input
* @throws Exception
*/
public static String decyrptFromRsaJS(String input) throws Exception {
System.out.println("原始信息:" + input );
String msg = "" ;
String[] results = input.split(" ");
for(int i=0 ;i splitArr = new java.util.ArrayList();
//循环处理62整数倍
for(int i=0 ; i < splitCnt ; i++){
String tmp = input.substring(splitLen * i , splitLen * (i + 1) );
tmp = new StringBuffer().append(tmp).reverse().toString();
tmp = toHexString(tmp);
byte[] t = RSAUtil.encrypt(RSAUtil.getKeyPair().getPrivate() , hexStringToBytes(tmp)); // 私钥加密
String hexStr = byte2hex(t);
splitArr.add(hexStr);
}
//单独处理剩余字符串处理
int splitRem = input.length()%splitLen;
if( splitRem != 0 ){
String tmp = input.substring(input.length() - splitRem);
tmp = new StringBuffer().append(tmp).reverse().toString();
tmp = toHexString(tmp);
byte[] t = RSAUtil.encrypt(RSAUtil.getKeyPair().getPrivate() , hexStringToBytes(tmp)); // 私钥加密
String hexStr = byte2hex(t);
splitArr.add(hexStr);
}
// 打印测试
String output = "";
for(String str : splitArr){
//System.out.print(str);
//System.out.print(" ");
output += str+" " ;
}
return output.substring(0, output.length() - 1);
}
/**
* java字节码转字符串
* @param b
* @return
*/
public static String byte2hex(byte[] b) { //一个字节的数,
// 转成16进制字符串
String hs = "";
String tmp = "";
for (int n = 0; n < b.length; n++) {
//整数转成十六进制表示
tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (tmp.length() == 1) {
hs = hs + "0" + tmp;
} else {
hs = hs + tmp;
}
}
tmp = null;
//return hs.toUpperCase(); //转成大写 JS 端不需要转成大写
return hs;
}
/**
* 字符串转java字节码
* @param b
* @return
*/
public static byte[] hex2byte(byte[] b) {
if ((b.length % 2) != 0) {
throw new IllegalArgumentException("长度不是偶数");
}
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += 2) {
String item = new String(b, n, 2);
// 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个进制字节
b2[n / 2] = (byte) Integer.parseInt(item, 16);
}
b = null;
return b2;
}
public static String toHexString(String s) {
String str = "";
for (int i = 0; i < s.length(); i++) {
int ch = (int) s.charAt(i);
String s4 = Integer.toHexString(ch);
str = str + s4;
}
return str;
}
/**
* 16进制 To byte[]
* @param hexString
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* Convert char to byte
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}
<%--
Created by IntelliJ IDEA.
User: [email protected]
Date: 2015/1/6
Time: 16:32
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
原始数据:
转码数据:
加密数据:
<%--
Created by IntelliJ IDEA.
User: [email protected]
Date: 2015/1/6
Time: 16:32
To change this template use File | Settings | File Templates.
--%>
<%@ page import="org.boyz.rsa.rsajs.RSAUtilForJS" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.net.URLDecoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
JS加密 -> JAVA解密 -> 结果
<%
String msg = request.getParameter("msg");
out.print("传入信息:"+msg+"
");
msg = URLDecoder.decode(msg,"UTF-8");
out.print("转码数据:"+msg+"
");
msg = RSAUtilForJS.decyrptFromRsaJS(msg);
out.print("解密数据:"+msg+"
");
%>
JAVA加密 -> JS解密 -> 举例
<%
String msg2 = "梳理2014年境外媒体、外国政要学者对中国的关注热词,也正是世界读中国的一扇扇窗口。";
out.print("原始信息:"+msg2+"
");
//msg2 = URLEncoder.encode(msg2,"UTF-8"); 此处注意,URL跳转已经转码不要再转码
//out.print("转码信息:"+msg2+"
");
msg2 = RSAUtilForJS.encryptToRsaJS(msg2);
out.print("加密信息:"+msg2+"
");
%>
<%--
Created by IntelliJ IDEA.
User: [email protected]
Date: 2015/1/6
Time: 16:32
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String msg2 = request.getParameter("msg2");
%>
JAVA加密 -> JS解密 -> 结果
资源下载路径:
http://download.csdn.net/detail/csto_sun/8333679