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

25 篇文章 0 订阅

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

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;
    }
}

使用示例:

RSAProvider().also {
    println("公钥:${it.publicKey}")
    println("私钥:${it.privateKey}")

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

Node/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'))
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

集成显卡

码字不易,需要您的鼓励😄

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值