使用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<String, Object> 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<String, Object> 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;
	}

	/**
	 * <p>
	 * 用私钥对信息生成数字签名
	 * </p>
	 *
	 * @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());
	}

	/**
	 * <p>
	 * 校验数字签名
	 * </p>
	 *
	 * @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;
	}

	/**
	 * <P>
	 * 私钥解密
	 * </p>
	 *
	 * @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;
	}

	/**
	 * <p>
	 * 公钥解密
	 * </p>
	 *
	 * @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;
	}

	/**
	 * <p>
	 * 公钥加密
	 * </p>
	 *
	 * @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;
	}

	/**
	 * <p>
	 * 私钥加密
	 * </p>
	 *
	 * @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<len; i++){
				int p=2*i;
				b[i] = (byte) (charToByte(hc[p]) << 4 | charToByte(hc[p+1]));
			}
			return b;
		}           
	}
 
	/*
	 * 字符转换为字节
	 */
	private static byte charToByte(char c) {
		return (byte) "0123456789ABCDEF".indexOf(c);
	}
	
	 /*
     * 字符串转16进制字符串
     */
    public static String string2HexString(String s) throws Exception{
        String r = bytes2HexString(string2Bytes(s));        
        return r;
    }
    
    /*
     * 字节数组转16进制字符串
     */
    public static String bytes2HexString(byte[] b) {
        String r = "";
        
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r += hex.toUpperCase();
        }
        
        return r;
    }
    
    /*
     * 字符串转字节数组
     */
    public static byte[] string2Bytes(String s){
        byte[] r = s.getBytes();
        return r;
    }
 
 
}

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

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

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
javascript进行RSA加密并用C#后台接收解码。 /// <summary> /// 产生一组RSA公钥、私钥 /// </summary> /// <returns></returns> public static Dictionary<string, string> CreateRsaKeyPair() { var keyPair = new Dictionary<string, string>(); var rsaProvider = new RSACryptoServiceProvider(1024); RSAParameters parameter = rsaProvider.ExportParameters(true); keyPair.Add("PUBLIC", BytesToHexString(parameter.Exponent) + "," + BytesToHexString(parameter.Modulus)); keyPair.Add("PRIVATE", rsaProvider.ToXmlString(true)); keyPair.Add("PUBLICKEY", rsaProvider.ToXmlString(false)); return keyPair; } /// <summary> /// RSA解密字符串 /// </summary> /// <param name="encryptData">密文</param> /// <param name="privateKey">私钥</param> /// <returns>明文</returns> public static string DecryptRSA(string encryptData, string privateKey) { string decryptData = ""; try { var provider = new RSACryptoServiceProvider(); provider.FromXmlString(privateKey); byte[] result = provider.Decrypt(HexStringToBytes(encryptData), false); ASCIIEncoding enc = new ASCIIEncoding(); decryptData = enc.GetString(result); } catch (Exception e) { throw new Exception("RSA解密出错!", e); } return decryptData; } public static string BytesToHexString(byte[] input) { StringBuilder hexString = new StringBuilder(64); for (int i = 0; i < input.Length; i++) { hexString.Append(String.Format("{0:X2}", input[i])); } return hexString.ToString(); } public static byte[] HexStringToBytes(string hex) { if (hex.Length == 0) { return new byte[] { 0 }; } if (hex.Length % 2 == 1) { hex = "0" + hex; } byte[] result = new byte[hex.Length / 2]; for (int i = 0; i < hex.Length / 2; i++) { result[i] = byte.Parse(hex.Substring(2 * i, 2), System.Globalization.NumberStyles.AllowHexSpecifier); } return result; }
RSA算法是一种非对称加密算法,常用于数据加密和数字签名等。在前端使用js实现RSA算法加密后端使用Java实现RSA算法解密,具体步骤如下: 前端(js)实现: 1. 生成RSA密钥对,代码如下: ```javascript function generateRSAKey() { var crypt = new JSEncrypt({ default_key_size: 1024 }); crypt.getKey(); return crypt; } ``` 2. 使用公钥加密数据,代码如下: ```javascript function encryptData(data, publicKey) { var crypt = new JSEncrypt(); crypt.setKey(publicKey); var encryptedData = crypt.encrypt(data); return encryptedData; } ``` 后端(Java)实现: 1. 使用私钥解密数据,代码如下: ```java public static String decryptData(String data, String privateKey) throws Exception { byte[] dataBytes = Base64.decodeBase64(data); byte[] keyBytes = Base64.decodeBase64(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateK); byte[] decryptedData = cipher.doFinal(dataBytes); return new String(decryptedData); } ``` 2. 生成RSA密钥对,代码如下: ```java public static Map<String, String> generateRSAKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, String> keyMap = new HashMap<String, String>(); keyMap.put("publicKey", Base64.encodeBase64String(publicKey.getEncoded())); keyMap.put("privateKey", Base64.encodeBase64String(privateKey.getEncoded())); return keyMap; } ``` 注意:在前端后端使用RSA算法加密解密的时候,需要使用相同的密钥对(即公钥和私钥)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值