前(jsencrypt)后(node-rsa/crypto)端 RSA 加密与解密

前端使用 jsencrypt 进行加密,服务端使用 node-rsa 或 crypto 进行解密。

jsencrypt 加密

需要注意的是 RSA 加密的数据长度是有限制的,过长的数据可能导致解密失败。允许的数据长度与密钥长度成正比。

import JSEncrypt from 'jsencrypt';

// 通过 node-rsa 或 crypto 生成的公钥,也可以其他方式生成,只要与私钥成对即可
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIIBIjA
......
owIDAQAB
-----END PUBLIC KEY-----
`;

/**
 * 对数据进行 RSA 加密,加密失败时会返回 false。
 *
 * JSEncrypt 只能加密字符串数据,因此使用 JSON.stringify 对要加密的数据进行序列化
 *
 * 但此时需要注意一些 JSON.stringify 的问题
 *
 *      比如:JSON.stringify(undefined) => undefined // 这不是一个字符串
 *      比如:JSON.stringify({ prop: undefined }) => '{}'
 *      比如:JSON.stringify(NaN) => 'null'
 *
 * @param {string | number | Object | Array} data 需要加密的数据
 * @param {string} publicKey 公钥,可选
 * @returns {string | false} 密文
 */
export const EncryptByRSA = (data, publicKey = PUBLIC_KEY) => {
    const encrypt = new JSEncrypt();

    encrypt.setPublicKey(publicKey);

    return encrypt.encrypt(JSON.stringify(data));
};

/**
 * 对密文进行 RSA 解密,秘钥不对会返回 false,数据不是加密后的密文会返回 null。
 *
 * 会使用 JSON.parse 对解密后数据进行反序列化
 *
 * @param {string} secretText 待解密的字符串
 * @param {string} privateKey 私钥
 * @returns {any} 解密后的数据
 */
export const DecryptByAES = (secretText, privateKey) => {
    const decrypt = new JSEncrypt();

    decrypt.setPrivateKey(privateKey);

    return JSON.parse(decrypt.decrypt(secretText));
};

crypto 解密

需要注意的是解密时需要设置正确的 padding,否则可能无法对 jsencrypt 加密的数据进行解密。

import crypto from 'crypto';

/**
 * 生成 RSA 公私钥对
 * @return {Object} { publicKey, privateKey }
 */
export const generateRSAKeyPair = () => {
    const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
        modulusLength: 2048, // 秘钥长度

        // 秘钥配置,详见 https://nodejs.cn/dist/latest-v18.x/docs/api/crypto.html#keyobjectexportoptions
        publicKeyEncoding: {
            type: 'spki', // 编码类型
            format: 'pem' // 编码格式
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem'
        }
    });
    return { publicKey, privateKey };
};

/**
 * 使用公钥进行加密,加密出错会抛出异常
 * @param {any} data 需要加密的数据,会使用 JSON.stringify 序列化
 * @param {string} publicKey
 * @return {string} 加密后的密文
 */
export const encrypt = (data, publicKey) => {
    const dataJSON = JSON.stringify(data);
    return crypto.publicEncrypt(publicKey, Buffer.from(dataJSON, 'utf-8')).toString('base64');
};

/**
 * 使用私钥进行解密,解密出错会抛出异常
 * @param {string} secretText 密文
 * @param {string} privateKey 私钥
 * @return {String} 解密后的明文,会使用 JSON.parse 反序列化
 */
export const decrypt = (secretText, privateKey) => {
    const dataStr = crypto
        .privateDecrypt(
            {
                key: privateKey,

                // padding 的值需要与公钥的编码类型相对应
                padding: crypto.constants.RSA_PKCS1_PADDING
            },
            Buffer.from(secretText, 'base64')
        )
        .toString('utf-8');
    return JSON.parse(dataStr);
};

node-rsa 解密

import NodeRSA from 'node-rsa';

/**
 * 生成 RSA 公私钥对
 * @return {Object} { publicKey, privateKey }
 */
export const generateRSAKeyPair = () => {
    const key = new NodeRSA({ b: 512 }); // 指定密钥长度

    key.setOptions({ encryptionScheme: 'pkcs1' }); // 指定加密格式

    const publicKey = key.exportKey('pkcs8-public-pem'); //制定输出格式
    const privateKey = key.exportKey('pkcs8-private-pem');

    return { publicKey, privateKey };
};

/**
 * 使用公钥进行加密,加密出错会抛出异常
 * @param {any} data 需要加密的数据,会使用 JSON.stringify 序列化
 * @param {string} publicKey
 * @return {string} 加密后的密文
 */
export const encrypt = (data, publicKey) => {
    const encrypt = new NodeRSA(publicKey, 'pkcs8-public-pem');
    encrypt.setOptions({ encryptionScheme: 'pkcs1' });

    return encrypt.encrypt(JSON.stringify(data), 'base64');
};

/**
 * 对密文进行 RSA 解密。如果解密失败,则抛出异常
 * @param {string} secretText 密文
 * @param {string} privateKey 私钥
 * @return {String} 解密后的明文,会使用 JSON.parse 反序列化
 */
export const decrypt = (secretText, privateKey) => {
    const decrypt = new NodeRSA(privateKey, 'pkcs8-private-pem');
    
    // jsencrypt 自身使用的是 pkcs1 加密方案,所以这里设置为 pkcs1
    decrypt.setOptions({ encryptionScheme: 'pkcs1' });

    return JSON.parse(decrypt.decrypt(secretText, 'utf8'));
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值