RSA前端加密,java后端解密
官网jsencrypt :JSEncryptencryptlong:encryptlong - npm前端1,安装1.1 安装jsencrypt,执行以下命令npm install jsencrypt --save-dev1.2 安装encryptlong,执行以下命令:npm i encryptlong -S2,创建rsa.js文件2.1 在src/util/文件夹下创建rsa.js文件2.2
官网
jsencrypt :JSEncrypt
encryptlong:encryptlong - npm
前端
1,安装
1.1 安装jsencrypt,执行以下命令
npm install jsencrypt --save-dev
1.2 安装encryptlong,执行以下命令:
npm i encryptlong -S
2,创建rsa.js文件
2.1 在src/util/文件夹下创建rsa.js文件
2.2 引入‘jsencrypt’,‘encryptlong’
/* 产引入jsencrypt实现数据RSA加密 */
import JSEncrypt from 'jsencrypt' // 处理长文本数据时报错 jsencrypt.js Message too long for RSA
/* 产引入encryptlong实现数据RSA加密 */
import Encrypt from 'encryptlong' // encryptlong是基于jsencrypt扩展的长文本分段加解密功能。
2.3 准备publicKey(公钥)&& privateKey(私钥)
1、 密钥对生成地址 http://web.chacuo.net/netrsakeypair
2、Windows系统可以使用git命令行工具
- 单击鼠标右键——git bash here 调出git bash
- 生成私钥,密钥长度为1024bit
$ openssl genrsa -out private.pem 1024
- 把pkcs1格式转为pkcs8格式(java侧使用的是pkcs8)
$ openssl pkcs8 -topk8 -inform PEM -in private.pem -outform pem -nocrypt -out pkcs8.pem
PKCS1的文件头格式 -----BEGIN RSA PRIVATE KEY-----
PKCS8的文件头格式 -----BEGIN PRIVATE KEY----- - 从私钥中提取公钥
-
$ openssl rsa -in pkcs8.pem -pubout -out public.pem
2.4 写完整的加密解密函数
export default {
/* JSEncrypt加密 */
rsaPublicData(data) {
var jsencrypt = new JSEncrypt()
jsencrypt.setPublicKey(publicKey)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
var result = jsencrypt.encrypt(data)
return result
},
/* JSEncrypt解密 */
rsaPrivateData(data) {
var jsencrypt = new JSEncrypt()
jsencrypt.setPrivateKey(privateKey)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
var result = jsencrypt.encrypt(data)
return result
},
/* 加密 */
encrypt(data) {
const PUBLIC_KEY = publicKey
var encryptor = new Encrypt()
encryptor.setPublicKey(PUBLIC_KEY)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
const result = encryptor.encryptLong(data)
return result
},
/* 解密 - PRIVATE_KEY - 验证 */
decrypt(data) {
const PRIVATE_KEY = privateKey
var encryptor = new Encrypt()
encryptor.setPrivateKey(PRIVATE_KEY)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
var result = encryptor.decryptLong(data)
return result
}
}
2.5在main.js主文件引入,将Rsa注册为公共方法,方便其他页面调用
import Rsa from "@/utils/rsa.js"
Vue.prototype.Rsa = Rsa // 将Rsa注册为公共方法,方便其他页面调用
3,在*.vue 页面使用RSA加解密(demo)
由于已经将Rsa注册为全局公共方法,所以在需要的地方通过 this.Rsa.方法名 引入并使用就可以了,具体操作见下面demo
3.1 demo完整代码
<template>
<div class="rsa-container">
<van-row>
<van-col span="24">
<img src="../../assets/img/demo/rsa_banner.jpg" height="220" width="100%"/>
<van-field v-model="rasEncryptData.reqStr" rows="1" autosize label="加密前" type="textarea" placeholder="请输入……" @input="reqTest()"></van-field>
<van-field v-model="rasEncryptData.encryptStr" rows="1" autosize label="加密后" type="textarea" placeholder="请输入……" readonly></van-field>
<van-field v-model="rasEncryptData.decryptStr" rows="1" autosize label="解密后" type="textarea" placeholder="请输入……" readonly></van-field>
</van-col>
</van-row>
</div>
</template>
<script>
export default {
data() { // 定义数据
return {
rasEncryptData: { // 加密后数据
reqStr: '', // 加密前数据
encryptStr: '', // 加密后数据
decryptStr: '' // 解密后数据
}
}
},
methods: { // 定义方法
reqTest() {
this.rasEncryptData.encryptStr = this.Rsa.encrypt(this.rasEncryptData.reqStr) // 加密
this.rasEncryptData.decryptStr = this.Rsa.decrypt(this.rasEncryptData.encryptStr) // 解密
}
},
mounted() { // 此时已经将编译好的模板,挂载到了页面指定的容器中显示
},
filters: {}, // 定义私有过滤器
directives: {}, // 定义私有指令
components: {}, // 定义实例内部私有组件的
beforeCreate() { }, // 实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
created() { }, // 实例已经在内存中创建OK,此时data和methods已经创建OK,此时还没有开始编译模板
beforeMount() { }, // 此时已经完成了模板的编译,但是还没有挂载到页面中
beforeUpdate() { }, // 状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
updated() { }, // 实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
beforeDestroy() { }, // 实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed() { } // Vue实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
}
</script>
4,前端rsa加密,后端去解密(乱码问题)
前端rsa加密,后端去解密,但是呢有中文的时候解密出来就乱了,想了各种办法未果;
结论,既然中文乱码,我就不传中文就行了哈。在加密之前用
这个方法很巧妙的利用了解决url加密的工具,何乐而不为呢。
// 前端加密前将所有中文encoder掉
// 此函数是js原生函数
var en = encodeURIComponent(str);
// 后台再转换回来就行了
String result = java.net.URLDecoder.decode(en ,"UTF-8");
4.1 前端公钥私钥都保存。
本人前端使用密钥时包含 密钥的头部 -----BEGIN PUBLIC KEY-----
后端要去掉头部。
后端
5、JAVA RSA后端加解密工具
package com.epf.gp.gp_admin.utils;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
public class RSAUtil {
// 加密数据和秘钥的编码方式
public static final String UTF_8 = "UTF-8";
// 填充方式
public static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
public static final String RSA_ALGORITHM_NOPADDING = "RSA";
//私钥
public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMsbzWNUjDWM0mgK\n" +
"RbY7yd/4+2JcsR7R1C+1BCWoeYTA9+lZtmSqQEPxj+W8YAmUw+uvNF6ZGOM3bAlt\n" +
"4DCgFVaqBUCPYh3gtUjtDoNd6OhQ1hab/i7HRYq+DU5bBfm0386X/GMqCbimGkIi\n" +
"T8hDxOlSjrl90D6atbfm5aXg3+m/AgMBAAECgYBI+CcU7ZYl0v7wo+ghrV88mR3v\n" +
"W6/Ci834iccApINr30vxyOpPRh8qicmR1tiF+K1eqiZdahFX6FxnEtN9hLXFNhqE\n" +
"Tr2L3/qChICNHJl/ZG7NDJo4vbrDzDHc6GV/OxUoUjVYOABPt/MhjCWyLg3Ywrul\n" +
"+kU+ZnFlNdBtJIjquQJBAObeQ4xfATBkP9hrabgpZoedzHKA3Cj2WWBk1S1QIW/C\n" +
"S3Oe1mTmzi71D2+S6XUsp1IBedkhsRh0VLaJ4m727S0CQQDhN/IHBps+DNX9PPRX\n" +
"XAVejj6qRl4tPNV1YuAej2RLX7DK/qL38zhP0e3C+qVhLnevi4MZWDRghPRs6brj\n" +
"dD4bAkB6ypKw0UMHovpWOGMlYVe4H9TCvgja/JPy8g50KF9wWq3Y1A2B40Scsxi/\n" +
"pirneJWRx1kwhHv13sHDyLiXflwNAkEA2t8NvLo04GaFB6fXJZbuOOgwCjZ8i6YV\n" +
"JBFFES31IDMMkxpHsoOR9DBlyhjf48PO2LDSZS0NKcfL6pRy5V0whQJAIcxeDmSm\n" +
"CIxDd4F+CRvu4aUBvKc/vWAnwtgO+SNboqbv07AjjvDoH4henkcWECPxpDAOPKte\n" +
"tB7VUhbOfBxsNA==";
/**
* 非对称加密,根据公钥和原始内容产生加密内容
*/
private static String encryptRSA(Key key, String plainText)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException,
BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.encodeBase64(cipher.doFinal(plainText.getBytes(UTF_8))).toString();
}
/**
* 根据私钥和加密内容产生原始内容
*/
private static String decryptRSA(Key key, String content) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, DecoderException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] contentArry = content.getBytes();
return new String(cipher.doFinal(Base64.decodeBase64(contentArry)), UTF_8);
}
/**
* 创建RSA的公钥和私钥示例 将生成的公钥和私钥用Base64编码后打印出来
* @throws NoSuchAlgorithmException
*/
public static void createKeyPairs() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
System.out.println("公钥"+Base64.encodeBase64(publicKey.getEncoded()));
System.out.println("私钥"+Base64.encodeBase64(privateKey.getEncoded()));
}
/**
* Description:默认的RSA解密方法 一般用来解密 参数 小数据
*/
public static String decryptRSADefault(String privateKeyStr,String data) {
try{
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM_NOPADDING);
byte[] privateKeyArray = privateKeyStr.getBytes();
byte[] dataArray = data.getBytes();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyArray));
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM_NOPADDING);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decodeBase64(dataArray)), UTF_8);
}catch (Exception e){
e.printStackTrace();
}
return "";
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, DecoderException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, BadPaddingException, InvalidKeyException {
String messageEn = "mLSTatgmYvhk8SGvny5grdYOisiYK2kX4tpnvgRL75GmP1oqjhnQa91u0Ch+/yZnclmKeu6Uid4DEkBBqeQyDj9oeZu8sEsvEHF/yIPbXFe4sKdBC5WbhDEH3TkbCrE/Y0zKQqlfdlbG9nGrbjDCts59w7e1Y/OoVtyryStedJ8=";
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(PRIVATE_KEY);
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
String s = URLDecoder.decode(RSAUtil.decryptRSADefault(RSAUtil.PRIVATE_KEY,messageEn) ,"UTF-8");
System.out.println(s);
}
}
前后端解密不通过情况:
密钥对的获取:主要涉及到两个参数:秘钥格式、秘钥长度
(1)常见秘钥格式:PKCS8(java适用)、PKCS1(其他),如果不是java平台生成的秘钥,有可能报以下错误:
java.security.InvalidKeyException: invalid key format //秘钥格式不合法
(2)秘钥长度:1024、2048(越长越安全,效率越低),详细介绍可以参考 :https://blog.csdn.net/luoluo_onion/article/details/78354799
注意:分段加解密时:1024的秘钥每次加密最大长度为117(128-11),长度为2048秘钥每次加解密长度为256,如果加密时设置的长度不匹配,可能会报以下错误:
javax.crypto.BadPaddingException : Decryption error //解码失败
秘钥获取的代码实现:java.security.KeyPairGenerator提供了生成秘钥的API,只需要调用即可。
public static Map<String, Object> genKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);//2048
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;
}
3、加密实现(公钥加密)
(1)获取Cipher需要的java.security.Key类型公钥;
(2)调用getInstance()方法获取cipher实例,参数为填充方式(加密和解密的填充方式要统一,否则解密失败!)
(3)使用公钥初始化cipher实例,同时设置加解密类型
(4)对数据进行分段加密,得到字节数组。
private static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
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 > 256) {
cache = cipher.doFinal(data, offSet, 256);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * 256;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
4、解密实现
(1)获取Cipher需要的java.security.Key类型私钥;
(2)调用getInstance()方法获取cipher实例,参数为填充方式(加密和解密的填充方式要统一,否则解密失败!)
(3)使用私钥初始化cipher实例,同时设置类型为解密类型
(4)分段解密,得到字节数组。
private static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
// 得到私钥
byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes());
PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key key = keyFactory.generatePrivate(pKCS8EncodedKeySpec);
// 解密数据
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
// SealedObject obj = new SealedObject(data, cipher);
int inputLen = data.length;
System.out.println(data);
System.out.println(inputLen);
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(data, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
//byte[] messageBytes = (byte[]) obj.getObject(cipher);
return decryptedData;
}
5\由于本人是首次接触RSA,并且对加密算法一片空白,所以过程中几乎把能遇见的错误碰了个遍,总结以下几个常见错误,引以为戒:
(1)java.security.InvalidKeyException: invalid key format
秘钥格式不合法,或者是秘钥被修改了。
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big.
秘钥字符串主体前后有空格,或者是换行,或者有多余字段,如(-----BEGIN RSA PRIVATE KEY-----)(秘钥格式不正确)
(3)java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
原因可能是没有使用 PKCS8(java适用)
(4)javax.crypto.BadPaddingException : Decryption error
解密时报这个错误,很可能是填充格式不匹配,请确认加密时使用的填充格式,解密时保持统一即可,我这里使用功能的是RSA/ECB/PKCS1Padding
(5)javax.crypto.BadPaddingException: Message is larger than modulus
解密时出现这个错误(这个错误我是羞于启齿的),可能是因为要解密的字符串中混入了什么奇怪的东西,你可以在解密前打印下要解密的字符串,再打印下转为base64后的字节数组长度(正常应该为128的倍数),否则应该是手残多加了什么东西呢。
前端参考:VUE项目使用RSA加解密(小白版)_江小白写bug的博客-CSDN博客_vue使用rsa加密
密钥生成参考:
(34条消息) 利用jsencrypt.js 进行前端RSA加密,java后端解密_tang89176的博客-CSDN博客hhha
更多推荐
所有评论(0)