js与java对接RSA加密、解密算法

一、什么是RSA加密

上世纪70年代产生的一种加密算法,其加密方式比较特殊,需要两个密钥:公开密钥简称公钥(publickey)和私有密钥简称私钥(privatekey)。公钥加密,私钥解密;私钥加密,公钥解密。这个算法就是伟大的RSA。

二、加密过程

使用公钥将数据加密,并通过私钥对加密信息进行解密。针对我们遇到的问题,公钥放在前端对用户名密码进行加密,私钥放在服务端对前端提交的加密数据进行解密,然后在做登陆的业务操作。流程如下图所示:

 

 

三、前端加密解密

根据模数和指数生成publicKey

  前端使用 node-forge 插件进行加密,后端并不会返回pem格式的公钥,而是返回模数(modules)和公钥指数(publicExponent),前端根据这两个参数去生成公钥

npm install --save node-forge
import forge from 'node-forge'

/**
 * 
 * @param msg 需要加密的字符
 * @param modules 模数
 * @param publicExponent 公钥指数
 * @returns 加密之后的字符串 16进制
 */
function aesEncrypt(msg, modules, publicExponent) {
  const BigInteger = forge.jsbn.BigInteger
  const n = forge.util.createBuffer(modules).toString('hex')
  const e = forge.util.createBuffer(publicExponent).toString('hex')
  const publicKey = forge.pki.setRsaPublicKey(new BigInteger(n, 16), new BigInteger(e, 16))

  const buffer = forge.util.createBuffer(msg, 'utf8')
  const bytes = buffer.getBytes()

  const res = publicKey.encrypt(bytes, 'RSA-OAEP', {
    md: forge.md.sha256.create(),
    mgf1: {
      md: forge.md.sha256.create()
    }
  })
  return forge.util.bytesToHex(res)
}

/**
 * 
 * @param serect 需要解密的字符串
 * @param modules 模数
 * @param publicExponent 公钥指数
 * @param privateExponent 私钥指数
 * @returns 解密后的字符串
 */
function aesDecrypt(serect, modules, publicExponent, privateExponent) {
  const BigInteger = forge.jsbn.BigInteger
  const n = forge.util.createBuffer(modules).toString('hex')
  const e = forge.util.createBuffer(publicExponent).toString('hex')
  const d = forge.util.createBuffer(privateExponent).toString('hex')
  const privateKey = forge.pki.setRsaPrivateKey(new BigInteger(n, 16), new BigInteger(e, 16), new BigInteger(d, 16))

  const res = privateKey.decrypt(res, 'RSA-OAEP', {
    md: forge.md.sha256.create(),
    mgf1: {
      md: forge.md.sha256.create()
    }
  })
  return res
}

 

四、后端加密代码

package com.example;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;

import javax.crypto.Cipher;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
/**
 * Unit test for simple App.
 */
public class AppTest {
    public static String RSA_KEY = "RSA";
    public static String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
    private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
    private static KeyFactory keyFactory = null;

    public static String PrivateEx="4abbae70395...";
    public static String PrivateMo="c8c156b0806..."; // modules
    public static String PublicEx="10001";
    public static String PublicMo="c8c156b0806..."; // modules
    static {
        try {

          keyFactory = KeyFactory.getInstance("RSA", DEFAULT_PROVIDER);
        } catch (NoSuchAlgorithmException ex) {
        }
    }

    public static String UTF8 = "UTF-8";

    /**
     * 通过私钥byte[]将公钥还原,适用于RSA算法
     * 
     * @param keyBytes
     * @return
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String modulus, String exponent) throws Exception {
        RSAPrivateKeySpec privateKey = new RSAPrivateKeySpec(new BigInteger(modulus,16), new BigInteger(exponent,16));
        return (RSAPrivateKey) keyFactory.generatePrivate(privateKey);
    }

    public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
        RSAPrivateKey rsaKey = (RSAPrivateKey) privateKey;
        Cipher ci = Cipher.getInstance(RSA_ALGORITHM, DEFAULT_PROVIDER);
        ci.init(Cipher.DECRYPT_MODE, rsaKey);

        ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        Integer index = 0;
        Integer blockSize = rsaKey.getModulus().bitLength() / 8;
        byte[] buffer = new byte[blockSize];
        while ((index = inputStream.read(buffer, 0, blockSize)) > 0) {
            byte[] clpherBlock = ci.doFinal(buffer, 0, index);
            outputStream.write(clpherBlock);
        }

        byte[] result= ((ByteArrayOutputStream) outputStream).toByteArray();
        outputStream.flush();
        return result;
    }

    public static String decryptString(PrivateKey privateKey, String encrypttext) {
        try {
            byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
            byte[] data = decrypt(privateKey, en_data);
            return new String(data);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * Rigorous Test :-)
     */
    
    public void shouldAnswerWithTrue() {
        String exponent = PrivateEx;
        String modulus =PrivateMo;
        String encryption = "3532a86906a..."; // 需要解密的串
        try {
            String result = decryptString(getPrivateKey(modulus, exponent),encryption);
            System.out.println("解密结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public static void main(String[] args) {
      new AppTest().shouldAnswerWithTrue();
    }
}

 

参考:

  node-forge

posted on 2022-06-29 17:23  sjpqy  阅读(2329)  评论(0编辑  收藏  举报

导航