使用jsencrypt.js进行RSA加密
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用jsencrypt.js进行RSA加密相关的知识,希望对你有一定的参考价值。
参考技术A 一个基于RSA加解密的js库使用公钥结合 jsencrypt 提供的 encrypt 方法(需要加密的内容)进行加密
使用私钥结合 jsencrypt 提供的 decrypt 方法进行解密
jsencrypt.js加密java后端解密
文章目录
1.什么是RSA?
计算机中常用的加密技术分为两类:对称加密、非对称加密。
RSA属于非对称加密。加密、解密过程使用不同的秘钥,分为公钥、私钥。公钥可以公开,私钥不可以。
对称加密:加密和解密使用相同的的秘钥Key,这个Key需要在网络上传输,不安全,因此需要非对称加密。
2.RSA算法
2.1 生成公钥和私钥
(1)随意选择两个大的素数P和Q,P不等于Q;
(2)令 N = P × Q 、 T = ( P − 1 ) × ( Q − 1 ) N = P \\times Q、T = (P - 1) \\times (Q - 1) N=P×Q、T=(P−1)×(Q−1);
(3)选择一个整数E作为秘钥,需要满足:gcd(E, T)=1 && E<T;
(4)根据 ( D × E ) m o d T = 1 (D \\times E) \\ mod \\ T = 1 (D×E) mod T=1,计算出D,作为另一个秘钥;
(5)使用PK=(N、E)作为公钥、SK=(N, D)作为私钥(当然可以反过来)。
2.2 使用公钥加密信息
- 使用PK=(N、E)公钥加密信息。
- 若明文为M,则密文C可以按照如下计算得到(要求M<N)
2.3 使用私钥解密信息
-
使用SK=(N, D)私钥解密信息。
-
如密文为C,则明文M可以按照如下计算得到:
4.RSA的应用:数字签名
数字签名是实现安全交易的核心技术之一,实现基础是RSA加密技术。
数字签名类似于我们生活中的手写签名,必须保证签名的人事后不能抵赖,同时不能让别人伪造我们的签名。因此数字签名需要保证:
-
(1)发送者事后不能抵赖对报文的签名;
-
(2)接受者不能伪造对报文的签名。
如果A向B发送报文M,A手中有私钥,公钥是公开的,A给M使用私钥进行加密再发给B即可。
这样即可保证上述两点:
- (1)因为只有A可以对M使用私钥进行加密,A不能抵赖;
- (2)B用公钥可以得到原始信息M,如果伪造成M’,则A可以证明其伪造了信息。
在解密之前我们先进行验签操作,如果验签失败,则认为内容是伪造,不进行解密。
5.RSA的安全性
RSA算法的安全性依赖于大数分解。因此为了保证安全性,需要使得P、Q非常大。
因为数据很大,又牵涉到幂次运算,因此计算量很大。
6.为什么要写这文章?
因为我想做一个数据加密,就开始了解Rsa加密算法,实现一个前端加密,后端解密的一个过程,然后我也不想重复造轮子,就上百度搜索,发现好多文章都是抄来抄去去的根本不合适。所以只好自己写了一个记录,以便到时候要用的时候再看,前端使用jsencrypt.js进行加密,然后在使用的时候我发现这个内容过长无法加密,然后又找了个扩展的,可以使用长内容加密,但是到后台解密的时候出问题了,无法解密出正确内容,我只好换回原理的版本,然后自己重写了两个方法来进行扩展,实现长内容加密。这个改进后的方法按理论可以进行很大文本文本加密传输,但是具体能承载多长我没有测试过,尽量不要使用rsa加密来加密比较长的文本,因为rsa加密是比较麻烦的,复杂度比较高,在加密之前也要考虑那些数据需要加密,那些数据不需要加密,不要通通都加密,这样解密的时候会浪费许多时间,增加服务器的压力,访问时间过长。
因为Rsa加密算法还是稍微复杂,我就不过的阐述,直接看源码吧,先复制过去能跑,然后再自己上网搜索,相关内容进行脑补,这个改进估计会存在问题,因为加密的长度限制117,和每次加密后的内容长度172 ,我不太清楚是不是固定的,我尝试了很多次都没有出现问题,等出现问题再解决吧。
7.前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<script src="../js/jsencrypt.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/jquery1.42.min.js" type="text/javascript" charset="utf-8"></script>
<body>
<script type="text/javascript">
/**
* 对加密工具进行封装
*/
let RsaUtil =
publicKey: '',
privateKey: '',
rsaEncrypt: ,
// 长文本加密
encryptLong: function(content)
let encryptionSectionSize = 117;
content = encodeURIComponent(content);
let ciphertext = '';
if (content && content.length > encryptionSectionSize)
let paragraphic = content.length / encryptionSectionSize;
let isIntger = (paragraphic | 0) === paragraphic;
let encryptionCountet = parseInt(paragraphic);
if (isIntger)
for (let i = 0; i < encryptionCountet; i++)
ciphertext += this.rsaEncrypt.encrypt(content.substring(i * encryptionSectionSize, (i * encryptionSectionSize) +
encryptionSectionSize));
else
let lastContentLength = content.length % encryptionSectionSize;
for (let i = 0; i < encryptionCountet; i++)
ciphertext += this.rsaEncrypt.encrypt(content.substring(i * encryptionSectionSize, (i * encryptionSectionSize) +
encryptionSectionSize));
ciphertext += this.rsaEncrypt.encrypt(content.substring(encryptionCountet * encryptionSectionSize,
encryptionCountet *
encryptionSectionSize + lastContentLength));
else
ciphertext = this.rsaEncrypt.encrypt(content);
return ciphertext;
,
// 长文本解密
decryptLong: function(content)
let decodeSectionSize = 172;
let decodeText = '';
let isFinish = false;
if (content && content.length > decodeSectionSize)
let paragraphic = content.length / decodeSectionSize;
let isIntger = (paragraphic | 0) === paragraphic;
let encryptionCountet = parseInt(paragraphic);
if (isIntger)
for (let i = 0; i < encryptionCountet; i++)
decodeText += this.rsaEncrypt.decrypt(content.substring(i * decodeSectionSize, (i * decodeSectionSize) +
decodeSectionSize));
isFinish = decodeText.indexOf('false') !== -1;
if (isFinish)
return isFinish;
else
let lastContentLength = content.length % decodeSectionSize;
for (let i = 0; i < encryptionCountet; i++)
decodeText += this.rsaEncrypt.encrypt(content.substring(i * decodeSectionSize, (i * decodeSectionSize) +
decodeSectionSize));
isFinish = decodeText.indexOf('false') !== -1;
if (isFinish)
return isFinish;
decodeText += this.rsaEncrypt.encrypt(content.substring(encryptionCountet * decodeSectionSize,
encryptionCountet *
decodeSectionSize + lastContentLength));
else
decodeText = this.rsaEncrypt.decrypt(content);
return decodeURIComponent(decodeText);
,
// 小于117长度的字符串加密
encrypt: function(content)
return this.encrypt(content);
,
// 小于117长度的字符串解密
decrypt: function(content)
return this.decrypt(content);
,
// 初始化RsaUtil工具对象
init: function(publicKey, privateKey)
if (!publicKey)
throw new Error('publicKey cant’t null');
let rsaEncrypt = new JSEncrypt();
rsaEncrypt.setPublicKey(publicKey);
if (privateKey)
rsaEncrypt.setPrivateKey(privateKey);
this.publicKey = publicKey;
this.privateKey = privateKey;
this.rsaEncrypt = rsaEncrypt;
async function getPublicKey()
return new Promise((resolve, reject) =>
$.ajax(
url: "http://localhost:8080/rsa/getPublicKey",
type: "GET",
data: ,
success: function(data, textStatus)
if (data && data.code === 200 && data.content.length > 0)
resolve(data.content);
else
reject(false);
,
error: function(error)
reject(false);
);
)
async function privateKeyDecode(content)
return new Promise((resolve, reject) =>
$.ajax(
url: "http://localhost:8080/rsa/privateKeyDecode",
type: "POST",
contentType: "application/json",
dataType: "json",
data: JSON.stringify(
content
),
success: function(data, textStatus)
if (data && data.code === 200 && data.content.length > 0)
resolve(data.content);
else
resolve(false);
,
error: function(error)
resolve(false);
);
)
let userInfo =
id: '1564868915154190336',
nickName: '小明',
sex: '男',
email: '1482334546@qq.com',
userType: '1',
userName: 'admin',
password: 'admin666?',
createTime: '2022-08-31 14:55',
valid: '1',
isDelete: '0',
headImg: 'https://profile.csdnimg.cn/7/3/0/2_m0_46188681',
let userInfoStr = JSON.stringify(userInfo)
async function dataEncryption()
let publicKey = await getPublicKey();
RsaUtil.init(publicKey);
console.log("后台获取到的公钥:" + publicKey)
let ciphertext = RsaUtil.encryptLong(userInfoStr);
console.log("使用后台公钥加密后的文本:" + ciphertext);
let decodeResult = await privateKeyDecode(ciphertext);
console.log("使用后台私钥解密后的文本:" + decodeResult)
console.log("是否解密成功",decodeResult === userInfoStr)
dataEncryption();
</script>
</body>
</html>
8.后端代码
RsaUtilClient配合 jsencrypt.js 解密的工具类
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import java.net.URLDecoder;
import java.security.*;
import java.security.spec.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class RsaUtilClient
public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";
private static volatile ConcurrentHashMap<String,Key> KEY_MAP = new ConcurrentHashMap<>(2);
/**
* base64解码
*
* @param content
* @return byte[]
* @author compass
* @date 2022/9/1 17:12
* @since 1.0.0
**/
public static byte[] decryptBASE64(String content)
return org.springframework.util.Base64Utils.decode(content.getBytes());
/**
* base64编码
*
* @param bytes 字符byte数组
* @return java.lang.String
* @author compass
* @date 2022/9/1 17:12
* @since 1.0.0
**/
public static String encryptBASE64(byte[] bytes)
return new String(org.springframework.util.Base64Utils.encode(bytes));
/**
* 用私钥对信息生成数字签名
*
* @param data 加密数据
* @param privateKey 私钥
* @return java.lang.String
* @author compass
* @date 2022/9/1 14:21
* @since 1.0.0
**/
public static String sign(byte[] data, String privateKey) throws Exception
// 解密由base64编码的私钥
byte[] keyBytes = decryptBASE64(privateKey);
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 用私钥对信息生成数字签名
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
return encryptBASE64(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
// 解密由base64编码的公钥
byte[] keyBytes = decryptBASE64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
// 验证签名是否正常
return signature.verify(decryptBASE64(sign));
/**
* 通过私钥解密
*
* @param data 加密的byte数组
* @param privateKey 私钥
* @return byte[]
* @author compass
* @date 2022/9/1 17:14
* @since 1.0.0
**/
public static byte[] decryptByPrivateKey(byte[] data, String privateKey)
throws Exception
// 对密钥解密
byte[] keyBytes = decryptBASE64(privateKey);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privatizationKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privatizationKey);
return cipher.doFinal(data);
/**
* 私钥解密前端jsencrypt分段加密的长文本内容,
* 前端加密的字符串需要使用encodeURIComponent编码,后端使用URLDecoder.decode解码来解决中文字符串无法解密的问题
*
* @param content 加密的内容
* @param privateKey 私钥
* @return java.lang.String
* @author compass
* @date 2022/9/1 16:53
* @since 1.0.0
**/
public static String jsencryptDecryptByPrivateKeyLong(String content, String privateKey)
String resultContent = "";
try
StringBuffer buffer = new StringBuffer();
if (content != null && content.trim().length() > 0)
String[] contentList = content.split("=");
for (String item : contentList)
byte[] itemBytes = Base64Utils.decode((item + "=").getBytes());
try
byte[] itemResultBytes = decryptByPrivateKey(itemBytes, privateKey);
String itemResultStr = new String(itemResultBytes);
buffer.append(itemResultStr);
catch (Exception e)
e.printStackTrace();
jsencrypt.js加密java后端解密