RSAUtil 前端 JavaScript JSEncrypt 实现 RSA (长文本)加密解密

文章归档:https://www.yuque.com/u27599042/coding_star/cl4dl599pdmtllw1

依赖

  1. import JSEncrypt from ‘jsencrypt’
pnpm i jsencrypt
  1. import {stringIsNull} from “@/utils/string_utils.js”:https://www.yuque.com/u27599042/coding_star/slncupw7un3ce7cb
  2. import {isNumber} from “@/utils/number_utils.js”:https://www.yuque.com/u27599042/coding_star/tuwmm3ghf5lgo4bw

导入依赖

import JSEncrypt from 'jsencrypt'
import {stringIsNull} from "@/utils/string_utils.js"
import {isNumber} from "@/utils/number_utils.js"

内部变量

/**
 * RSA 加密算法获取密钥对中的公钥使用的 key
 *
 * @type {string}
 */
export const PUBLIC_KEY = 'RSAPublicKey'

/**
 * RSA 加密算法获取密钥对中的密钥使用的 key
 *
 * @type {string}
 */
export const PRIVATE_KEY = 'RSAPrivateKey'

/**
 * RSA 密钥对的 bit 数(密钥对的长度)。
 * 常用 1024、2048,密钥对的 bit 数,越大越安全,但是越大对服务器的消耗越大
 *
 * @type {number}
 */
let keySize = 1024

/**
 * keySize bit 数下的 RSA 密钥对所能够加密的最大明文大小。
 * RSA 算法一次能加密的明文长度与密钥长度(RSA 密钥对的 bit 数)成正比,
 * 默认情况下,Padding 方式为 OPENSSL_PKCS1_PADDING,RSA 算法会使
 * 用 11 字节的长度用于填充,所以默认情况下,RSA 所能够加密的最大明文大
 * 小为 (keySize / 8 - 11) byte
 *
 * @type {number}
 */
let maxEncryptPlainTextLen = 117

/**
 * keySize bit 数下的 RSA 密钥对所能够解密的最大密文大小。
 * (keySize / 8) byte
 *
 * @type {number}
 */
let maxDecryptCipherTextLen = 128

/**
 * 密钥长度为 1024 bit 下,通过公钥生成的密文字符串的长度
 *
 * @type {number}
 */
let cipherTextStrLen = 172

设置 RSA 密钥对的 bit 数

/**
 * 为 RSA 密钥对的 bit 数赋值,同时重新计算 keySize bit 数下的
 * RSA 密钥对所能够加密的最大明文大小、所能够解密的最大密文大小
 *
 * @param size RSA 密钥对的 bit 数
 */
export function setKeySize(size) {
    if (!isNumber(size) || size <= 0) {
        throw new TypeError("参数 {size} 需要是大于 0 的整数")
    }
    keySize = size
    maxEncryptPlainTextLen = keySize / 8 - 11
    maxDecryptCipherTextLen = keySize / 8
}

获取指定字符的 UTF8 字节大小

/**
 * 获取指定字符的 UTF8 字节大小
 * 代码来源: https://blog.csdn.net/csdn_yuan_/article/details/107428744
 *
 * @param charCode 字符编码
 * @return {number} 指定字符的 UTF8 字节大小
 */
export function getCharByteSizeUTF8(charCode) {
    if (!isNumber(charCode) || charCode < 0) {
        throw new TypeError("参数 {charCode} 需要是大于 0 的整数")
    }
    //字符代码在000000 – 00007F之间的,用一个字节编码
    if (charCode <= 0x007f) {
        return 1
    }
    //000080 – 0007FF之间的字符用两个字节
    else if (charCode <= 0x07ff) {
        return 2
    }
    //000800 – 00D7FF 和 00E000 – 00FFFF之间的用三个字节,注: Unicode在范围 D800-DFFF 中不存在任何字符
    else if (charCode <= 0xffff) {
        return 3
    }
    //010000 – 10FFFF之间的用4个字节
    else {
        return 4
    }
}

获取字符串的 UTF8 字节长度

/**
 * 获取字符串的 UTF8 字节长度
 * 代码来源: https://blog.csdn.net/csdn_yuan_/article/details/107428744
 *
 * @param str 字符串
 * @returns {number} 字符串的 UTF8 字节长度
 */
export function getStrByteLenUTF8(str) {
    if (stringIsNull(str)) {
        throw new TypeError("参数 {str} 需要非空字符串")
    }
    // 获取字符串的字符长度
    const strLen = str.length
    // 保存字符串的字节长度
    let strByteLen = 0
    // 遍历判断字符串中的每个字符,统计字符串的 UTF8 字节长度
    for (let i = 0; i < strLen; i++) {
        // 获取当前遍历字符的编码
        let charCode = str.charCodeAt(i);
        // 获取并记录当前遍历字符的 UTF8 字节大小
        strByteLen += getCharByteSizeUTF8(charCode)
    }
    return strByteLen
}

获取字符串的 UTF8 字节长度, 同时获取按照指定的子字符串字节长度划分的子字符串数组

/**
 * 获取字符串的 UTF8 字节长度,同时获取按照指定的子字符串字节长度划分的子字符串数组
 * 代码参考: https://blog.csdn.net/csdn_yuan_/article/details/107428744
 *
 * @param str 字符串
 * @param subStrByteLen 子字符串字节长度
 * @return {[]} 按照指定的子字符串字节长度划分的子字符串数组
 */
export function getStrByteLenUTF8AndSubStrs(str, subStrByteLen) {
    if (stringIsNull(str)) {
        throw new TypeError("参数 {str} 需要非空字符串")
    }
    if (!isNumber(subStrByteLen) || subStrByteLen <= 0) {
        throw new TypeError("参数 {subStrByteLen} 需要是大于 0 的整数")
    }
    // 获取字符串的字符长度
    const strLen = str.length
    // 保存字符串的字节长度
    let strByteLen = 0
    // 记录上一次分隔的字符串的位置
    let preIdx = 0;
    // 记录当前子字符串的字节大小
    let subStrByteSize = 0;
    // 记录子字符串
    const subStrs = []
    // 遍历判断字符串中的每个字符,统计字符串的 UTF8 字节长度
    for (let i = 0; i < strLen; i++) {
        // 获取当前遍历字符的编码
        let charCode = str.charCodeAt(i);
        // 获取并记录当前遍历字符的 UTF8 字节大小
        let charByteSizeUTF8 = getCharByteSizeUTF8(charCode)
        strByteLen += charByteSizeUTF8
        // 当前子字符串的字节大小
        subStrByteSize += charByteSizeUTF8
        // 子字符串达到切割长度
        if (subStrByteSize > subStrByteLen) {
            // 当前子字符串加入返回结果数组中
            subStrs.push(str.substring(preIdx, i))
            // 更新数据
            preIdx = i
            subStrByteSize = charByteSizeUTF8
        }
    }
    // 如果还有子字符串还为加入返回结果数组中
    if (subStrByteSize > 0) {
        subStrs.push(str.substring(preIdx))
    }
    return {
        strByteLen,
        subStrs
    }
}

使用公钥对明文进行加密(支持长文本)

/**
 * 使用公钥对明文进行加密(支持长文本)
 *
 * @param publicKey 公钥
 * @param plainText 明文
 * @returns {string} 明文加密后的密文
 */
export function encryptByPublicKey(publicKey, plainText) {
    if (stringIsNull(publicKey) || stringIsNull(plainText)) {
        throw new TypeError("参数 {publicKey} {plainText} 需要非空字符串")
    }
    // 获取明文字符串的字节大小和根据指定字节大小划分的子字符串数组
    const { strByteLen: plainTextByteSize, subStrs: plainTextSubStrArr } = getStrByteLenUTF8AndSubStrs(
        plainText,
        maxEncryptPlainTextLen
    )
    // 明文加密后的完整密文
    let cipherText = ""
    // 对明文进行分段加密
    plainTextSubStrArr.forEach(subStr => {
        // 获取加密解密器
        const encryptor = new JSEncrypt()
        // 设置公钥
        encryptor.setPublicKey(publicKey)
        // 加密
        cipherText += encryptor.encrypt(subStr)
    })
    return cipherText
}

使用私钥对密文进行解密(支持长文本)

注意: 此方法只适用于使用和上述加密方法逻辑相同的加密处理得到的密文的解密

/**
 * 使用私钥对密文进行解密(支持长文本)
 * 注意: 此方法只适用于使用和上述加密方法逻辑相同的加密处理得到的密文的解密
 *
 * @param privateKey 密钥
 * @param cipherText 密文
 * @return {string} 密文解密后的明文
 */
export function decryptByPrivateKey(privateKey, cipherText) {
    if (stringIsNull(privateKey) || stringIsNull(cipherText)) {
        throw new TypeError("参数 {privateKey} {cipherText} 需要非空字符串")
    }
    // 获取密文的字符长度
    let cipherTextLen = cipherText.length
    // 计算分段解密的次数, cipherTextStrLen 每段密文长度
    let decryptCount = cipherTextLen / cipherTextStrLen
    // 解密后的完整明文
    let plainText = ""
    // 对密文进行分段解密
    for (let i = 0; i < decryptCount; i++) {
        // 分段密文距离开始位置的偏移量
        let offSet = i * cipherTextStrLen
        let subCipherText = cipherText.substring(offSet, offSet + cipherTextLen)
        // 加密解密器
        const encryptor = new JSEncrypt()
        // 设置私钥
        encryptor.setPrivateKey(privateKey)
        // 解密
        plainText += encryptor.decrypt(subCipherText)
    }
    return plainText
}

与 RSAUtil 搭配的 Java 后端 RSAUtilInteractiveWithFrontEnd

完整源码

import JSEncrypt from 'jsencrypt'
import {stringIsNull} from "@/utils/string_utils.js"
import {isNumber} from "@/utils/number_utils.js"

/**
 * RSA 加密算法获取密钥对中的公钥使用的 key
 *
 * @type {string}
 */
export const PUBLIC_KEY = 'RSAPublicKey'

/**
 * RSA 加密算法获取密钥对中的密钥使用的 key
 *
 * @type {string}
 */
export const PRIVATE_KEY = 'RSAPrivateKey'

/**
 * RSA 密钥对的 bit 数(密钥对的长度)。
 * 常用 1024、2048,密钥对的 bit 数,越大越安全,但是越大对服务器的消耗越大
 *
 * @type {number}
 */
let keySize = 1024

/**
 * keySize bit 数下的 RSA 密钥对所能够加密的最大明文大小。
 * RSA 算法一次能加密的明文长度与密钥长度(RSA 密钥对的 bit 数)成正比,
 * 默认情况下,Padding 方式为 OPENSSL_PKCS1_PADDING,RSA 算法会使
 * 用 11 字节的长度用于填充,所以默认情况下,RSA 所能够加密的最大明文大
 * 小为 (keySize / 8 - 11) byte
 *
 * @type {number}
 */
let maxEncryptPlainTextLen = 117

/**
 * keySize bit 数下的 RSA 密钥对所能够解密的最大密文大小。
 * (keySize / 8) byte
 *
 * @type {number}
 */
let maxDecryptCipherTextLen = 128

/**
 * 密钥长度为 1024 bit 下,通过公钥生成的密文字符串的长度
 *
 * @type {number}
 */
let cipherTextStrLen = 172

/**
 * 为 RSA 密钥对的 bit 数赋值,同时重新计算 keySize bit 数下的
 * RSA 密钥对所能够加密的最大明文大小、所能够解密的最大密文大小
 *
 * @param size RSA 密钥对的 bit 数
 */
export function setKeySize(size) {
    if (!isNumber(size) || size <= 0) {
        throw new TypeError("参数 {size} 需要是大于 0 的整数")
    }
    keySize = size
    maxEncryptPlainTextLen = keySize / 8 - 11
    maxDecryptCipherTextLen = keySize / 8
}

/**
 * 获取指定字符的 UTF8 字节大小
 * 代码来源: https://blog.csdn.net/csdn_yuan_/article/details/107428744
 *
 * @param charCode 字符编码
 * @return {number} 指定字符的 UTF8 字节大小
 */
export function getCharByteSizeUTF8(charCode) {
    if (!isNumber(charCode) || charCode < 0) {
        throw new TypeError("参数 {charCode} 需要是大于 0 的整数")
    }
    //字符代码在000000 – 00007F之间的,用一个字节编码
    if (charCode <= 0x007f) {
        return 1
    }
    //000080 – 0007FF之间的字符用两个字节
    else if (charCode <= 0x07ff) {
        return 2
    }
    //000800 – 00D7FF 和 00E000 – 00FFFF之间的用三个字节,注: Unicode在范围 D800-DFFF 中不存在任何字符
    else if (charCode <= 0xffff) {
        return 3
    }
    //010000 – 10FFFF之间的用4个字节
    else {
        return 4
    }
}

/**
 * 获取字符串的 UTF8 字节长度
 * 代码来源: https://blog.csdn.net/csdn_yuan_/article/details/107428744
 *
 * @param str 字符串
 * @returns {number} 字符串的 UTF8 字节长度
 */
export function getStrByteLenUTF8(str) {
    if (stringIsNull(str)) {
        throw new TypeError("参数 {str} 需要非空字符串")
    }
    // 获取字符串的字符长度
    const strLen = str.length
    // 保存字符串的字节长度
    let strByteLen = 0
    // 遍历判断字符串中的每个字符,统计字符串的 UTF8 字节长度
    for (let i = 0; i < strLen; i++) {
        // 获取当前遍历字符的编码
        let charCode = str.charCodeAt(i);
        // 获取并记录当前遍历字符的 UTF8 字节大小
        strByteLen += getCharByteSizeUTF8(charCode)
    }
    return strByteLen
}

/**
 * 获取字符串的 UTF8 字节长度,同时获取按照指定的子字符串字节长度划分的子字符串数组
 * 代码参考: https://blog.csdn.net/csdn_yuan_/article/details/107428744
 *
 * @param str 字符串
 * @param subStrByteLen 子字符串字节长度
 * @return {[]} 按照指定的子字符串字节长度划分的子字符串数组
 */
export function getStrByteLenUTF8AndSubStrs(str, subStrByteLen) {
    if (stringIsNull(str)) {
        throw new TypeError("参数 {str} 需要非空字符串")
    }
    if (!isNumber(subStrByteLen) || subStrByteLen <= 0) {
        throw new TypeError("参数 {subStrByteLen} 需要是大于 0 的整数")
    }
    // 获取字符串的字符长度
    const strLen = str.length
    // 保存字符串的字节长度
    let strByteLen = 0
    // 记录上一次分隔的字符串的位置
    let preIdx = 0;
    // 记录当前子字符串的字节大小
    let subStrByteSize = 0;
    // 记录子字符串
    const subStrs = []
    // 遍历判断字符串中的每个字符,统计字符串的 UTF8 字节长度
    for (let i = 0; i < strLen; i++) {
        // 获取当前遍历字符的编码
        let charCode = str.charCodeAt(i);
        // 获取并记录当前遍历字符的 UTF8 字节大小
        let charByteSizeUTF8 = getCharByteSizeUTF8(charCode)
        strByteLen += charByteSizeUTF8
        // 当前子字符串的字节大小
        subStrByteSize += charByteSizeUTF8
        // 子字符串达到切割长度
        if (subStrByteSize > subStrByteLen) {
            // 当前子字符串加入返回结果数组中
            subStrs.push(str.substring(preIdx, i))
            // 更新数据
            preIdx = i
            subStrByteSize = charByteSizeUTF8
        }
    }
    // 如果还有子字符串还为加入返回结果数组中
    if (subStrByteSize > 0) {
        subStrs.push(str.substring(preIdx))
    }
    return {
        strByteLen,
        subStrs
    }
}

/**
 * 使用公钥对明文进行加密(支持长文本)
 *
 * @param publicKey 公钥
 * @param plainText 明文
 * @returns {string} 明文加密后的密文
 */
export function encryptByPublicKey(publicKey, plainText) {
    if (stringIsNull(publicKey) || stringIsNull(plainText)) {
        throw new TypeError("参数 {publicKey} {plainText} 需要非空字符串")
    }
    // 获取明文字符串的字节大小和根据指定字节大小划分的子字符串数组
    const { strByteLen: plainTextByteSize, subStrs: plainTextSubStrArr } = getStrByteLenUTF8AndSubStrs(
        plainText,
        maxEncryptPlainTextLen
    )
    // 明文加密后的完整密文
    let cipherText = ""
    // 对明文进行分段加密
    plainTextSubStrArr.forEach(subStr => {
        // 获取加密解密器
        const encryptor = new JSEncrypt()
        // 设置公钥
        encryptor.setPublicKey(publicKey)
        // 加密
        cipherText += encryptor.encrypt(subStr)
    })
    return cipherText
}

/**
 * 使用私钥对密文进行解密(支持长文本)
 * 注意: 此方法只适用于使用和上述加密方法逻辑相同的加密处理得到的密文的解密
 *
 * @param privateKey 密钥
 * @param cipherText 密文
 * @return {string} 密文解密后的明文
 */
export function decryptByPrivateKey(privateKey, cipherText) {
    if (stringIsNull(privateKey) || stringIsNull(cipherText)) {
        throw new TypeError("参数 {privateKey} {cipherText} 需要非空字符串")
    }
    // 获取密文的字符长度
    let cipherTextLen = cipherText.length
    // 计算分段解密的次数, cipherTextStrLen 每段密文长度
    let decryptCount = cipherTextLen / cipherTextStrLen
    // 解密后的完整明文
    let plainText = ""
    // 对密文进行分段解密
    for (let i = 0; i < decryptCount; i++) {
        // 分段密文距离开始位置的偏移量
        let offSet = i * cipherTextStrLen
        let subCipherText = cipherText.substring(offSet, offSet + cipherTextLen)
        // 加密解密器
        const encryptor = new JSEncrypt()
        // 设置私钥
        encryptor.setPrivateKey(privateKey)
        // 解密
        plainText += encryptor.decrypt(subCipherText)
    }
    return plainText
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
前端Vue RSA加密示例代码如下: ```javascript 前端Vue RSA加密 import JSEncrypt from 'jsencrypt' // 创建RSA加密实例 const encrypt = new JSEncrypt() // 设置RSA公钥 const publicKey = 'YOUR_RSA_PUBLIC_KEY' encrypt.setPublicKey(publicKey) // 要加密的数据 const data = 'YOUR_DATA_TO_ENCRYPT' // 使用RSA公钥进行加密 const encryptedData = encrypt.encrypt(data) // 将加密后的数据发送到后端进行解密 ``` 后端Java解密示例代码如下: ```java import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import javax.crypto.Cipher; import org.apache.commons.codec.binary.Base64; public class RSADecrypt { public static String decrypt(String encryptedData, String privateKeyStr) throws Exception { // 将Base64编码后的私钥字符串转换为PrivateKey对象 byte[] privateKeyBytes = Base64.decodeBase64(privateKeyStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); // 使用私钥进行解密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] encryptedBytes = Base64.decodeBase64(encryptedData); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); // 返回解密后的数据 return new String(decryptedBytes); } } ``` 请将 `YOUR_RSA_PUBLIC_KEY` 替换为你的RSA公钥,然后在前端将加密后的数据发送到后端,后端调用 `RSADecrypt.decrypt()` 方法进行解密,并将 `YOUR_DATA_TO_ENCRYPT` 替换为你要加密的数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值