JAVA+Node/JavaScript 前后端通讯 RSA 加解密实现

实际项目中,前后端或跨语言加密通讯的场景十分常见。这里以 JavaNode.js(兼容浏览器)两种开发语言为例,实现 RSA 加解密通讯。

JAVA端加解密

此代码采用分段加解密,理论上支持无限长度的文本内容

java 复制代码
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public final class RSAProvider {

    private final static String RSA = "RSA";
    private final static String SHA256WithRSA = "SHA256WithRSA";
    private final static int MAX = 117;

    private String privateKey;
    private String publicKey;

    /**
     * 创建 RSA 工具类,自动生成公私钥(字符串格式)
     * @throws Exception
     */
    public RSAProvider() throws Exception {
        //自动生成密钥
        KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA);
        generator.initialize(1024, new SecureRandom());
        KeyPair keyPair = generator.genKeyPair();

        Base64.Encoder encoder = Base64.getEncoder();
        initKey(
                encoder.encodeToString(keyPair.getPublic().getEncoded()),
                encoder.encodeToString(keyPair.getPrivate().getEncoded())
        );
    }

    /**
     * 使用特定公私钥初始化工具类
     * @param pubKey
     * @param priKey
     */
    public RSAProvider(String pubKey, String priKey){
        initKey(pubKey, priKey);
    }

    private void initKey(String pubKey, String priKey){
        this.privateKey = priKey;
        this.publicKey = pubKey;
    }

    private PrivateKey buildPriKey() throws Exception {
        if(privateKey == null || privateKey.isEmpty())  throw new RuntimeException("PRIVATE KEY is empty!");

        return KeyFactory.getInstance(RSA)
                .generatePrivate(
                        new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))
                );
    }

    private PublicKey buildPubKey() throws Exception {
        if(publicKey == null || publicKey.isEmpty())  throw new RuntimeException("PUBLIC KEY is empty!");

        return KeyFactory.getInstance(RSA)
                .generatePublic(
                        new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))
                );
    }

    /**
     * 使用私钥签名
     * @param content
     * @return
     */
    public String sign(String content) throws Exception {
        Signature signature = Signature.getInstance(SHA256WithRSA);
        signature.initSign(buildPriKey());
        signature.update(content.getBytes());
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    /**
     * 公钥验签
     * @param message
     * @param signed
     * @return
     * @throws Exception
     */
    public boolean verifySign(String message, String signed) throws Exception {
        Signature signature = Signature.getInstance(SHA256WithRSA);
        signature.initVerify(buildPubKey());
        signature.update(message.getBytes());
        return signature.verify(Base64.getDecoder().decode(signed));
    }


    /**
     * 使用公钥加密
     * @param content 任意长度的字符
     * @return
     */
    public String encrypt(String content) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA);
        cipher.init(Cipher.ENCRYPT_MODE, buildPubKey());
        //此方法只能加密长度小于 117 bytes
//        return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes()));

        byte[] bytes = content.getBytes();
        ByteArrayOutputStream bops = new ByteArrayOutputStream();
        int offLen = 0;

        while (bytes.length - offLen > 0){
            bops.write(cipher.doFinal(bytes, offLen, Math.min(bytes.length - offLen, MAX)));
            offLen += MAX;
        }
        bops.close();
        return Base64.getEncoder().encodeToString(bops.toByteArray());
    }

    /**
     * 使用私钥解密
     * @param data 任意长度的密文(分段解密)
     * @return
     * @throws Exception
     */
    public String decrypt(String data) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA);
        cipher.init(Cipher.DECRYPT_MODE, buildPriKey());
        // 此方法报错:Data must not be longer than 128 bytes
//        return new String(cipher.doFinal(Base64.getDecoder().decode(data)));

        byte[] bytes =  Base64.getDecoder().decode(data);
        ByteArrayOutputStream bops = new ByteArrayOutputStream();
        int offLen = 0;

        while (bytes.length - offLen > 0){
            bops.write(cipher.doFinal(bytes, offLen, Math.min(bytes.length - offLen, 128)));
            offLen += 128;
        }
        bops.close();
        return bops.toString();
    }

    public String getPrivateKey() {
        return privateKey;
    }
    public String getPublicKey() {
        return publicKey;
    }
}

使用示例:

kotlin 复制代码
RSAProvider().also {
    println("公钥:${it.publicKey}")
    println("私钥:${it.privateKey}")

    val text = "Hello,集成显卡!!"
    val miwen = it.encrypt(text)
    println("密文:${miwen}")
    println("解码:${it.decrypt(miwen)}")
}

Node/JavaScript 加解密

javascript 复制代码
// 请先执行 npm i node-rsa
const NodeRSA = require("node-rsa")

const encryptionScheme = "pkcs1"
module.exports = {
    encrypt (data, publicKey) {
        const pubKey = new NodeRSA(publicKey, 'pkcs8-public')
        //设置与后端一致的加密方式 pkcs1
        pubKey.setOptions({ encryptionScheme })
        return pubKey.encrypt(Buffer.from(data), 'base64')
    },

    decrypt (data, privateKey){
        const priKey = new NodeRSA(privateKey, 'pkcs8-private')
        priKey.setOptions({ encryptionScheme })
        return priKey.decrypt(Buffer.from(data, 'base64'), 'utf8')
    },

    sign (data, privateKey){
        const priKey = new NodeRSA(privateKey, 'pkcs8-private')
        return priKey.sign(Buffer.from(data)).toString('base64')
    },

    verify (data, signature, publicKey){
        const pubKey = new NodeRSA(publicKey, 'pkcs8-public')
        return pubKey.verify(data, Buffer.from(signature, 'base64'))
    }
}
相关推荐
你的人类朋友14 分钟前
【Node.js】什么是Node.js
javascript·后端·node.js
黑客影儿39 分钟前
Kali Linux 环境中的系统配置文件与用户配置文件大全
linux·运维·程序人生·安全·网络安全·系统安全·学习方法
源码宝1 小时前
【智慧工地源码】智慧工地云平台系统,涵盖安全、质量、环境、人员和设备五大管理模块,实现实时监控、智能预警和数据分析。
java·大数据·spring cloud·数据分析·源码·智慧工地·云平台
David爱编程2 小时前
面试必问!线程生命周期与状态转换详解
java·后端
LKAI.2 小时前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi
HeyZoeHey2 小时前
Mybatis执行sql流程(一)
java·sql·mybatis
2301_793086872 小时前
SpringCloud 07 微服务网关
java·spring cloud·微服务
柳贯一(逆流河版)4 小时前
Spring 三级缓存:破解循环依赖的底层密码
java·spring·缓存·bean的循环依赖
三年呀4 小时前
标题:移动端安全加固:发散创新,筑牢安全防线引言:随着移动互联网
网络·python·安全
Q_Q19632884755 小时前
python的电影院座位管理可视化数据分析系统
开发语言·spring boot·python·django·flask·node.js·php