来源:搜狐 时间:2023-08-05 09:27:07
因为RSA加密的代码都是比较通用的,所以没有特意去整合,这里参照着两位大神的代码重新写了一遍,做了一些简单的修改,符合本地运行环境
服务端代参照:http://www.cnblogs.com/zhujiabin/p/7118126.html
客户端代码参照:https://jackiedark.github.io/2018/02/05/JSEncrypt长文本分段加解密/
JS加密依赖:jsencrypt.jsGithub地址:https://github.com/travist/jsencrypt可客户端尽量依赖JAVA自带的Jar,只是Base64加密的时候额外依赖了apache的工具类commons-net-3.3.jar
服务端工RSA工具类
package com.wzh.config.utils;import org.apache.commons.net.util.Base64;import org.apache.log4j.Logger;import javax.crypto.Cipher;import java.io.byteArrayOutputStream;import java.security.*;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.HashMap;import java.util.Map;/** * <RSA加密解密工具类> * <额外依赖 commons-net-3.3.jar,日志用的log4j,如果是其他的日志框架可以更改> * @author wzh * @version 2018-12-16 18:20 * @see [相关类/方法] (可选) **/public class RsaUtils{private static Logger log = Logger.getLogger(RsaUtils.class);/** * 块加密大小 */private static final int cache_SIZE = 1024;/** * 加密算法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;/** * Base64字符串解码为二进制数据 * @param base64 * @return 二进制数据 * @throws Exception */public static byte[] decodeBase64(String base64)throws Exception{return Base64.decodeBase64(base64.getbytes());}/** * 二进制数据编码为Base64字符串 * @param bytes * @return Base64字符串 * @throws Exception */public static String encodeBase64(byte[] bytes)throws Exception{return new String(Base64.encodeBase64(bytes));}/** * 生成秘钥对 * @return 返回公钥和私钥的Map集合 * @throws Exception */public static Map<String, Object> initKeyPair()throws Exception{KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGen.initialize(CACHE_SIZE);KeyPair keyPair = keyPairGen.generateKeyPair();RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();Map<String, Object> keyMap = new HashMap<String, Object>(2);// 公钥keyMap.put(PUBLIC_KEY, publicKey);// 私钥keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}/** * 获取私钥 * @param keyMap 秘钥对Map * @return 私钥字符串 * @throws Exception */public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {Key key = (Key) keyMap.get(PRIVATE_KEY);return encodeBase64(key.getEncoded());}/** * 获取公钥字符串 * @param keyMap 秘钥对Map * @return 公钥字符串 * @throws Exception */public static String getPublicKey(Map<String, Object> keyMap) throws Exception {Key key = (Key) keyMap.get(PUBLIC_KEY);return encodeBase64(key.getEncoded());}/** * 使用私钥生成数字签名 * @param data 使用私钥加密的数据 * @param privateKey 是哟啊字符串 * @return 数字签名 * @throws Exception */public static String sign(byte[] data, String privateKey) throws Exception {// 获取byte数组byte[] keyBytes = decodeBase64(privateKey);// 构造PKCS8EncodedKeySpec对象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 encodeBase64(signature.sign());}/** * 校验数字签名 * @param data 私钥加密的数据 * @param publicKey 公钥字符串 * @param sign 私钥生成的签名 * @return 校验成功返回true 失败返回false * @throws Exception */public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {// 获取byte数组byte[] keyBytes = decodeBase64(publicKey);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);// 构造X509EncodedKeySpec对象KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);// 指定的加密算法Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);// 取公钥匙对象PublicKey publicK = keyFactory.generatePublic(keySpec);signature.initVerify(publicK);signature.update(data);// 验证签名是否正常return signature.verify(decodeBase64(sign));}/** * 私钥加密 * @param data 需要加密的数据 * @param privateKey 私钥 * @return 加密后的数据 * @throws Exception */public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {byte[] keyBytes = 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 data 需要加密的数据 * @param publicKey 公钥字符串 * @return 加密后的数据 * @throws Exception */public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {byte[] keyBytes = 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 encryptedData 公钥加密的数据 * @param privateKey 私钥字符串 * @return 私钥解密的数据 * @throws Exception */public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {byte[] keyBytes = 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 公钥字符串 * @return 公钥解密的数据 * @throws Exception */public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {byte[] keyBytes = 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 公钥字符串 * @return 加密后的字符串 */public static String encryptedDataByPublic(String data, String PUBLICKEY) {try {data = encodeBase64(encryptByPublicKey(data.getBytes(), PUBLICKEY));} catch (Exception e) {e.printStackTrace();log.error(e.getMessage(),e);}return data;}/** * 私钥解密方法 * @param data 公钥加密的字符串 * @param PRIVATEKEY 私钥字符串 * @return 私钥解密的字符串 */public static String decryptDataByPrivate(String data, String PRIVATEKEY) {String temp = "";try {byte[] rs = decodeBase64(data);//以utf-8的方式生成字符串temp = new String(decryptByPrivateKey(rs, PRIVATEKEY),"UTF-8");} catch (Exception e) {e.printStackTrace();}return temp;}public static void main(String[] args) {try {Map<String, Object> keyMap = RsaUtils.initKeyPair();String publicKey = RsaUtils.getPublicKey(keyMap);String privateKey = RsaUtils.getPrivateKey(keyMap);System.out.println("公钥:" publicKey);System.out.println("私钥:" privateKey);String source = "我是需要私钥加密的字符串!";System.out.println("签名验证逻辑,私钥加密--公钥解密,需要加密的字符串:" source);byte[] data = source.getBytes();byte[] encodedData = RsaUtils.encryptByPrivateKey(data, privateKey);System.out.println("私钥加密后:" new String(encodedData));String sign = RsaUtils.sign(encodedData, privateKey);System.out.println("签名:" sign);boolean status = RsaUtils.verify(encodedData, publicKey, sign);System.out.println("验证结果:" status);byte[] decodedData = RsaUtils.decryptByPublicKey(encodedData, publicKey);String target = new String(decodedData);System.out.println("公钥解密私钥加密的数据:" target);System.out.println("---------公钥加密----私钥解密----------");// 这里尽量长一点,复制了一段歌词String msg = "月溅星河,长路漫漫,风烟残尽,独影阑珊;谁叫我身手不凡,谁让我爱恨两难,到后来,""肝肠寸断。幻世当空,恩怨休怀,舍悟离迷,六尘不改;且怒且悲且狂哉,是人是鬼是妖怪,不过是,""心有魔债。叫一声佛祖,回头无岸,跪一人为师,生死无关;善恶浮世真假界,尘缘散聚不分明,难断!""我要这铁棒有何用,我有这变化又如何;还是不安,还是氐惆,金箍当头,欲说还休。我要这铁棒醉舞魔,""我有这变化乱迷浊;踏碎灵霄,放肆桀骜,世恶道险,终究难逃。";String ecodeMsg = RsaUtils.encryptedDataByPublic(msg,publicKey);System.out.println("加密后的歌词:" ecodeMsg);String decodeMsg = RsaUtils.decryptDataByPrivate(ecodeMsg,privateKey);System.out.println("解密后的歌词:" decodeMsg);} catch (Exception e) {e.printStackTrace();}}}
首先测试一下工具类,main函数跑一下,成功验证签名,加密,解密
image.png
客户端JS代码,需要JSEncrypt库,前文有给出github地址,这里对这个库做一个简单的扩展,因为RSA长文本超过秘钥长度要报错,所以需要扩展修改下
/** * --------------------------- * 此JS需加载JSEncrypt库的后面,加密解密调用着两个方法 * --------------------------- *//** * 长文本加密 * @param {string} string 待加密长文本 * @returns {string} 加密后的base64编码* */JSEncrypt.prototype.encryptLong = function (string) {var k = this.getKey();try {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) {console.log(ex);return false;}};/** * 长文本解密 * @param {string} string 加密后的base64编码 * @returns {string} 解密后的原文 * */JSEncrypt.prototype.decryptLong = function (string) {var k = this.getKey();var maxLength = 128;try {var string = b64tohex(string);var ct = "";if (string.length > maxLength * 2) {var lt = string.match(/.{1,256}/g);//128位解密。取256位lt.forEach(function (entry) {var t1 = k.decrypt(entry);ct= t1;});return ct;}var y = k.decrypt(string);return y;} catch (ex) {return false;}};function hex2b64(h) {var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /";var b64padchar="=";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= b64padchar;return ret;}
一个简单的测试页面,就不做前后台衔接了,只是在前提用后台生成的公钥进行加密,然后后台main方法解密一下。
<!DOCTYPE html><html><head><title>MyHtml.html</title><meta name="keywords" content="keyword1,keyword2,keyword3"><meta name="description" content="this is my page"><meta name="content-type" content="text/html; charset=UTF-8"><script src="${request.contextPath}/js/jquery-3.3.1.min.js"></script><script src="${request.contextPath}/js/jsencrypt.js?v=123"></script><script src="${request.contextPath}/js/RsaJs.js?v=321123"></script><script type="text/javascript">$(function() {$('#submit').click(function() {var data = $('#msg').val();// 公钥var publickey = $('#publickey').val();// 使用jsencrypt库加密前端参数var jsencrypt = new JSEncrypt();jsencrypt.setPublicKey(publickey);// 这里调用长文本的加密方法var ecodeMsg = jsencrypt.encryptLong(data);$('#ecodeMsg').val(ecodeMsg);});});</script></head><body>需要加密的内容:</br><textarea id="msg" name="msg" rows="10" cols="60"></textarea></br>公钥:</br><textarea id="publickey" rows="10" cols="60"></textarea></br>密文:</br><textarea id="ecodeMsg" rows="10" cols="60"></textarea><br/><br/><input id="submit" type="button" value="加密" /></body></body></html>
简单的测试,页面获取密文
image.png
后台main解密一下
import com.wzh.config.utils.RsaUtils;/** * <一句话功能描述> * <功能详细描述> * @author wzh * @version 2018-12-16 23:31 * @see [相关类/方法] (可选) **/public class RsaManTest {public static void main(String[] args) {String msg = RsaUtils.decryptDataByPrivate("XsM6CYaNhdx2pJXebCgl3g3pF7FX9KrPY gtwgbQs0Q1mqJL4VHqQytxOJfUwXHLP/hLck80AWSctJ29/dB4IQ2mSbcO4OInAJMkPwqWsnh1E9bFlFP2KjQ5RBVngb//IiSgBSFo8NR00y1/h47CrNch6ljW1nCLG82Qk2olhfI=","MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIlXVcewyLHiE9aidGpcR7hMT5opgXTGUfWX2idJnr2bEdzg8Id6FOFgmzAWBkoSfAZodkTgZcOUjc/F/ZDQbuSDSBiakVb6T WK53oVKb8a9Y9Ouq63Bwqihq9kqp9 aLSRcRqyLkF/tGg87dGMEMGeHq2djOI8rrbXGlyQNhAgMBAAECgYAHMCD3MJNwa qp4xrArk 6n5PS97UkzXRgrC/ounuqZM2L/KlaNBE yf1xSIMb7mhY0kMLdv52asE8xQQYaB28WVJHxExgcMDDdhtOp 4WEe2xPWrJSUWSvLQWxrJ01yw9smezt1N0qrA4psJ eq3JP366wZ3GLFhq0BOW8wGQJBANZyo2Dm1aP4aBHfLSa51YQJ izFJyC53Hn43CPhXNDT08GO6tuZ9KiIJkk7rNdDrkAFnR4cDwEZT0C6Fk/VWA8CQQCj8 /fGGV z3D0VCFkPf VlZdNuITFD wzlaalJZq5mYtzdHAZ3AGxpNs Qbykq4TSb5XhfQoZuBeMD3brCv2PAkEA1Wuby4mP3zMOJ5MrtVnG9DSVxU6kxT4T/VO9ivvzSmU2XnDkrY7H3Z46NDHurwHNfivYFSopiJdut2U7ZVJW4wJAc/FsLq7IB9eXH5HnU0Zs2lHBgBrYT7Gre3844WTy6AaZNsOz1UjVXyHaLLTwBkm5SBv8Z3QBzpugitpiZNjQJAR9SqcarmoE7xNxOJ0gqj2Pht26rAfcQogpoVvbvJTgGlpvEnnV2wEX47mzLFewzQ2nxjwAJPdzr rOKXKnDCFA==");System.out.println(msg);}}
前台加密,后台解密成功
相关推荐
猜你喜欢