前言
最近研究了RSA非对称加密,关于什么是RSA,网上各种文章一搜一大把,但是关于如何使用RSA完成前后端的组合加密解密,东西就非常少了,并且由于RSA的特性,一个1024位的密钥只能加密117位字节数据,当数据量超过117位字节的时候,程序就会抛出异常,下面就给出如何完成前端RSA分段解密和后端RSA分段解密。
准备
前端RSA的JS类库jsencrypt-master 或 https://github.com/travist/jsencrypt
文档中还是有一些地方的坑是需要注意的,比如公钥与私钥Key在用Base64加解密时,不使用直接使用
Base64.encodeBase64String()直接转成字符串,只能用
new String(Base64.encodeBase64(publicKey.getEncoded()))方式
看网上好多人都是采用getModulus()作为公私钥进行加解密,但是我在实际应用中采用此方法发现前端不能解码,后来直接采用传统方式采用getEncoded()作为公私钥就可以了。有待前端大牛解释
后端java的RSA辅助方法类,代码如下:
package com.eshop.oauth.utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* RSA非对称加密辅助类
* 前端使用jsencrypt进行分段加密不支持modulus形式加密只能支持Encode方式加密,
* 即前端使用jsencrypt加密时使用encryptByPublicKey()、encryptByPrivateKey()方法
* 后端进行分段解密
*/
public class RSAUtil {
/** 指定加密算法为DESede */
private static String ALGORITHM = "RSA/ECB/PKCS1Padding";/RSA/ECB/PKCS1Padding
/**
* 加密算法RSA
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 签名算法
*/
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* 获取公钥的key
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 获取私钥的key
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/* RSA最大加密明文大小 */
private static final int MAX_ENCRYPT_BLOCK = 117;
/* RSA最大解密密文大小 */
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
* 指定key的大小(64的整数倍,最小512位) */
private static int KEYSIZE = 1024;
/* 公钥Key字符串 */
public static String PUBLIC_KEY_ENCODED = "RSAPublicKeyEncoded";
/* 私钥Key字符串 */
public static String PRIVATE_KEY_ENCODED = "RSAPrivateEncoded";
/* 公钥模量key */
public static String PUBLIC_MODULUS_KEY = "RSAPublicModulusKey";
/* 公钥指数key */
public static String PUBLIC_EXPONENT_KEY = "RSAPublicExponentKey";
/* 私钥模量key */
public static String PRIVATE_MODULUS_KEY = "RSAPrivateModulusKey";
/* 私钥指数key */
public static String PRIVATE_EXPONENT_KEY = "RSAPrivateExponentKey";
private static Map keyPairMap = new ConcurrentHashMap<>();
private static KeyFactory keyFactory = null;
static {
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
genKeyPair(KEYSIZE);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
public RSAUtil(){
try {
genKeyPair(KEYSIZE);
} catch (Exception e) {
e.printStackTrace();
}
}
public RSAUtil(int keySize){
try {
genKeyPair(keySize);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成密钥对(公钥和私钥)
*
* @return
* @throws Exception
*/
public static Map genKeyPair(int keySize) throws Exception {
if (keySize <= 0) {
keySize = KEYSIZE;
}
/** RSA算法要求有一个可信任的随机数源 */
SecureRandom random = new SecureRandom();
/** 为RSA算法创建一个KeyPairGenerator对象 */
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
/** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
kpg.initialize(keySize, random);
/** 生成密匙对 */
KeyPair kp = kpg.generateKeyPair();
/** 得到公钥 */
Key publicKey = kp.getPublic();
/** 得到私钥 */
Key privateKey = kp.getPrivate();
/** 用字符串将生成的密钥写入文件 */
//keyPairMap.put(PUBLIC_KEY, publicKey);
//keyPairMap.put(PRIVATE_KEY, privateKey);
String algorithm = publicKey.getAlgorithm(); // 获取算法
KeyFactory keyFact = KeyFactory.getInstance(algorithm);
RSAPublicKeySpec keySpec = (RSAPublicKeySpec)keyFact.getKeySpec(publicKey, RSAPublicKeySpec.class);
BigInteger publicModulus = keySpec.getModulus();
BigInteger publicExponent = keySpec.getPublicExponent();
//公钥模量及指数
keyPairMap.put(PUBLIC_MODULUS_KEY, HexUtil.bytes2Hex(publicModulus.toByteArray()));
keyPairMap.put(PUBLIC_EXPONENT_KEY, HexUtil.bytes2Hex(publicExponent.toByteArray()));
// 得到公钥字符串
//keyPairMap.put(PUBLIC_KEY_ENCODED, HexUtil.bytes2Hex(publicKey.getEncoded()));
keyPairMap.put(PUBLIC_KEY_ENCODED, new String(Base64.encodeBase64(publicKey.getEncoded())));
RSAPrivateCrtKeySpec privateKeySpec = (RSAPrivateCrtKeySpec)keyFact.getKeySpec(privateKey, RSAPrivateCrtKeySpec.class);
BigInteger privateModulus = privateKeySpec.getModulus();
BigInteger privateExponent = privateKeySpec.getPrivateExponent();
//私钥模量及指数
keyPairMap.put(PRIVATE_MODULUS_KEY, HexUtil.bytes2Hex(privateModulus.toByteArray()));
keyPairMap.put(PRIVATE_EXPONENT_KEY, HexUtil.bytes2Hex(privateExponent.toByteArray()));
// 得到私钥字符串
//keyPairMap.put(PRIVATE_KEY_ENCODED, HexUtil.bytes2Hex(privateKey.getEncoded()));
keyPairMap.put(PRIVATE_KEY_ENCODED, new String(Base64.encodeBase64(privateKey.getEncoded())));
return keyPairMap;
}
/**
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的公钥对象。
*
* @param hexModulus 系数。
* @param hexExponent 专用指数。
* @return RSA专用公钥对象。
*/
public static RSAPublicKey getRSAPublicKey(String hexModulus, String hexExponent) {
if (isBlank(hexModulus) || isBlank(hexExponent)) {
System.out.println("hexModulus and hexExponent cannot be empty. return null(RSAPublicKey).");
return null;
}
byte[] modulus = null;
byte[] exponent = null;
try {
modulus = HexUtil.hex2Bytes(hexModulus);
//modulus = Base64.decodeBase64(hexModulus);
exponent = HexUtil.hex2Bytes(hexExponent);
} catch (Exception ex) {
System.out.println("hexModulus or hexExponent value is invalid. return null(RSAPublicKey).");
ex.printStackTrace();
}
if (modulus != null && exponent != null) {
return generateRSAPublicKey(modulus, exponent);
}
return null;
}
/**
* 根据给定的系数和专用指数构造一个RSA专用的公钥对象。
*
* @param modulus 系数。
* @param exponent 专用指数。
* @return RSA专用公钥对象。
*/
public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] exponent) {
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus),
new BigInteger(exponent));
try {
return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
} catch (InvalidKeySpecException ex) {
System.out.println("RSAPublicKeySpec is unavailable.");
ex.printStackTrace();
} catch (NullPointerException ex) {
System.out.println("RSAUtil#KEY_FACTORY is null, can not generate KeyFactory instance.");
ex.printStackTrace();
}
return null;
}
/**
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的私钥对象。
*
* @param hexModulus 系数。
* @param hexExponent 专用指数。
* @return RSA专用私钥对象。
*/
public static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexExponent) {
if (isBlank(hexModulus) || isBlank(hexExponent)) {
System.out.println("hexModulus and hexExponent cannot be empty. RSAPrivateKey value is null to return.");
return null;
}
byte[] modulus = null;
byte[] exponent = null;
try {
modulus = HexUtil.hex2Bytes(hexModulus);
//modulus = Base64.decodeBase64(hexModulus);
exponent = HexUtil.hex2Bytes(hexExponent);
} catch (Exception ex) {
System.out.println("hexModulus or hexExponent value is invalid. return null(RSAPrivateKey).");
ex.printStackTrace();
}
if (modulus != null && exponent != null) {
return generateRSAPrivateKey(modulus, exponent);
}
return null;
}
/**
* 根据给定的系数和专用指数构造一个RSA专用的私钥对象。
*
* @param modulus 系数。
* @param exponent 专用指数。
* @return RSA专用私钥对象。
*/
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] exponent) {
RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus),
new BigInteger(exponent));
try {
return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException ex) {
System.out.println("RSAPrivateKeySpec is unavailable.");
ex.printStackTrace();
} catch (NullPointerException ex) {
System.out.println("RSAUtil#KEY_FACTORY is null, can not generate KeyFactory instance.");
ex.printStackTrace();
}
return null;
}
/**
*
* 用私钥对信息生成数字签名
*
*
* @param data 已加密数据
* @param privateKey 私钥(BASE64编码)
*
* @return
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateK);
signature.update(data);
return Base64.encodeBase64String(signature.sign());
}
/**
*
* 校验数字签名
*
*
* @param data 已加密数据
* @param publicKey 公钥(BASE64编码)
* @param sign 数字签名
*
* @return
* @throws Exception
*
*/
public static boolean verify(byte[] data, String publicKey, String sign)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicK = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicK);
signature.update(data);
return signature.verify(Base64.decodeBase64(sign));
}
/**
* 使用给定的公钥加密给定的字符串。
*
* @param publicKey 给定的公钥。
* @param plaintext 字符串。
* @return 给定字符串的密文。
*/
public static String encryptString(Key key, String plaintext) {
if (key == null || plaintext == null) {
return null;
}
byte[] data = plaintext.getBytes();
try {
byte[] en_data = encrypt(key, data);
return new String(Base64.encodeBase64String(en_data));
// return new String(HexUtil.bytes2Hex(en_data));
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 使用指定的公钥加密数据。
*
* @param publicKey 给定的公钥。
* @param data 要加密的数据。
* @return 加密后的数据。
*/
public static byte[] encrypt(Key publicKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHM);
ci.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = ci.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = ci.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
/**
* 使用给定的公钥/私钥解密给定的字符串。
* @param key 给定的公钥或私钥
* @param encryptText 密文
* @return 原文字符串。
*/
public static String decryptString(Key key, String encryptText) {
if (key == null || isBlank(encryptText)) {
return null;
}
try {
byte[] en_data = Base64.decodeBase64(encryptText);
//byte[] en_data = HexUtil.hex2Bytes(encryptText);
byte[] data = decrypt(key, en_data);
return new String(data);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 使用指定的公钥解密数据。
* @param publicKey 指定的公钥
* @param data 要解密的数据
* @return 原数据
* @throws Exception
*/
public static byte[] decrypt(Key publicKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHM);
ci.init(Cipher.DECRYPT_MODE, publicKey);
//return ci.doFinal(data);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = ci.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = ci.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
/**
*
* 私钥解密
*
*
* @param encryptedData 已加密数据
* @param privateKey 私钥(BASE64编码)
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
/**
*
* 公钥解密
*
*
* @param encryptedData 已加密数据
* @param publicKey 公钥(BASE64编码)
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
/**
*
* 公钥加密
*
*
* @param data 源数据
* @param publicKey 公钥(BASE64编码)
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String publicKey)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
/**
*
* 私钥加密
*
*
* @param data 源数据
* @param privateKey 私钥(BASE64编码)
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
/**
* 判断非空字符串
* @param cs 待判断的CharSequence序列
* @return 是否非空
*/
private static boolean isBlank(final CharSequence cs) {
int strLen;
if (cs == null || (strLen = cs.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(cs.charAt(i)) == false) {
return false;
}
}
return true;
}
/**
* 返回公钥Key
* @return
*/
public static String getPublicKey() {
Object obj = keyPairMap.get(PUBLIC_KEY_ENCODED);
if (obj != null) {
return obj.toString();
}
return null;
}
/**
* 返回私钥Key
* @return
*/
public static String getPrivateKey() {
Object obj = keyPairMap.get(PRIVATE_KEY_ENCODED);
if (obj != null) {
return obj.toString();
}
return null;
}
/**
* 返回公钥对的模数/系数
* @return
*/
public static String getPublicKeyModulus() {
Object obj = keyPairMap.get(PUBLIC_MODULUS_KEY);
if (obj != null) {
return obj.toString();
}
return null;
}
/**
*
* @return
*/
public static String getPublicKeyExponent() {
Object obj = keyPairMap.get(PUBLIC_EXPONENT_KEY);
if (obj != null) {
return obj.toString();
}
return null;
}
/**
* 返回私钥对的模数/系数
* @return
*/
public static String getPrivateKeyModulus() {
Object obj = keyPairMap.get(PRIVATE_MODULUS_KEY);
if (obj != null) {
return obj.toString();
}
return null;
}
/**
*
* @return
*/
public static String getPrivateKeyExponent() {
Object obj = keyPairMap.get(PRIVATE_EXPONENT_KEY);
if (obj != null) {
return obj.toString();
}
return null;
}
public static void main(String[] args) {
//new RSAUtil();
/*
String source = "123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!123hello你好!";
System.out.println("RSAUtil.PUBLIC_MODULUS=="+RSAUtil.getPublicKeyModulus());
System.out.println("RSAUtil.PUBLIC_EXPONENT=="+RSAUtil.getPublicKeyExponent());
System.out.println("RSAUtil.PRIVATE_MODULUS=="+RSAUtil.getPrivateKeyModulus());
System.out.println("RSAUtil.PRIVATE_EXPONENT=="+RSAUtil.getPrivateKeyExponent());
//公钥加密,私钥解密
PublicKey publicKey = RSAUtil.getRSAPublicKey(RSAUtil.getPublicKeyModulus(), RSAUtil.getPublicKeyExponent());
String encript = RSAUtil.encryptString(publicKey, source);
System.out.println("加密后数据:"+encript);
PrivateKey privateKey = RSAUtil.getRSAPrivateKey(RSAUtil.getPrivateKeyModulus(), RSAUtil.getPrivateKeyExponent());
String newSource = RSAUtil.decryptString(privateKey, encript);
System.out.println("解密后数据:"+newSource);*/
//私钥加密,公钥解密
// String priKeyStr = RSAUtil.encryptString(privateKey, source);
// System.out.println("加密后数据:"+priKeyStr);
// String oldSource = RSAUtil.decryptString(publicKey, priKeyStr);
// System.out.println("解密后数据:"+oldSource);
try {
test();
}catch (Exception e) {
e.printStackTrace();
}
}
static void test() throws Exception {
String publicKey = RSAUtil.getPublicKey();
String privateKey = RSAUtil.getPrivateKey();
System.out.println("RSAUtil.publicKey=="+publicKey);
System.out.println("RSAUtil.privateKey=="+privateKey);
System.err.println("公钥加密——私钥解密");
String source = "12345678";
//System.out.println("\r加密前文字:\r\n" + source);
byte[] data = source.getBytes();
byte[] str = new String(Base64.encodeBase64(RSAUtil.encryptByPublicKey(data, publicKey))).getBytes();
//System.out.println("加密后文字:\r\n" + new String(Base64.encodeBase64(encodedData)));
//byte[] data = "e2el90IAtp1O9sCPcyeIy1FRv2yjs0wxNYFTZUPw2GaIu16U9QFH5TLft+IL157Kj5ILr/fYRig2VFxUhna6Pr4FL67sD1wqB5SYoO5wgbUBaKf+xUolg2U+KvejyCLX5xbADshbqK6Pv4foo5apmTlz9UB0cF4y4meeqEDODtE=".getBytes();
byte[] encodedData = Base64.decodeBase64(str);
byte[] decodedData = RSAUtil.decryptByPrivateKey(encodedData, privateKey);
String target = new String(decodedData);
System.out.println("解密后文字: \r\n" + target);
}
static void testSign() throws Exception {
String publicKey = RSAUtil.getPublicKey();
String privateKey = RSAUtil.getPrivateKey();
System.err.println("私钥加密——公钥解密");
String source = "这是一行测试RSA数字签名的无意义文字";
System.out.println("原文字:\r\n" + source);
byte[] data = source.getBytes();
byte[] encodedData = RSAUtil.encryptByPrivateKey(data, privateKey);
System.out.println("加密后:\r\n" + Base64.encodeBase64String(encodedData));
byte[] decodedData = RSAUtil.decryptByPublicKey(encodedData, publicKey);
String target = new String(decodedData);
System.out.println("解密后: \r\n" + target);
System.err.println("私钥签名——公钥验证签名");
String sign = RSAUtil.sign(encodedData, privateKey);
System.err.println("签名:\r" + sign);
boolean status = RSAUtil.verify(encodedData, publicKey, sign);
System.err.println("验证结果:\r" + status);
}
static void testHttpSign() throws Exception {
String publicKey = RSAUtil.getPublicKey();
String privateKey = RSAUtil.getPrivateKey();
String param = "id=1&name=张三";
byte[] encodedData = RSAUtil.encryptByPrivateKey(param.getBytes(), privateKey);
System.out.println("加密后:" + encodedData);
byte[] decodedData = RSAUtil.decryptByPublicKey(encodedData, publicKey);
System.out.println("解密后:" + new String(decodedData));
String sign = RSAUtil.sign(encodedData, privateKey);
System.err.println("签名:" + sign);
boolean status = RSAUtil.verify(encodedData, publicKey, sign);
System.err.println("签名验证结果:" + status);
}
}
package com.init.mine.util;
public class HexUtil {
private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* 16进制转byte数组
* @param data 16进制字符串
* @return byte数组
* @throws Exception 转化失败的异常
*/
public static byte[] hex2Bytes(final String data) throws Exception {
final int len = data.length();
if ((len & 0x01) != 0) {
throw new Exception("Odd number of characters.");
}
final byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data.charAt(j), j) << 4;
j++;
f = f | toDigit(data.charAt(j), j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* bytes数组转16进制String
* @param data bytes数组
* @return 转化结果
*/
public static String bytes2Hex(final byte[] data) {
return bytes2Hex(data, true);
}
/**
* bytes数组转16进制String
* @param data bytes数组
* @param toLowerCase 是否小写
* @return 转化结果
*/
public static String bytes2Hex(final byte[] data, final boolean toLowerCase) {
return bytes2Hex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* bytes数组转16进制String
* @param data bytes数组
* @param toDigits DIGITS_LOWER或DIGITS_UPPER
* @return 转化结果
*/
private static String bytes2Hex(final byte[] data, final char[] toDigits) {
final int l = data.length;
final char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return new String(out);
}
/**
* 16转化为数字
* @param ch 16进制
* @param index 索引
* @return 转化结果
* @throws Exception 转化失败异常
*/
private static int toDigit(final char ch, final int index)
throws Exception {
final int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new Exception("Illegal hexadecimal character " + ch
+ " at index " + index);
}
return digit;
}
/*
* 16进制字符串转字符串
*/
public static String hex2String(String hex) throws Exception{
String r = bytes2String(hexString2Bytes(hex));
return r;
}
/*
* 字节数组转字符串
*/
public static String bytes2String(byte[] b) throws Exception {
String r = new String (b,"UTF-8");
return r;
}
/*
* 16进制字符串转字节数组
*/
public static byte[] hexString2Bytes(String hex) {
if ((hex == null) || (hex.equals(""))){
return null;
}
else if (hex.length()%2 != 0){
return null;
}
else{
hex = hex.toUpperCase();
int len = hex.length()/2;
byte[] b = new byte[len];
char[] hc = hex.toCharArray();
for (int i=0; i
具体操作
点击下载【准备】部分的JS类库,完成后解压,获取jsencrypt-master\bin\jsencrypt.js,同级目录下还有一个jsencrypt.min.js,是用于生产环境的,这里我就不做其他操作了,我就只改写jsencrypt.js,也就是我演示需要用到的js文件,将这个js文件拷贝到我们的项目中,打开之后添加一个js方法,
添加一个编码方法此方法在加密JS文件中就,但是我在使用时老提示此方法不存在 ,所以我提出来了,方法看下:
var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad="=";
function hex2b64(h) {
var i;
var c;
var ret = "";
for(i = 0; i+3 <= h.length; i+=3) {
c = parseInt(h.substring(i,i+3),16);
ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
}
if(i+1 == h.length) {
c = parseInt(h.substring(i,i+1),16);
ret += b64map.charAt(c << 2);
}
else if(i+2 == h.length) {
c = parseInt(h.substring(i,i+2),16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
}
while((ret.length & 3) > 0) ret += b64pad;
return ret;
}
如果想自己实现十六进制与二进制转换方法提供如下
/ Convert a hex string to a byte array
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
hex.push((bytes[i] >>> 4).toString(16));
hex.push((bytes[i] & 0xF).toString(16));
}
return hex.join("");
}
添加分段加解密函数(分段解密有两个方法,建议用第二个),方法如下:
JSEncrypt.prototype.encryptLong=function (d){
var k = this.key;
var maxLength = (((k.n.bitLength() + 7) >> 3) - 11);
try {
var lt = "";
var ct = "";
if (d.length > maxLength) {
lt = d.match(/.{1,117}/g);
lt.forEach(function (entry) {
var t1 = k.encrypt(entry);
ct += t1;
});
return hex2b64(ct);
}
var t = k.encrypt(d);
var y = hex2b64(t);
return y;
} catch (ex) {
return false;
}
}
JSEncrypt.prototype.encryptLong2 = function (string) {
var k = this.getKey();
try {
var lt = "";
var ct = "";
//RSA每次加密117bytes,需要辅助方法判断字符串截取位置
//1.获取字符串截取点
var bytes = new Array();
bytes.push(0);
var byteNo = 0;
var len, c;
len = string.length;
var temp = 0;
for (var i = 0; i < len; i++) {
c = string.charCodeAt(i);
if (c >= 0x010000 && c <= 0x10FFFF) {
byteNo += 4;
} else if (c >= 0x000800 && c <= 0x00FFFF) {
byteNo += 3;
} else if (c >= 0x000080 && c <= 0x0007FF) {
byteNo += 2;
} else {
byteNo += 1;
}
if ((byteNo % 117) >= 114 || (byteNo % 117) == 0) {
if (byteNo - temp >= 114) {
bytes.push(i);
temp = byteNo;
}
}
}
//2.截取字符串并分段加密
if (bytes.length > 1) {
for (var i = 0; i < bytes.length - 1; i++) {
var str;
if (i == 0) {
str = string.substring(0, bytes[i + 1] + 1);
} else {
str = string.substring(bytes[i] + 1, bytes[i + 1] + 1);
}
var t1 = k.encrypt(str);
ct += t1;
}
;
if (bytes[bytes.length - 1] != string.length - 1) {
var lastStr = string.substring(bytes[bytes.length - 1] + 1);
ct += k.encrypt(lastStr);
}
return hex2b64(ct);
}
var t = k.encrypt(string);
var y = hex2b64(t);
return y;
} catch (ex) {
return false;
}
};
JSEncrypt.prototype.decryptLong = function (string) {
var k = this.getKey();
var maxLength = ((k.n.bitLength() + 7) >> 3);
//var maxLength = 128;
try {
var str = b64tohex(string);
//var b=hex2Bytes(str);
var inputLen = str.length;
var ct = "";
if (str.length > maxLength) {
var lt = str.match(/.{1,256}/g);
lt.forEach(function (entry) {
var t1 = k.decrypt(entry);
ct += t1;
});
return ct;
}
var y = k.decrypt(b64tohex(string));
return y;
} catch (ex) {
return false;
}
};
JSEncrypt.prototype.decryptLong2 = function (string) {
var k = this.getKey();
// var maxLength = ((k.n.bitLength()+7)>>3);
var MAX_DECRYPT_BLOCK = 128;
try {
var ct = "";
var t1;
var bufTmp;
var hexTmp;
var str = b64tohex(string);
var buf = hexToBytes(str);
var inputLen = buf.length;
//开始长度
var offSet = 0;
//结束长度
var endOffSet = MAX_DECRYPT_BLOCK;
//分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
bufTmp = buf.slice(offSet, endOffSet);
hexTmp = bytesToHex(bufTmp);
t1 = k.decrypt(hexTmp);
ct += t1;
} else {
bufTmp = buf.slice(offSet, inputLen);
hexTmp = bytesToHex(bufTmp);
t1 = k.decrypt(hexTmp);
ct += t1;
}
offSet += MAX_DECRYPT_BLOCK;
endOffSet += MAX_DECRYPT_BLOCK;
}
return ct;
} catch (ex) {
return false;
}
};
在需要解密的地方直接使用私钥解密
/**
* 对前端传递过来的密码 RSA密文进行解密
* @param pwd
* @return
*/
private String decoded(String pwd) {
try {
byte[] encodedData = Base64.decodeBase64(pwd.getBytes());
byte[] decodedData = RSAUtil.decryptByPrivateKey(encodedData, RSAUtil.getPrivateKey());
return new String(decodedData);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
然后在我们的前端页面里面写上具体的加密方式,并调用我们加在jsencrypt.js里面的方法,将加密的数据传输到后台,再调用JAVA辅助类进行解密就OK了,辅助类底部的main方法也写有具体的调用方式,就一句代码的事,就不重复了,这里贴一下前端如何使用JS库加密数据:
function generateEntype(){
var words = "这里写上一句超长的话!";
var publicKey = "your publicKey";//这里的公钥是通过base64加密转化的
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var encryptPwd = encrypt.encryptLong(words);
$.ajax({
url: "store/savePage",
type: 'post',
data: {data:encryptPwd},
dataType: 'json',
//contentType: 'application/json; charset=utf-8',
success: function (data) {
console.info(data);
},
error: function (xhr) {
//console.error('出错了');
}
});
}
补充
由于Base64在编码时会有一些特殊字符出现,所以前端需要时行
encodeURIComponent(encryptData)编码
后台需要采用解码
password.replaceAll("%2B","+")
.replaceAll("%3D","=")
.replaceAll("%2F","/")
.replaceAll("%3F","?")
.replaceAll("%20"," ")
.replaceAll("%23","#")
.replaceAll("%25","%")
.replaceAll("%26","&")
.replaceAll("%2F","/");
写到这里,差不多已经完了,上面的方法以及代码都是自己不断探索总结出来的东西,希望可以帮助到读者。jsencrypt这个JS类库是我踩过的所有坑中最好的一个了,但是这个东西还是有缺陷的,除了上面我们解决掉的超长数据分段加密,它只能用公钥进行加密,私钥进行解密。由于我只探索到这里就已经完成了自己需要的东西,所以就暂时停止了,如果我解决了后面说的这个问题,就在后续的博客中进行更新。
不管遇到多少坑,比较安慰的是最终还是搞出来了。写此文章以做记录吧