前端使用
jsencryp
实现分段加密。
解决长文本RSA
加密报错问题。
支持文本包含中文。
支持后端解密。
前端加密代码:
// import { JSEncrypt } from 'jsencrypt'
const JSEncrypt = require('jsencrypt')
/*** 使用 JSEncrypt 实现分段 RSA 加密(正确处理中文字符)* @param {string} data - 要加密的数据* @param {string} publicKey - PEM格式的公钥* @returns {string} 加密后的数据(Base64编码)*/
function encryptWithJSEncrypt(data, publicKey) {const encrypt = new JSEncrypt();encrypt.setPublicKey(publicKey);// RSA 2048位密钥时,最大加密明文长度为245字节const MAX_ENCRYPT_BLOCK = 245;const encryptedBytes = [];// 按字符安全地分段,避免切断UTF-8字符let startIndex = 0;while (startIndex < data.length) {// 尝试获取一段文本,确保其UTF-8编码后的字节长度不超过MAX_ENCRYPT_BLOCKlet endIndex = startIndex;let segment = '';// 逐个添加字符,直到接近最大长度限制while (endIndex < data.length) {const nextSegment = segment + data.charAt(endIndex);const nextSegmentBytes = new TextEncoder().encode(nextSegment);// 如果添加下一个字符会超出限制,则停止if (nextSegmentBytes.length > MAX_ENCRYPT_BLOCK) {break;}segment = nextSegment;endIndex++;}// 如果没有成功获取到任何字符(理论上不应该发生),则至少获取一个字符if (segment.length === 0 && startIndex < data.length) {segment = data.charAt(startIndex);endIndex = startIndex + 1;}// 加密当前段const encryptedBlock = encrypt.encrypt(segment);if (!encryptedBlock) {throw new Error('加密失败');}// 将加密后的Base64字符串转换为字节数组const encryptedBlockBytes = atob(encryptedBlock);for (let j = 0; j < encryptedBlockBytes.length; j++) {encryptedBytes.push(encryptedBlockBytes.charCodeAt(j));}// 移动到下一段startIndex = endIndex;}// 将所有加密后的字节数据进行Base64编码let result = '';for (let i = 0; i < encryptedBytes.length; i++) {result += String.fromCharCode(encryptedBytes[i]);}return btoa(result);
}// 带不带-----BEGIN PUBLIC KEY-----均可
let pubkey = '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwCKy+dsq0bbBhdt2Z2UDirtemU/7lAb746JngP4KLDmH9IgLZfranlflVLjfDW6BMYEVP3mN0HPlW4RWMPORgaHUu1yECzWqQX2bpDlN+IAZZhytTlMEyA+3DqKy3tS3QlIheDpMj7T6rOrfVFUDu3A4Tg/I3xqkToAHWI3rW3O3kDcFkFWVKYLiHsBhCd6LtauYENsB36voQKMGDrd9KZd5ndcy7otAU/+ITEfIMyKoD0eCojuDVOtREVY9jaos6kMDAtm8+ppbC7/0Q4TBf1zWaC1xDI6sTp+L6sYD6fDE2ni1/ly718hvHdPYC6GanmrME51pGGp8FgIkYpWXiQIDAQAB-----END PUBLIC KEY-----'let longText = "这是一段需要加密的长文本示例。RSA加密算法对加密的数据长度有限制,使用分段加密可以解决这个问题。当文本长度超过密钥所能处理的最大长度时,我们需要将文本分成多个部分分别进行加密。Java后端使用的RSA/ECB/PKCS1Padding模式与JSEncrypt默认模式兼容"
let encrypt= encryptWithJSEncrypt(longText, pubkey)
console.log(encrypt)
后端工具类:
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;public class RSAUtils {private static final String ALGORITHM = "RSA";private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";private static final int KEY_SIZE = 2048;// RSA最大加密明文大小private static final int MAX_ENCRYPT_BLOCK = 245;// RSA最大解密密文大小private static final int MAX_DECRYPT_BLOCK = 256;/*** 生成RSA密钥对** @return KeyPair对象包含公钥和私钥*/public static KeyPair generateKeyPair() throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);keyPairGenerator.initialize(KEY_SIZE);return keyPairGenerator.generateKeyPair();}/*** 使用公钥加密数据(支持分段加密)** @param data 要加密的数据* @param publicKey 公钥* @return 加密后的数据(Base64编码)*/public static String encrypt(String data, PublicKey publicKey) throws Exception {Cipher cipher = Cipher.getInstance(TRANSFORMATION);cipher.init(Cipher.ENCRYPT_MODE, publicKey);byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);int inputLen = dataBytes.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(dataBytes, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(dataBytes, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return Base64.getEncoder().encodeToString(encryptedData);}/*** 使用私钥解密数据(支持分段解密)** @param encryptedData 加密的数据(Base64编码)* @param privateKey 私钥* @return 解密后的原始数据*/public static String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {Cipher cipher = Cipher.getInstance(TRANSFORMATION);cipher.init(Cipher.DECRYPT_MODE, privateKey);byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);int inputLen = encryptedBytes.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(encryptedBytes, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedBytes, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return new String(decryptedData, StandardCharsets.UTF_8);}/*** 从字符串加载公钥** @param publicKey 公钥字符串* @return PublicKey对象*/public static PublicKey loadPublicKey(String publicKey) throws Exception {byte[] keyBytes = Base64.getDecoder().decode(publicKey);X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);return keyFactory.generatePublic(spec);}/*** 从字符串加载私钥** @param privateKey 私钥字符串* @return PrivateKey对象*/public static PrivateKey loadPrivateKey(String privateKey) throws Exception {byte[] keyBytes = Base64.getDecoder().decode(privateKey);PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);return keyFactory.generatePrivate(spec);}/*** 将公钥转换为字符串** @param publicKey 公钥* @return Base64编码的公钥字符串*/public static String publicKeyToString(PublicKey publicKey) {return Base64.getEncoder().encodeToString(publicKey.getEncoded());}/*** 将私钥转换为字符串** @param privateKey 私钥* @return Base64编码的私钥字符串*/public static String privateKeyToString(PrivateKey privateKey) {return Base64.getEncoder().encodeToString(privateKey.getEncoded());}/*** 测试加密解密功能*/public static void main(String[] args) {try {// 生成密钥对KeyPair keyPair = generateKeyPair();PublicKey publicKey = keyPair.getPublic();PrivateKey privateKey = keyPair.getPrivate();// 打印公钥和私钥System.out.println("公钥: " + publicKeyToString(publicKey));System.out.println("私钥: " + privateKeyToString(privateKey));// 测试数据String testData = "这是一段需要加密的长文本示例。RSA加密算法对加密的数据长度有限制,使用分段加密可以解决这个问题。当文本长度超过密钥所能处理的最大长度时,我们需要将文本分成多个部分分别进行加密。Java后端使用的RSA/ECB/PKCS1Padding模式与JSEncrypt默认模式兼容";// 加密String encryptedData = encrypt(testData, publicKey);System.out.println("加密后数据: " + encryptedData);// 解密String decryptedData = decrypt(encryptedData, privateKey);System.out.println("解密后数据: " + decryptedData);// 验证System.out.println("加密解密是否成功: " + testData.equals(decryptedData));} catch (Exception e) {e.printStackTrace();}}
}