JS 与 JAVA 跨语言实现 RSA 和 AES加密算法

简介:
开发中为了数据的安全性使用加密算法对数据进行加密是一种很常见的情况,但就一种语言来说,直接使用提供的相应的库进行少许封装是一件很容易的事。但是在一些情况下我们可能需要跨语言来实现,比如前后端分离的 web 开发中,我们需要前端使用 JS 进行加密与解密,后端则使用 Java、PHP等。这个时候由于不再是使用相同的库,相应的库中提供的默认参数设置也就存在不一样的情况,这个时候要做到前端与后端数据的加密解密交换,就不太容易了。本人也是折腾了两天啃了书,查了许多资料才搞定的。

这里不对原理进行详细赘述,如果还不了解加密算法细节和原理的可以自行查找,本人博客也提供 AES 与 RSA 简单原理介绍,你可以查看。如需详细全面了解,推荐读《Java 加密与解密艺术》这本书。

相关参考:
RSA 加密算法原理简述:https://blog.csdn.net/gulang03/article/details/81176133

AES 加密算法的原理详解:https://blog.csdn.net/gulang03/article/details/81175854

下面的例子是经过测试的,确保了前端与后端都能对相同的数据进行加解密,使用时请尽量与我的环境保持一致。

JDK:1.8.0_201

JS 的相关库及版本,参考源码的注释,由于前端使用了 webpack 打包工具,所以代码中会存在 ES6 语法,如果您使用的是原生 JS,只需参考其中的方法即可。

AES
前端 JS 实现:

/**
 * AES 加密算法封装,
 * 密钥长度定位 256 位,
 * 对外提供密钥的生成、加密、解密
 * 本模块测试时使用的是:crypto-js@3.1.9-1,uuid@3.3.2
 */
 
let CryptJS = require("crypto-js");
let UUID = require("uuid")
 
 
export var AESUtil = {
    /**
     * AES 加密
     * @param _content 待加密内容
     * @param _key aesKey, 
     * @param _iv 初始化向量
     * @return 返回经 BASE64 处理之后的密文
     */
    encrypt: function (_content, _key, _iv) {
        // 先以 UTF-8 编码解码参数 返回 any 类型
        let content = CryptJS.enc.Utf8.parse(_content);
        let aesKey = CryptJS.enc.Utf8.parse(_key);
        let iv = CryptJS.enc.Utf8.parse(_iv);
 
        // 加密
        let encrypted = CryptJS.AES.encrypt(content, aesKey, {
            iv: iv,
            mode: CryptJS.mode.CBC,
            padding: CryptJS.pad.Pkcs7
        })
        // console.log(encrypted)
        return CryptJS.enc.Base64.stringify(encrypted.ciphertext);
    },
 
    /**
     * AES 解密
     * @param:_content 待解密的内容[Base64处理过的]
     * @param:解密用的 AES key
     * @param: 初始化向量
     * @return 返回以 UTF-8 处理之后的明文
     */
    decrypt: function (_content, _key, _iv) {    
        // let content = CryptJS.enc.Base64.parse(_content);
        // content = CryptJS.enc.Base64.stringify(content);
        let aesKey = CryptJS.enc.Utf8.parse(_key);
        let iv = CryptJS.enc.Utf8.parse(_iv);
 
        // 解密
        let decrypted = CryptJS.AES.decrypt(_content, aesKey, {
            iv: iv,
            mode: CryptJS.mode.CBC,
            padding: CryptJS.pad.Pkcs7
        })
        // console.log(decrypted)
        return decrypted.toString(CryptJS.enc.Utf8);
    },
 
    /**
     * 获得 AES 密钥
     * @returns 32 字节的AES密钥
     */
    getAesKey: function () {
        let uuid = UUID.v1();
        // console.log(uuid)
        let aeskey = CryptJS.enc.Utf8.parse(uuid)
        aeskey = CryptJS.enc.Base64.stringify(aeskey).substring(2, 34)
        // console.log(aeskey + "
" + "长度:" + aeskey.length);
        return aeskey;
    },
 
    /**
     * 获得初始化向量
     * @returns 16 字节的初始化向量
     */
    getIv: function () {
        let uuid = UUID.v1();
        let iv = CryptJS.enc.Utf8.parse(uuid);
        iv = CryptJS.enc.Base64.stringify(iv).substring(2, 18);
        // console.log(iv + "
" + "长度:" + iv.length);
        return iv;
    },
 
    /**
     * 获得 AES key 及 初始化向量 iv
     * 其实 iv 和 aesKey 两者的生成并没有什么关系,两者只是对各自的长度有限制,
     * 这里只是为了方便使用,进行了一个组合返回。
     * @return 返回 iv 和 aesKey 的组合
     */
    getAESKeyAndIv: function () {
        let aesKeyAndIv = {
            "iv": this.getIv(),
            "aesKey": this.getAesKey(),
        }
 
        return aesKeyAndIv;
    }
}

  后端 Java 实现: 

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.UUID;
 
/**
 * AES 加密方法,是对称的密码算法(加密与解密的密钥一致),这里使用最大的 256 位的密钥,
 * 对外提供密钥生成、加密、解密方法
 */
public class AESUtil {
    /**
     * 获得一个 密钥长度为 8*32 = 256 位的 AES 密钥,
     * @return 返回经 BASE64 处理之后的密钥字符串(并截取 32 字节长度)
     */
    public static String getAESStrKey() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        UUID uuid = UUID.randomUUID();
        String aesKey= Base64.getEncoder().encodeToString(uuid.toString().getBytes()).substring(2,34);
        return aesKey;
    }
 
    /**
     * 获得一个初始化向量,初始化向量长度为 4*4 = 16 个字节
     * @return 返回经 BASE64 处理之后的密钥字符串(并截取 16 字节长度)
     */
    public static String getIv(){
        UUID uuid = UUID.randomUUID();
        String iv = Base64.getEncoder().encodeToString(uuid.toString().getBytes()).substring(2,18);
        return iv;
    }
 
    /**
     * 获得 AES key 及 初始化向量 iv
     * 其实 iv 和 aesKey 两者的生成并没有什么关系,两者只是对各自的长度有限制,
     * 这里只是为了方便使用,进行了一个组合返回。
     * @return 返回 iv 和 aesKey 的组合
     */
    public static HashMap<String, String>  getAESKeyAndIv() throws UnsupportedEncodingException, NoSuchAlgorithmException {
        HashMap<String, String> aesKeyAndIv = new HashMap<>();
        aesKeyAndIv.put("aesKey", AESUtil.getAESStrKey());
        aesKeyAndIv.put("iv", AESUtil.getIv());
 
        return aesKeyAndIv;
    }
 
    /**
     * 加密
     * @param content 待加密内容
     * @param secretKeyStr 加密使用的 AES 密钥,BASE64 编码后的字符串
     * @param iv 初始化向量,长度为 16 个字节,16*8 = 128 位
     * @return 加密后的密文,进行 BASE64 处理之后返回
     */
    public static String encryptAES(byte[] content, String secretKeyStr, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
        // 获得一个 SecretKeySpec
        // SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKeyStr), "AES");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyStr.getBytes(), "AES");
        // 获得加密算法实例对象 Cipher
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/补码方式"
        // 获得一个 IvParameterSpec
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());  // 使用 CBC 模式,需要一个向量 iv, 可增加加密算法的强度
        // 根据参数初始化算法
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
 
        // 执行加密并返回经 BASE64 处助理之后的密文
        return Base64.getEncoder().encodeToString(cipher.doFinal(content));
    }
 
    /**
     * 解密
     * @param content: 待解密内容,是 BASE64 编码后的字节数组
     * @param secretKeyStr: 解密使用的 AES 密钥,BASE64 编码后的字符串
     * @param iv: 初始化向量,长度 16 字节,16*8 = 128 位
     * @return 解密后的明文,直接返回经 UTF-8 编码转换后的明文
     */
    public static String decryptAES(byte[] content, String secretKeyStr, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, UnsupportedEncodingException {
        // 密文进行 BASE64 解密处理
        byte[] contentDecByBase64 = Base64.getDecoder().decode(content);
        // 获得一个 SecretKeySpec
        // SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKeyStr), "AES");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyStr.getBytes(), "AES");
        // 获得加密算法实例对象 Cipher
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/补码方式"
        // 获得一个初始化 IvParameterSpec
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
        // 根据参数初始化算法
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        // 解密
        return new String(cipher.doFinal(contentDecByBase64), "utf8");
    }
}

AES 测试:
由于 AES 是对称加密算法,所以只需保证,前端与后端都是使用相同的密钥和待加密的明文,只要双方的结果一样就表示通过测试。这里只测试加密与解密方法,关于 密钥 及 初始化向量 的生成和转换的方法本人已经测试过,这里就不再展示。

js:

    import { AESUtil } from './AESUtil'    
 
    // 测试的明文
    let content = "abcdefg789+-*+="    
    let IV = "BjNzhiZDctOGMxOS" // 注意IV必须是 16 个字节 8*16 = 128 位
    let aesKey = "I0YmM5NTgtY2IyYi00OWIzLWFkZTktZj" // 由于采用的 AES-256, 所以 Key 为 32 个字节 32*8 = 256
 
 
    // 加密:
    let encrypted = AESUtil.encrypt(content, aesKey, IV); 
 
    // 解密:
    let decrypted = AESUtil.decrypt(encrypted, aesKey, IV);
 
    console.log("密钥为:" + aesKey)
    console.log("初始化向量为:" + IV)
    console.log("密文:" + encrypted)
    console.log("明文:" + decrypted)
    console.log("解密之后是否与之前相同:" + (content === decrypted))

 

Java:

    public static void main(String[] args){
        String content = "abcdefg789+-*+="; // 待加密的字符串
 
        String aesKey = "I0YmM5NTgtY2IyYi00OWIzLWFkZTktZj";
        String iv = "BjNzhiZDctOGMxOS";
 
        try {
            // 加密
            String encrypted = AESUtil.encryptAES(content.getBytes(), aesKey, iv);
            // 解密
            String decrypted = AESUtil.decryptAES(encrypted.getBytes(), aesKey, iv);
 
            System.out.println("加密之后的密文为:" + encrypted +
                    "
" + "解密之后的明文为:" + decrypted +
                    "
" + "原始明文为:" + content +
                    "
" + "解密是否成功:" + (content.equals(decrypted)));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

RSA

    前端 JS 实现:

/**
 * RSA 加密算法封装,
 * 对外提供密钥的生成、加密、解密
 * 本模块测试时使用的是:node-rsa@1.0.1
 */
 
let NodeRSA = require("node-rsa");
 
export var RSAUtil = {
    /**
     * 生成 RSA 密钥对
     * @returns 以 PEM 格式返回 公钥与私钥 的组合对象 
     */
    getRSAKeyPair: function () {
        // 生成空对象
        let keyPair = new NodeRSA();
        keyPair.setOptions({
            encryptionScheme:"pkcs1"
        })
        // keyPairObj, 保存经 BASE64 编码处理之后 PEM 格式的 RSA 密钥对
        let keyPairObj = {
            publicKey: '',
            privateKey: ''
        };
        // keysize: 2048; 公指数为:65537
        keyPair.generateKeyPair(2048, 65537);
        /**
         * 导出密钥,对输出的密钥做一些格式化处理,以便 Java 端能直接使用,算然经过处理但是并不影响 JS 端的密钥导入,及正确性。
         * 1. 公钥
         * 2. 私钥
         */
        keyPairObj.publicKey = keyPair.exportKey("pkcs8-public-pem").replace(/-----BEGIN PUBLIC KEY-----/, '').replace(/-----END PUBLIC KEY-----/, '').replace(/
/g, '');
        keyPairObj.privateKey = keyPair.exportKey("pkcs8-private-pem").replace(/-----BEGIN PRIVATE KEY-----/, '').replace(/-----END PRIVATE KEY-----/, '').replace(/
/g, '');
 
        return keyPairObj;
    },
 
    /**
     * 公钥加密,加密之后以 BASE64 形式编码
     * @param buffer : 待加密内容 编码格式:utf-8
     * @param publicKey: 加密使用的公钥, 格式是:pkcs8 pem
     * @param encoding: 加密之后的输出编码类型 默认输出编码格式是:base64
     * @param source_encoding: 指定代加密内容的编码方式,默认是:utf-8
     * @returns: 返回以 BASE64 处理之后的加密内容
     */
    publicKeyEncrypt: function(buffer, pubicKey, encoding = "base64", source_encoding = "utf8"){
        // 导入 publickey
        let key = new NodeRSA();
        key.setOptions({
            encryptionScheme:"pkcs1", // 默认是:pkcs1_oaep,Java 端默认是 pkcs1, 这里做个修改
        })
        key.importKey(pubicKey, "pkcs8-public-pem");
        // 加密并返回加密结果
        return key.encrypt(buffer, encoding, source_encoding);
    },
 
    /**
     * 私钥解密,解密之后 返回 utf8编码的字符串
     * @param buffer: Buffer object or base64 encoded string
     * @param privateKey: 解密用的私钥,格式是:pkcs8 pem
     * @param encoding: 加密之后的类型 buffer OR json, 默认是 buffer
     * @returns:默认返回值类型就是 encoding 的默认值,即 buffer
     */
    privateKeyDecrypt: function(buffer, privateKey, encoding = "buffer"){
        // 导入 privatekey
        let key = new NodeRSA();
        key.setOptions({
            encryptionScheme: "pkcs1", // 默认是:pkcs1_oaep,Java 端默认是 pkcs1, 这里做个修改
        })
        key.importKey(privateKey, "pkcs8-private-pem");
        // 解密
        return key.decrypt(buffer, encoding);
    }
}

后端 Java 实现:

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
 
/**
 * RSA 是非对称的密码算法,密钥分公钥和私钥, 可以使用一方加密另一方解密,不过由于私钥长度往往很长,
 * 考虑到对网络资源的消耗,一般就公开公钥,使用公钥加密,私钥进行解密,所以这里只提供这种模式需要
 * 的方法。
 * 对外提供密钥对生成、密钥转换、公钥加密、私钥解密方法
 */
 
public class RSAUtil {
    /**
     * 生成密钥对:密钥对中包含公钥和私钥
     * @return 包含 RSA 公钥与私钥的 keyPair
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static KeyPair getKeyPair() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");    // 获得RSA密钥对的生成器实例
        SecureRandom secureRandom = new SecureRandom(String.valueOf(System.currentTimeMillis()).getBytes("utf-8")); // 说的一个安全的随机数
        keyPairGenerator.initialize(2048, secureRandom);    // 这里可以是1024、2048 初始化一个密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();   // 获得密钥对
        return keyPair;
    }
 
    /**
     * 获取公钥 (并进行 Base64 编码,返回一个 Base64 编码后的字符串)
     * @param keyPair:RSA 密钥对
     * @return 返回一个 Base64 编码后的公钥字符串
     */
    public static String getPublicKey(KeyPair keyPair){
        PublicKey publicKey = keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
 
        return Base64.getEncoder().encodeToString(bytes);
    }
 
    /**
     * 获取私钥(并进行Base64编码,返回一个 Base64 编码后的字符串)
     * @param keyPair:RSA 密钥对
     * @return 返回一个 Base64 编码后的私钥字符串
     */
    public static String getPrivateKey(KeyPair keyPair){
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }
 
    /**
     * 将 Base64 编码后的公钥转换成 PublicKey 对象
     * @param pubStr:Base64 编码后的公钥字符串
     * @return PublicKey
     */
    public static PublicKey string2PublicKey(String pubStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = Base64.getDecoder().decode(pubStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }
 
    /**
     * 将 Base64 码后的私钥转换成 PrivateKey 对象
     * @param priStr:Base64 编码后的私钥字符串
     * @return PrivateKey
     */
    public static PrivateKey string2Privatekey(String priStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = Base64.getDecoder().decode(priStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }
 
    /**
     * 公钥加密
     * @param content 待加密的内容 byte[]
     * @param publicKey 加密所需的公钥对象 PublicKey
     * @return 加密后的字节数组 byte[]
     */
    public static byte[] publicEncrytype(byte[] content, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] bytes = cipher.doFinal(content);
        return bytes;
    }
 
    /**
     * 私钥解密
     * @param content   待解密的内容 byte[],这里要注意,由于我们中间过程用的都是 BASE64 ,所以在传入参数前应先进行 BASE64 解析
     * @param privateKey    解密需要的私钥对象 PrivateKey
     * @return 解密后的字节数组 byte[],这里是元数据,需要根据情况自行转码
     */
    public static byte[] privateDecrypt(byte[] content, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] bytes = cipher.doFinal(content);
        return bytes;
    }
}

RSA 测试:
RSA 加密算法是非对称加密算法,密钥匙成对出现的,可以使用一方加密另一方解密,不过由于四幺长度往往很长,考虑到对网络资源的消耗,一般就公开公钥,使用公钥进行加密,私钥进行解密,所以上述封装都是仅提供这种模式的封装。这里还要特别注意的一点时,RSA 在加密的时候即使密钥不变,每一次加密相同的数据之后的密文结果也是不一样的。如下图:

 

鉴于RSA的这些特殊性,测试的时候,除了要保证密钥对相同之外,还应采用一端( 例如:JS 使用 publicKey 加密数据 )加另一端( Java 使用 privateKey 解密数据 )解密,只要保证解密之后的结果是对的就 OK 了。鉴于这种思路进行如下测试:

JS:

import {RSAUtil} from "./RSAUtil"
// RSA 测试
{
    let content = "abcdefg456789+-"
 
    // 生成密钥对
    // let keyPair = RSAUtil.getRSAKeyPair();
    
    let keyPair = {};
    keyPair.publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs02q0YdzYndJXqvr+DiFwfeQ+VSXSz3yj6vlCmvcorkKs0qIchDNd20/xY12a8hh2p8HkAt8lTL0qpC+dDahMJk8OIoeSltAHPwneUMu6EdeG5F5HNBQfuzFcFDEjZFI2mdUMsSFZqyw5HlNGF12YPNbOrR5FTiRcTRUgzlvcXM1gDDwbxpZY3rNZpoXvIwpsKMrlB+DTkn9802Qwrs07u+UCaCCqxnAQGmCiGwKbha/jQTa/1Y5aTtC9Zn+RPjvjZ+M03GZfin3u0rhLRGNJfcRsDd5zcdsZwsf8fId+TpcuHOOUvvkcLT/WEL5I5TQV0o2AR41BNW2cGOxFL+0xwIDAQAB"
    keyPair.privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzTarRh3Nid0leq+v4OIXB95D5VJdLPfKPq+UKa9yiuQqzSohyEM13bT/FjXZryGHanweQC3yVMvSqkL50NqEwmTw4ih5KW0Ac/Cd5Qy7oR14bkXkc0FB+7MVwUMSNkUjaZ1QyxIVmrLDkeU0YXXZg81s6tHkVOJFxNFSDOW9xczWAMPBvGlljes1mmhe8jCmwoyuUH4NOSf3zTZDCuzTu75QJoIKrGcBAaYKIbApuFr+NBNr/VjlpO0L1mf5E+O+Nn4zTcZl+Kfe7SuEtEY0l9xGwN3nNx2xnCx/x8h35Oly4c45S++RwtP9YQvkjlNBXSjYBHjUE1bZwY7EUv7THAgMBAAECggEAQHjD3DV1Ism7owP0hDtmtRkcktp80DxFFK39XGLuYcBhfZhmOYWbK78nuBQmqZjSvraCFKRctpUs7ou/P7BJA12GDtpzC8+F3SY511t16WWIDCehwd+RoiHm2HziP/kmlgmjd+G8CfA8ZtrLAuDQaQn4GsK76wp9GZR0cv7a+JKW0LyaTDidTx8fYGsKHV4odps/erR08SBBs5rUETRXcJpGktOUe7k7YlNHCJf+Y1W5yKKehWvTh71veayhjBzxv9RqR0fVgnJDUMVEPATaTj1pVKyZgA/9oia9m8yXAnCVR6GutH0P1FisFijnAN1CpVbHeKhUy1KLZeemGG0zgQKBgQDqQRg9HBwfAHbzWE2NLRcCPIl5UOimYFeRRSBY+7DfDZFCu/jfmgh4mhrlJzDkny7dAJGtfSSM+ozppahuDB6uZjWHrbKZu2WG0qQwWojDAKsfY/TbsWlFm+TLhfALsFnSlVbTpUWqcG6/B/Jdd07ieccGFwTfLNCFdH6b64vDBwKBgQDD8rMHKQcvTEXdQTZGqJ1crTwqXuU3XMOau4h27VR7snDe4xpwl1rsP5W5SiraP5/9EtPwGKwjnEPUSWRHvj2wtBs/SB4A1VBdzKY+yM98eFzSf9HJZRra42SlU/Fw/jHKkRAUkp7gluc5DwZi7JfuxXS1RVgfMWbsTLKT8udQQQKBgQDlPUqBEu8aD5RYU0OhMkzf7WoDBICHwKQxD1q2eaf+wAI1Mko8VzqO+w/yzEV2laiAsbvd8SdBpzcatvh6qPWlaXRdEEhFVTPnml7+yronSpIrp9/I1nbUndhqquncJnngMDDF8WiZgGmAHEC74rOZwd5YQVKNLAfrcbMs1nbxJQKBgQC3SfOq8/bTiF4lq5VQnPKtuSH5ZFC265/QwjDRRgjruCuaYgbeYMXdDVFJRBY3lqJaAN2czgdfPBG6pngWH97mxmJiXFwsXVzSkNbFDeP/wzrYcFXVNCzdqS0A9Td4gV4j5HONOuVAogdhuSs5J6Sq5arY0Sev7e8fhFLaz7ENwQKBgQDjRRfpDUqxeZNjjx0Huu5EFe776ATLxhXHJT7xT7+NXKQK+SwzGMnYPK6fHlkoCOACIX0yqm2CqFNv/XT2rYJ2mFsXs4uDYV7QSDfqJ4fMyUQxsYzqQH/qBi2gP630+il7UmIA7Uddsfl6Wx8ugICX2frUTLNu6/B0a0hFiO++bw=="
    
    let encrypted = RSAUtil.publicKeyEncrypt(content, keyPair.publicKey);
    let dencrypted = RSAUtil.privateKeyDecrypt(encrypted, keyPair.privateKey);
    console.dir("密钥对:" + 
        "
	" + "公钥:" + keyPair.publicKey + 
        "
	" + "私钥:" + keyPair.privateKey
        )
    console.log("加密之后的密文(经 BASE64 处理):" + encrypted)
    console.log("解密之后的明文:" + dencrypted.toString('utf8') +
        "
" + "原始明文:" + content + 
        "
" + "测试是否通过:" + ( dencrypted.toString('utf8') === content )
    )
}

Java:

      取上面红圈内的内容到 Java 测试代码中,使用相同的 密钥对 其进行解密,解密成功就表示测试通过。

    public static void main(String[] args){
        // 待加密内容
        String content = "abcdefg456789+-";
 
        // 经 BASE64 处理之后的公钥和私钥
        String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs02q0YdzYndJXqvr+DiFwfeQ+VSXSz3yj6vlCmvcorkKs0qIchDNd20/xY12a8hh2p8HkAt8lTL0qpC+dDahMJk8OIoeSltAHPwneUMu6EdeG5F5HNBQfuzFcFDEjZFI2mdUMsSFZqyw5HlNGF12YPNbOrR5FTiRcTRUgzlvcXM1gDDwbxpZY3rNZpoXvIwpsKMrlB+DTkn9802Qwrs07u+UCaCCqxnAQGmCiGwKbha/jQTa/1Y5aTtC9Zn+RPjvjZ+M03GZfin3u0rhLRGNJfcRsDd5zcdsZwsf8fId+TpcuHOOUvvkcLT/WEL5I5TQV0o2AR41BNW2cGOxFL+0xwIDAQAB";
        String privateKeyStr = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzTarRh3Nid0leq+v4OIXB95D5VJdLPfKPq+UKa9yiuQqzSohyEM13bT/FjXZryGHanweQC3yVMvSqkL50NqEwmTw4ih5KW0Ac/Cd5Qy7oR14bkXkc0FB+7MVwUMSNkUjaZ1QyxIVmrLDkeU0YXXZg81s6tHkVOJFxNFSDOW9xczWAMPBvGlljes1mmhe8jCmwoyuUH4NOSf3zTZDCuzTu75QJoIKrGcBAaYKIbApuFr+NBNr/VjlpO0L1mf5E+O+Nn4zTcZl+Kfe7SuEtEY0l9xGwN3nNx2xnCx/x8h35Oly4c45S++RwtP9YQvkjlNBXSjYBHjUE1bZwY7EUv7THAgMBAAECggEAQHjD3DV1Ism7owP0hDtmtRkcktp80DxFFK39XGLuYcBhfZhmOYWbK78nuBQmqZjSvraCFKRctpUs7ou/P7BJA12GDtpzC8+F3SY511t16WWIDCehwd+RoiHm2HziP/kmlgmjd+G8CfA8ZtrLAuDQaQn4GsK76wp9GZR0cv7a+JKW0LyaTDidTx8fYGsKHV4odps/erR08SBBs5rUETRXcJpGktOUe7k7YlNHCJf+Y1W5yKKehWvTh71veayhjBzxv9RqR0fVgnJDUMVEPATaTj1pVKyZgA/9oia9m8yXAnCVR6GutH0P1FisFijnAN1CpVbHeKhUy1KLZeemGG0zgQKBgQDqQRg9HBwfAHbzWE2NLRcCPIl5UOimYFeRRSBY+7DfDZFCu/jfmgh4mhrlJzDkny7dAJGtfSSM+ozppahuDB6uZjWHrbKZu2WG0qQwWojDAKsfY/TbsWlFm+TLhfALsFnSlVbTpUWqcG6/B/Jdd07ieccGFwTfLNCFdH6b64vDBwKBgQDD8rMHKQcvTEXdQTZGqJ1crTwqXuU3XMOau4h27VR7snDe4xpwl1rsP5W5SiraP5/9EtPwGKwjnEPUSWRHvj2wtBs/SB4A1VBdzKY+yM98eFzSf9HJZRra42SlU/Fw/jHKkRAUkp7gluc5DwZi7JfuxXS1RVgfMWbsTLKT8udQQQKBgQDlPUqBEu8aD5RYU0OhMkzf7WoDBICHwKQxD1q2eaf+wAI1Mko8VzqO+w/yzEV2laiAsbvd8SdBpzcatvh6qPWlaXRdEEhFVTPnml7+yronSpIrp9/I1nbUndhqquncJnngMDDF8WiZgGmAHEC74rOZwd5YQVKNLAfrcbMs1nbxJQKBgQC3SfOq8/bTiF4lq5VQnPKtuSH5ZFC265/QwjDRRgjruCuaYgbeYMXdDVFJRBY3lqJaAN2czgdfPBG6pngWH97mxmJiXFwsXVzSkNbFDeP/wzrYcFXVNCzdqS0A9Td4gV4j5HONOuVAogdhuSs5J6Sq5arY0Sev7e8fhFLaz7ENwQKBgQDjRRfpDUqxeZNjjx0Huu5EFe776ATLxhXHJT7xT7+NXKQK+SwzGMnYPK6fHlkoCOACIX0yqm2CqFNv/XT2rYJ2mFsXs4uDYV7QSDfqJ4fMyUQxsYzqQH/qBi2gP630+il7UmIA7Uddsfl6Wx8ugICX2frUTLNu6/B0a0hFiO++bw==";
 
        try{
            PrivateKey privateKey = RSAUtil.string2Privatekey(privateKeyStr);
            PublicKey publicKey = RSAUtil.string2PublicKey(publicKeyStr);
 
 
            // 加密
//            byte[] encrypteds = RSAUtil.publicEncrytype(content.getBytes(), publicKey);
//            String encryptedBase64Str = Base64.getEncoder().encodeToString(encrypteds);
 
             String encryptedBase64Str = "MPgbiM3u3ZnY0X9ilqO6+hzlxXEEKYGDMdxbZMQOXAMN149WBK/i1AB8/TnSmbclcqC6oy/Xe3UnqxWJmiybTz8hXdAdZJ6HmWWN8LXajv+1ElJJr+ZKDd3kg/cj39UKmwSmCEccX8Ntaj8BYXPxvzZfAuRqp9xjQ5SQtvcxJu4E4dZ1ecnvWvWiYI9qnJWLjMUhPaqWwJlX1fS4dKTp8GCJ+ZMdh6+oU9090famP0l4RN8nxnIiJPEKX3Z/si5Ad2MjoeFyZKjyuHihyGGZ5Ilpvb4DZzqcv55uRKvr0D4XZDcZN9eumx+rcqUGtV2RsIpBsqleqNJ4y1Z0jk5YMA==";
             byte[] encrypteds = Base64.getDecoder().decode(encryptedBase64Str);
 
 
            // 解密
            byte[] dencrypteds = RSAUtil.privateDecrypt(encrypteds, privateKey);
            // byte[] dencrypteds = RSAUtil.privateDecrypt(Base64.getDecoder().decode(encryptedBase64Str), privateKey);
            String dencrypted = new String(dencrypteds, "utf8");
 
            // 信息打印
            System.out.println(
                    "公钥为(BASE64 处理之后):" + publicKeyStr + "
"
                    + "私钥为(BASE64 处理之后):" + privateKeyStr + "
"
                    + "公钥加密密文(BASE64 处理之后):" + encryptedBase64Str + "
"
                    + "私钥解密之后(UTF-8 处理之后):" + dencrypted + "
"
                    + "Java平台测试是否通过:" + (dencrypted.equals(content))
            );
 
            RSAUtil.getPublicKey(RSAUtil.getKeyPair());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

————————————————
版权声明:本文为CSDN博主「「已注销」」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gulang03/article/details/82230408

原文地址:https://www.cnblogs.com/Im-Victor/p/15155951.html