使用RSA非对称加密完成JavaScript前端分段加密和java后端分段解密

前言
最近研究了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); } }

还有一个辅助类,HexUtil

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类库是我踩过的所有坑中最好的一个了,但是这个东西还是有缺陷的,除了上面我们解决掉的超长数据分段加密,它只能用公钥进行加密,私钥进行解密。由于我只探索到这里就已经完成了自己需要的东西,所以就暂时停止了,如果我解决了后面说的这个问题,就在后续的博客中进行更新。 

不管遇到多少坑,比较安慰的是最终还是搞出来了。写此文章以做记录吧

你可能感兴趣的:(java)