springboot + vue 前后端加密传输 RSA互相加解密、加签验签、密钥对生成
作者:mmseoamin日期:2024-04-29

参考

二、关于PKCS#1和PKCS#8格式密钥

由于Java非对称加解密、加验签都是采用PKCS#8格式的密钥,PKCS#1格式的密钥跑不通,这里先简单介绍一下两者的区别。

1、简介

PKCS#1和PKCS#8是两个不同的数字证书标准。

PKCS#1是一个公钥加密标准,它定义了使用RSA算法进行加密和签名的格式。主要用于对数字签名、加密以及数字签名验证等应用。

PKCS#8则是一个私钥保护标准,它定义了私钥的存储格式。它主要用于在文件中对私钥进行保护,以防止意外泄露或不当使用。

总的来说,PKCS#1是针对公钥的标准,而PKCS#8是针对私钥的标准。

2、区别

两者的密钥格式不一样,下面以标准.pem格式为例,看下PKCS#1格式和PKCS#8格式密钥的区别:

  • PKCS#1格式私钥:
    -----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQC5BW6T9GVaaG/epGDjPpY3wN0DrBt+NojvxkEgpUdOAxgAepqe
    ...
    TbzKH/LEqZN8WVau3bf41yAx2YoaOsIJJtOUTYcfh14=
    -----END RSA PRIVATE KEY-----
    
    • PKCS#8格式私钥:
      -----BEGIN PRIVATE KEY-----
      MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALkFbpP0ZVpob96k
      ...
      wgkm05RNhx+HXg==
      -----END PRIVATE KEY-----
      
      • PKCS#1格式公钥:
        -----BEGIN RSA PUBLIC KEY-----
        MIICXAIBAAKBgQC5BW6T9GVaaG/epGDjPpY3wN0DrBt+NojvxkEgpUdOAxgAepqe
        ...
        TbzKH/LEqZN8WVau3bf41yAx2YoaOsIJJtOUTYcfh14=
        -----END RSA PUBLIC KEY-----
        
        • PKCS#8格式公钥:
          -----BEGIN PUBLIC KEY-----
          MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALkFbpP0ZVpob96k
          ...
          wgkm05RNhx+HXg==
          -----END PUBLIC KEY-----
          

          四、前端RSA加解密、加验签示例

          1、相关依赖

          // JSEncrypt
          npm i jsencrypt
          // jsrsasign
          npm i jsrsasign
          

          2、cryptoUtils工具类封装

          import CryptoJS from "crypto-js";
          import JSEncrypt from "jsencrypt";
          import JsRsaSign from "jsrsasign";
          /**
           * RSA加密
           * @param publicKey 公钥
           * @param plainText 明文
           * @returns {*} 密文
           */
          export function encryptByRSA(publicKey, plainText) {
            const encryptor = new JSEncrypt();
            encryptor.setPublicKey(publicKey);
            return encryptor.encrypt(plainText);
          }
          /**
           * RSA解密
           * @param privateKey 私钥
           * @param cipherText 密文
           * @returns {*} 明文
           */
          export function decryptByRSA(privateKey, cipherText) {
            const decrypter = new JSEncrypt();
            decrypter.setPrivateKey(privateKey);
            return decrypter.decrypt(cipherText);
          }
          /**
           * 生成RSA密钥对,填充模式为PKCS8。
           * 更多模式参考:https://kjur.github.io/jsrsasign/api/symbols/KEYUTIL.html
           * @returns {{privateKey: (string|string|*), publicKey: (string|string|*)}}
           */
          export function generateRsaKeyWithPKCS8() {
            const keyPair = JsRsaSign.KEYUTIL.generateKeypair("RSA", 1024);
            const privateKey = JsRsaSign.KEYUTIL.getPEM(keyPair.prvKeyObj, "PKCS8PRV");
            const publicKey = JsRsaSign.KEYUTIL.getPEM(keyPair.pubKeyObj);
            return { privateKey, publicKey };
          }
          /**
           * SHA256和RSA加签
           * @param privateKey 私钥
           * @param msg 加签内容
           * @returns {string} Base64编码签名内容
           */
          export function signBySHA256WithRSA(privateKey, msg) {
            const key = JsRsaSign.KEYUTIL.getKey(privateKey);
            const signature = new JsRsaSign.KJUR.crypto.Signature({
              alg: "SHA256withRSA",
            });
            signature.init(key);
            signature.updateString(msg);
            // 签名后的为16进制字符串,这里转换为16进制字符串
            return JsRsaSign.hextob64(signature.sign());
          }
          /**
           * SHA256和RSA验签
           * @param publicKey 公钥:必须为标准pem格式。如果是PKCS1格式,必须包含-----BEGIN RSA PRIVATE KEY-----,如果是PKCS8格式,必须包含-----BEGIN PRIVATE KEY-----
           * @param base64SignStr Base64编码签名字符串
           * @param msg 原内容
           * @returns {boolean} 是否验签通过
           */
          export function verifyBySHA256WithRSA(publicKey, base64SignStr, msg) {
            const key = JsRsaSign.KEYUTIL.getKey(publicKey);
            const signature = new JsRsaSign.KJUR.crypto.Signature({
              alg: "SHA256withRSA",
            });
            signature.init(key);
            signature.updateString(msg);
            // 需要将Base64进制签名字符串转换成16进制字符串
            return signature.verify(JsRsaSign.b64tohex(base64SignStr));
          }
          

          3、测试用例

          先在前端测试下密钥对生成,RSA加解密、加验签,测试代码如下:

          import * as CryptoUtils from '@/utils/cryptoUtils.js';
          const {privateKey, publicKey} = CryptoUtils.generateRsaKeyWithPKCS8();
          console.log(`生成的私钥为:\n${privateKey}`);
          console.log(`生成的公钥为:\n${publicKey}`);
          const cipherText = CryptoUtils.encryptByRSA(publicKey, "test");
          console.log(`test加密后的内容为:\n${cipherText}`);
          const plainText = CryptoUtils.decryptByRSA(privateKey, cipherText);
          console.log(`解密后的内容为:\n${plainText}`);
          const signature = CryptoUtils.signBySHA256WithRSA(privateKey, "test");
          console.log(`生成的签名:\n${signature}`);
          const isVerified = CryptoUtils.verifyBySHA256WithRSA(publicKey, signature, "test");
          console.log(`是否验签通过:${isVerified}`);
          

          控制台输出结果为:

          生成的私钥为:
          -----BEGIN PRIVATE KEY-----
          MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMv1adNmKNw4rtr2
          Dy92DZ3Nk1SCrGGetlq0sSgLY8bJsByAvP2ZOOiiWq2xDUVu8ZpQfM8v89tjegfr
          sIUcN/ZVLc68Ric3BIto85oQc9jBz+aCk1rXeNMePkSVqNaC/n4kJ73Y41ZgKAe0
          GvyqMyUNhk8VZd+QSNATSv+lGep3AgMBAAECgYBoKMvDry98z+HUZsb4iQSJK1xr
          U1SvgftEtXSnq7Fn6sZquABMTry2aXt/qqTJadAu653hvW5/Av1mICKEyBV3aT4O
          jQRGPMgp6WhXvQepUIuyi9qlfUVsJy/+J0zGKZeKsCFlwZ2e2j4Un7Bb//pgUfjJ
          rbPtwC7U85oHjtJb6QJBAOdcm07ThSXFbicj2MuX9Gh7geMjncf6aqnrOwUFjO0d
          5OxfYRAxrZD1GghygHyoJ4ZOHgJ0s6HVEYjg/u6DBdsCQQDhrb4IOVdSew2cW15f
          t/5DAKUXRRQBfz0OxOs0Uv5k7zqI+YmysWVRGaZgj8oMZ7gYxN1eYNOKTwVjiuwb
          uyaVAkEA0OGSMpPT1WsvbVT26bFyb1Z6yTihvif/XxPKgFknh/kCcsoWFwnS+1ne
          vBusl181+BLVE0CL4aM9pogEghB3GwJAWJTVzmyTdfCO+xxyAqg5yRrrsiKPI7dJ
          xA5PNA6PhBbSpwkrn1Q6LIcg4y4NZKkhfbdoHK9s2REDUHsrCgd/sQJAALEe+PCX
          hcHWnwbm4kRFyJCO4dWkii7o28ohTRourlNsoEmiu1+7lt7PY1+C3D+6A4FFCY/H
          pGM0i0lJue8rZA==
          -----END PRIVATE KEY-----
          生成的公钥为:
          -----BEGIN PUBLIC KEY-----
          MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL9WnTZijcOK7a9g8vdg2dzZNU
          gqxhnrZatLEoC2PGybAcgLz9mTjoolqtsQ1FbvGaUHzPL/PbY3oH67CFHDf2VS3O
          vEYnNwSLaPOaEHPYwc/mgpNa13jTHj5ElajWgv5+JCe92ONWYCgHtBr8qjMlDYZP
          FWXfkEjQE0r/pRnqdwIDAQAB
          -----END PUBLIC KEY-----
          test加密后的内容为:
          KjcaDKLnBbvxRzuKMysqoz9MHRXCUNIH67+XDiFGTJbM8Rjw4Cei0CzjAPjk2jgAR37Kgh6lX2+Xg8AI9wEmzWr08bt8i2FFxVMrcfOCs5zI1y+2T7G9034f5b0gNx/Pc4dDz+1k453vo0AhCC0vrtb1OfbsRu5oOFns0TqoAMY=
          解密后的内容为:test
          生成的签名:
          t0koTqhiWmq/wEvI/ieJq5kZj7Dc/limF7GNVtHNLReqLVBXZvAZrOIwdqda7LBHBSHcRZBISWtbuyDiOR9KFPObrOgOEUOdfACUMzjWKCtO8ZgcQ+U02FyGeeH2rT9rJEJAXDEM+Kn3+H4ZdbrUFPY3jQRl535wnK9CLpxqAG4=
          是否验签通过:true
          
          备注:为什么在前端生成PKCS#8格式密钥呢?

          因为在Java中非对称加解密、加验签都是用的PKCS#8,PKCS#1格式密钥需要转换成PKCS#8。

          五、Java后端RSA加解密、加验签

          1、CryptoUtils工具类封装

          import com.universe.crypto.CryptoUtils.Algorithm.Encryption;
          import com.universe.crypto.CryptoUtils.Algorithm.Signing;
          import lombok.AllArgsConstructor;
          import lombok.Data;
          import lombok.Getter;
          import lombok.NoArgsConstructor;
          import org.apache.commons.lang3.StringUtils;
          import javax.crypto.Cipher;
          import javax.crypto.KeyGenerator;
          import javax.crypto.NoSuchPaddingException;
          import javax.crypto.SecretKey;
          import javax.crypto.spec.IvParameterSpec;
          import javax.crypto.spec.SecretKeySpec;
          import java.nio.charset.Charset;
          import java.nio.charset.StandardCharsets;
          import java.security.Key;
          import java.security.KeyFactory;
          import java.security.KeyPair;
          import java.security.KeyPairGenerator;
          import java.security.NoSuchAlgorithmException;
          import java.security.PrivateKey;
          import java.security.PublicKey;
          import java.security.Signature;
          import java.security.spec.InvalidKeySpecException;
          import java.security.spec.KeySpec;
          import java.security.spec.PKCS8EncodedKeySpec;
          import java.security.spec.X509EncodedKeySpec;
          import java.util.Base64;
          import java.util.Base64.Decoder;
          import java.util.Base64.Encoder;
          import java.util.HashMap;
          import java.util.Map;
          import java.util.concurrent.ConcurrentHashMap;
          /**
           * 支持AES、DES、RSA加密、数字签名以及生成对称密钥和非对称密钥对
           */
          public class CryptoUtils {
          	private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
          	private static final Encoder BASE64_ENCODER = Base64.getEncoder();
          	private static final Decoder BASE64_DECODER = Base64.getDecoder();
          	private static final Map KEY_FACTORY_CACHE = new ConcurrentHashMap<>();
          	private static final Map CIPHER_CACHE = new HashMap<>();
          	/**
          	 * 生成对称密钥,目前支持的算法有AES、DES
          	 * @param algorithm
          	 * @return
          	 * @throws NoSuchAlgorithmException
          	 */
          	public static String generateSymmetricKey(Algorithm algorithm) throws NoSuchAlgorithmException {
          		KeyGenerator generator = KeyGenerator.getInstance(algorithm.getName());
          		generator.init(algorithm.getKeySize());
          		SecretKey secretKey = generator.generateKey();
          		return BASE64_ENCODER.encodeToString(secretKey.getEncoded());
          	}
          	/**
          	 * 生成非对称密钥对,目前支持的算法有RSA、DSA。备注:默认生成的密钥格式为PKCS8
          	 * @param algorithm
          	 * @return
          	 * @throws NoSuchAlgorithmException
          	 */
          	public static AsymmetricKeyPair generateAsymmetricKeyPair(Algorithm algorithm) throws NoSuchAlgorithmException {
          		KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm.getName());
          		generator.initialize(algorithm.getKeySize());
          		KeyPair keyPair = generator.generateKeyPair();
          		String publicKey = BASE64_ENCODER.encodeToString(keyPair.getPublic().getEncoded());
          		String privateKey = BASE64_ENCODER.encodeToString(keyPair.getPrivate().getEncoded());
          		return new AsymmetricKeyPair(publicKey, privateKey);
          	}
          	public static String encryptByRSA(String publicKeyText, String plainText) throws Exception {
          		return encryptAsymmetrically(publicKeyText, plainText, Encryption.RSA_ECB_PKCS1);
          	}
          	public static String decryptByRSA(String privateKeyText, String ciphertext) throws Exception {
          		return decryptAsymmetrically(privateKeyText, ciphertext, Encryption.RSA_ECB_PKCS1);
          	}
          	/**
          	 * SHA1签名算法和DSA加密算法结合使用生成数字签名
          	 * @param privateKeyText
          	 * @param msg
          	 * @return 数字签名
          	 * @throws Exception
          	 */
          	public static String signBySHA1WithDSA(String privateKeyText, String msg) throws Exception {
          		return doSign(privateKeyText, msg, Encryption.DSA, Signing.SHA1WithDSA);
          	}
          	/**
          	 * SHA1签名算法和RSA加密算法结合使用生成数字签名
          	 * @param privateKeyText 私钥
          	 * @param msg 待加签内容
          	 * @return 数字签名
          	 * @throws Exception
          	 */
          	public static String signBySHA1WithRSA(String privateKeyText, String msg) throws Exception {
          		return doSign(privateKeyText, msg, Encryption.RSA_ECB_PKCS1, Signing.SHA1WithRSA);
          	}
          	/**
          	 * SHA256签名算法和RSA加密算法结合使用生成数字签名
          	 * @param privateKeyText 私钥
          	 * @param msg 待加签内容
          	 * @return 数字签名
          	 * @throws Exception
          	 */
          	public static String signBySHA256WithRSA(String privateKeyText, String msg) throws Exception {
          		return doSign(privateKeyText, msg, Encryption.RSA_ECB_PKCS1, Signing.SHA256WithRSA);
          	}
          	/**
          	 * SHA1签名算法和DSA加密算法检验数字签名
          	 * @param publicKeyText 公钥
          	 * @param msg 待验签内容
          	 * @param signatureText 数字
          	 * @return 检验是否成功
          	 * @throws Exception
          	 */
          	public static boolean verifyBySHA1WithDSA(String publicKeyText, String msg, String signatureText) throws Exception {
          		return doVerify(publicKeyText, msg, signatureText, Encryption.DSA, Signing.SHA1WithDSA);
          	}
          	/**
          	 * SHA1签名算法和RSA加密算法检验数字签名
          	 * @param publicKeyText 公钥
          	 * @param msg 待验签内容
          	 * @param signatureText 签名
          	 * @return 校验是否成功
          	 * @throws Exception
          	 */
          	public static boolean verifyBySHA1WithRSA(String publicKeyText, String msg, String signatureText) throws Exception {
          		return doVerify(publicKeyText, msg, signatureText, Encryption.RSA_ECB_PKCS1, Signing.SHA1WithRSA);
          	}
          	/**
          	 * SHA256签名算法和RSA加密算法检验数字签名
          	 * @param publicKeyText 公钥
          	 * @param msg 待验签内容
          	 * @param signatureText 签名
          	 * @return 校验是否成功
          	 * @throws Exception
          	 */
          	public static boolean verifyBySHA256WithRSA(String publicKeyText, String msg, String signatureText) throws Exception {
          		return doVerify(publicKeyText, msg, signatureText, Encryption.RSA_ECB_PKCS1, Signing.SHA256WithRSA);
          	}
          	/**
          	 * 对称加密
          	 * @param secretKey 密钥
          	 * @param iv 加密向量,只有CBC模式才支持,如果是CBC则必传
          	 * @param plainText 明文
          	 * @param algorithm 对称加密算法,如AES、DES
          	 * @return
          	 * @throws Exception
          	 */
          	public static String encryptSymmetrically(String secretKey, String iv, String plainText, Algorithm algorithm) throws Exception {
          		SecretKey key = decodeSymmetricKey(secretKey, algorithm);
          		IvParameterSpec ivParameterSpec = StringUtils.isBlank(iv) ? null : decodeIv(iv);
          		byte[] plainTextInBytes = plainText.getBytes(DEFAULT_CHARSET);
          		byte[] ciphertextInBytes = transform(algorithm, Cipher.ENCRYPT_MODE, key, ivParameterSpec, plainTextInBytes);
          		return BASE64_ENCODER.encodeToString(ciphertextInBytes);
          	}
          	/**
          	 * 对称解密
          	 * @param secretKey 密钥
          	 * @param iv 加密向量,只有CBC模式才支持,如果是CBC则必传
          	 * @param ciphertext 密文
          	 * @param algorithm 对称加密算法,如AES、DES
          	 * @return
          	 * @throws Exception
          	 */
          	public static String decryptSymmetrically(String secretKey, String iv, String ciphertext, Algorithm algorithm) throws Exception {
          		SecretKey key = decodeSymmetricKey(secretKey, algorithm);
          		IvParameterSpec ivParameterSpec = StringUtils.isBlank(iv) ? null : decodeIv(iv);
          		byte[] ciphertextInBytes = BASE64_DECODER.decode(ciphertext);
          		byte[] plainTextInBytes = transform(algorithm, Cipher.DECRYPT_MODE, key, ivParameterSpec, ciphertextInBytes);
          		return new String(plainTextInBytes, DEFAULT_CHARSET);
          	}
          	/**
          	 * 非对称加密
          	 * @param publicKeyText 公钥
          	 * @param plainText 明文
          	 * @param algorithm 非对称加密算法
          	 * @return
          	 * @throws Exception
          	 */
          	public static String encryptAsymmetrically(String publicKeyText, String plainText, Algorithm algorithm) throws Exception {
          		PublicKey publicKey = regeneratePublicKey(publicKeyText, algorithm);
          		byte[] plainTextInBytes = plainText.getBytes(DEFAULT_CHARSET);
          		byte[] ciphertextInBytes = transform(algorithm, Cipher.ENCRYPT_MODE, publicKey, plainTextInBytes);
          		return BASE64_ENCODER.encodeToString(ciphertextInBytes);
          	}
          	/**
          	 * 非对称解密
          	 * @param privateKeyText 私钥
          	 * @param ciphertext 密文
          	 * @param algorithm 非对称加密算法
          	 * @return
          	 * @throws Exception
          	 */
          	public static String decryptAsymmetrically(String privateKeyText, String ciphertext, Algorithm algorithm) throws Exception {
          		PrivateKey privateKey = regeneratePrivateKey(privateKeyText, algorithm);
          		byte[] ciphertextInBytes = BASE64_DECODER.decode(ciphertext);
          		byte[] plainTextInBytes = transform(algorithm, Cipher.DECRYPT_MODE, privateKey, ciphertextInBytes);
          		return new String(plainTextInBytes, DEFAULT_CHARSET);
          	}
          	/**
          	 * 生成数字签名
          	 * @param privateKeyText 私钥
          	 * @param msg 传输的数据
          	 * @param encryptionAlgorithm 加密算法,见Algorithm中的加密算法
          	 * @param signatureAlgorithm 签名算法,见Algorithm中的签名算法
          	 * @return 数字签名
          	 * @throws Exception
          	 */
          	public static String doSign(String privateKeyText, String msg, Algorithm encryptionAlgorithm, Algorithm signatureAlgorithm)
          		throws Exception {
          		PrivateKey privateKey = regeneratePrivateKey(privateKeyText, encryptionAlgorithm);
          		// Signature只支持签名算法
          		Signature signature = Signature.getInstance(signatureAlgorithm.getName());
          		signature.initSign(privateKey);
          		signature.update(msg.getBytes(DEFAULT_CHARSET));
          		byte[] signatureInBytes = signature.sign();
          		return BASE64_ENCODER.encodeToString(signatureInBytes);
          	}
          	/**
          	 * 数字签名验证
          	 * @param publicKeyText 公钥
          	 * @param msg 传输的数据
          	 * @param signatureText 数字签名
          	 * @param encryptionAlgorithm 加密算法,见Algorithm中的加密算法
          	 * @param signatureAlgorithm 签名算法,见Algorithm中的签名算法
          	 * @return 校验是否成功
          	 * @throws Exception
          	 */
          	public static boolean doVerify(String publicKeyText, String msg, String signatureText, Algorithm encryptionAlgorithm,
          		Algorithm signatureAlgorithm) throws Exception {
          		PublicKey publicKey = regeneratePublicKey(publicKeyText, encryptionAlgorithm);
          		Signature signature = Signature.getInstance(signatureAlgorithm.getName());
          		signature.initVerify(publicKey);
          		signature.update(msg.getBytes(DEFAULT_CHARSET));
          		return signature.verify(BASE64_DECODER.decode(signatureText));
          	}
          	/**
          	 * 将密钥进行Base64位解码,重新生成SecretKey实例
          	 * @param secretKey 密钥
          	 * @param algorithm 算法
          	 * @return
          	 */
          	private static SecretKey decodeSymmetricKey(String secretKey, Algorithm algorithm) {
          		byte[] key = BASE64_DECODER.decode(secretKey);
          		return new SecretKeySpec(key, algorithm.getName());
          	}
          	private static IvParameterSpec decodeIv(String iv) {
          		byte[] ivInBytes = BASE64_DECODER.decode(iv);
          		return new IvParameterSpec(ivInBytes);
          	}
          	private static PublicKey regeneratePublicKey(String publicKeyText, Algorithm algorithm)
          		throws NoSuchAlgorithmException, InvalidKeySpecException {
          		byte[] keyInBytes = BASE64_DECODER.decode(publicKeyText);
          		KeyFactory keyFactory = getKeyFactory(algorithm);
          		// 公钥必须使用RSAPublicKeySpec或者X509EncodedKeySpec
          		KeySpec publicKeySpec = new X509EncodedKeySpec(keyInBytes);
          		PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
          		return publicKey;
          	}
          	private static PrivateKey regeneratePrivateKey(String key, Algorithm algorithm) throws Exception {
          		byte[] keyInBytes = BASE64_DECODER.decode(key);
          		KeyFactory keyFactory = getKeyFactory(algorithm);
          		// 私钥必须使用RSAPrivateCrtKeySpec或者PKCS8EncodedKeySpec
          		KeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyInBytes);
          		PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
          		return privateKey;
          	}
          	private static KeyFactory getKeyFactory(Algorithm algorithm) throws NoSuchAlgorithmException {
          		KeyFactory keyFactory = KEY_FACTORY_CACHE.get(algorithm);
          		if (keyFactory == null) {
          			keyFactory = KeyFactory.getInstance(algorithm.getName());
          			KEY_FACTORY_CACHE.put(algorithm, keyFactory);
          		}
          		return keyFactory;
          	}
          	private static byte[] transform(Algorithm algorithm, int mode, Key key, byte[] msg) throws Exception {
          		return transform(algorithm, mode, key, null, msg);
          	}
          	private static byte[] transform(Algorithm algorithm, int mode, Key key, IvParameterSpec iv, byte[] msg) throws Exception {
          		Cipher cipher = CIPHER_CACHE.get(algorithm);
          		// double check,减少上下文切换
          		if (cipher == null) {
          			synchronized (CryptoUtils.class) {
          				if ((cipher = CIPHER_CACHE.get(algorithm)) == null) {
          					cipher = determineWhichCipherToUse(algorithm);
          					CIPHER_CACHE.put(algorithm, cipher);
          				}
          				cipher.init(mode, key, iv);
          				return cipher.doFinal(msg);
          			}
          		}
          		synchronized (CryptoUtils.class) {
          			cipher.init(mode, key, iv);
          			return cipher.doFinal(msg);
          		}
          	}
          	private static Cipher determineWhichCipherToUse(Algorithm algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException {
          		Cipher cipher;
          		String transformation = algorithm.getTransformation();
          		// 官方推荐的transformation使用algorithm/mode/padding组合,SunJCE使用ECB作为默认模式,使用PKCS5Padding作为默认填充
          		if (StringUtils.isNotEmpty(transformation)) {
          			cipher = Cipher.getInstance(transformation);
          		} else {
          			cipher = Cipher.getInstance(algorithm.getName());
          		}
          		return cipher;
          	}
          	/**
          	 * 算法分为加密算法和签名算法,更多算法实现见:
          * jdk8中的标准算法 */ public static class Algorithm { public interface Encryption { Algorithm AES_ECB_PKCS5 = new Algorithm("AES", "AES/ECB/PKCS5Padding", 128); Algorithm AES_CBC_PKCS5 = new Algorithm("AES", "AES/CBC/PKCS5Padding", 128); Algorithm DES_ECB_PKCS5 = new Algorithm("DES", "DES/ECB/PKCS5Padding", 56); Algorithm DES_CBC_PKCS5 = new Algorithm("DES", "DES/CBC/PKCS5Padding", 56); Algorithm RSA_ECB_PKCS1 = new Algorithm("RSA", "RSA/ECB/PKCS1Padding", 1024); Algorithm DSA = new Algorithm("DSA", 1024); } public interface Signing { Algorithm SHA1WithDSA = new Algorithm("SHA1withDSA", 1024); Algorithm SHA1WithRSA = new Algorithm("SHA1WithRSA", 2048); Algorithm SHA256WithRSA = new Algorithm("SHA256WithRSA", 2048); } @Getter private String name; @Getter private String transformation; @Getter private int keySize; public Algorithm(String name, int keySize) { this(name, null, keySize); } public Algorithm(String name, String transformation, int keySize) { this.name = name; this.transformation = transformation; this.keySize = keySize; } } @Data @NoArgsConstructor @AllArgsConstructor public static class AsymmetricKeyPair { private String publicKey; private String privateKey; } }

          2、测试用例

          AsymmetricKeyPair keyPair = CryptoUtils.generateAsymmetricKeyPair(Encryption.RSA_ECB_PKCS1);
          String privateKey = keyPair.getPrivateKey();
          String publicKey = keyPair.getPublicKey();
          System.out.println("生成的私钥为:\n" + privateKey);
          System.out.println("生成的公钥为:\n" + publicKey);
          String cipherText = CryptoUtils.encryptByRSA(publicKey, "test");
          String plainText = CryptoUtils.decryptByRSA(privateKey, cipherText);
          System.out.println("test加密后的密文为:\n" + cipherText);
          System.out.println("解密后的明文为:" + plainText);
          String signature = CryptoUtils.signBySHA256WithRSA(privateKey, "message");
          boolean isVerified = CryptoUtils.verifyBySHA256WithRSA(publicKey, "message", signature);
          System.out.println("message加签后的签名为:" + signature);
          System.out.println("验签是否通过:" + isVerified);
          

          控制台输出如下:

          生成的私钥为:
          MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL4TdBqlxJByJURSqNzthP18t2Q6tzmdwfoTJka0yy/DjEL1/mBPdcygwWBfVYLBvwuBUOyG4pbf/de0pmgc9b7+SwNqqpHtmLxaEqW3ebovm0R2UFom0OS/5X5pdWNKuKDTCpP2xC1JSfRqS+7WNgFEhCK7hRWBzIpifXvLzZNtAgMBAAECgYAcjOd/qS6hU8PtQ01CAhtbyAPz9i3XZa7hVUcGj9mFTyYeWLzg0o6rMepaA3fgsCF2JPJ21LvsVbDXWbc1JER1LYIWYZ79XnYZQgezmfqeSFx+CJkndRZ/qXGnf/1EUJFjEafbkXGRvnv05B4QHBQGn3gfIa2xsLw2r3Yf8M5/gQJBAOXgqeH5yyJ6iNIw5S8EAR7VAUMPteQ/JqQ6l+wYcesj/prqhwRX4x2m/wKh6qM2zZcFLMujYTyJsIQkzmOkFXkCQQDTrO1bnO84dbEVOUv73g0J1jg/3EIbt8uh7T4Iu82/9ycUyQ5D8oANh53rfPf5DXycXWXRlu2gwGvsyNRW1LSVAkEAnYs6go/Cgw+9g2hVOcKhzfKnmcFDpHkPT5CEnB8ou8GAdcVz4SsmkSTpMnGrsE4X2n+Gcs23D1lCK15aQHms6QJBAJ/8GmXcph25Lj9JT/msaZReuaLimYCTmK/pPLKjJy4I4hveng6S8V/IeX4rtMwi+mTAXp1bgny2EpwjagG6wEUCQH9ap3J9fh8UicEPD1LO/GUTTNXuPiwWOcNgetntrjPAJx5b0bf6hEJOyEHtIsuSuzAD8G41o2tpsefact3EwzY=
          生成的公钥为:
          MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+E3QapcSQciVEUqjc7YT9fLdkOrc5ncH6EyZGtMsvw4xC9f5gT3XMoMFgX1WCwb8LgVDshuKW3/3XtKZoHPW+/ksDaqqR7Zi8WhKlt3m6L5tEdlBaJtDkv+V+aXVjSrig0wqT9sQtSUn0akvu1jYBRIQiu4UVgcyKYn17y82TbQIDAQAB
          test加密后的密文为:
          EA19/wkHbdXTc8sfLFhmnp/MWW3PLx2LeYFHWFNdhvY38+Zoa4Ci8HJw8okkxzTfsSkgsiybMaz82rwF9lfcuEWzjbuGeVOvdkI0p/Cv+PDfikMYwOsxA7OqBJ/Hktn25l/ryEv7TxYlMFQ48jB0KPw/0Ivec9qfX2pgnyBl7WM=
          解密后的明文为:test
          message加签后的签名为:
          AByFyRoc/321db16voe9NQaicwkscTOGjBZGefWzB7dMadWXBtUPIK3CUXADLiiesehgAAcDbl06qVz++x/6xeWPCK2ucCfn9dFybZfmAIsn+3TATuDQIFvz/m2cHQAuH9fkmiGgMPOVY/VcILwri3RETuQ+wz4YSmP89o1cFqk=
          验签是否通过:true
          

          ————————————————

          六、前后端加解密、加验签交互测试

          1、前端加密,后端解密

          这里我们用前端生成的密钥对做测试。

          (1) 前端代码
          import * as CryptoUtils from '@/utils/cryptoUtils.js';
          const {privateKey, publicKey} = CryptoUtils.generateRsaKeyWithPKCS8();
          console.log(`生成的私钥为:\n${privateKey}`);
          console.log(`生成的公钥为:\n${publicKey}`);
          const cipherText = CryptoUtils.encryptByRSA(publicKey, "test");
          console.log(`test加密后的内容为:\n${cipherText}`);
          ————————————————
          

          控制台输出:

          生成的私钥为:
          -----BEGIN PRIVATE KEY-----
          MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMv1adNmKNw4rtr2
          Dy92DZ3Nk1SCrGGetlq0sSgLY8bJsByAvP2ZOOiiWq2xDUVu8ZpQfM8v89tjegfr
          sIUcN/ZVLc68Ric3BIto85oQc9jBz+aCk1rXeNMePkSVqNaC/n4kJ73Y41ZgKAe0
          GvyqMyUNhk8VZd+QSNATSv+lGep3AgMBAAECgYBoKMvDry98z+HUZsb4iQSJK1xr
          U1SvgftEtXSnq7Fn6sZquABMTry2aXt/qqTJadAu653hvW5/Av1mICKEyBV3aT4O
          jQRGPMgp6WhXvQepUIuyi9qlfUVsJy/+J0zGKZeKsCFlwZ2e2j4Un7Bb//pgUfjJ
          rbPtwC7U85oHjtJb6QJBAOdcm07ThSXFbicj2MuX9Gh7geMjncf6aqnrOwUFjO0d
          5OxfYRAxrZD1GghygHyoJ4ZOHgJ0s6HVEYjg/u6DBdsCQQDhrb4IOVdSew2cW15f
          t/5DAKUXRRQBfz0OxOs0Uv5k7zqI+YmysWVRGaZgj8oMZ7gYxN1eYNOKTwVjiuwb
          uyaVAkEA0OGSMpPT1WsvbVT26bFyb1Z6yTihvif/XxPKgFknh/kCcsoWFwnS+1ne
          vBusl181+BLVE0CL4aM9pogEghB3GwJAWJTVzmyTdfCO+xxyAqg5yRrrsiKPI7dJ
          xA5PNA6PhBbSpwkrn1Q6LIcg4y4NZKkhfbdoHK9s2REDUHsrCgd/sQJAALEe+PCX
          hcHWnwbm4kRFyJCO4dWkii7o28ohTRourlNsoEmiu1+7lt7PY1+C3D+6A4FFCY/H
          pGM0i0lJue8rZA==
          -----END PRIVATE KEY-----
          生成的公钥为:
          -----BEGIN PUBLIC KEY-----
          MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL9WnTZijcOK7a9g8vdg2dzZNU
          gqxhnrZatLEoC2PGybAcgLz9mTjoolqtsQ1FbvGaUHzPL/PbY3oH67CFHDf2VS3O
          vEYnNwSLaPOaEHPYwc/mgpNa13jTHj5ElajWgv5+JCe92ONWYCgHtBr8qjMlDYZP
          FWXfkEjQE0r/pRnqdwIDAQAB
          -----END PUBLIC KEY-----
          test加密后的内容为:
          KjcaDKLnBbvxRzuKMysqoz9MHRXCUNIH67+XDiFGTJbM8Rjw4Cei0CzjAPjk2jgAR37Kgh6lX2+Xg8AI9wEmzWr08bt8i2FFxVMrcfOCs5zI1y+2T7G9034f5b0gNx/Pc4dDz+1k453vo0AhCC0vrtb1OfbsRu5oOFns0TqoAMY=
          解密后的内容为:test
          ————————————————
          

          (2) 后端代码

          String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMv1adNmKNw4rtr2Dy92DZ3Nk1SCrGGetlq0sSgLY8bJsByAvP2ZOOiiWq2xDUVu8ZpQfM8v89tjegfrsIUcN/ZVLc68Ric3BIto85oQc9jBz+aCk1rXeNMePkSVqNaC/n4kJ73Y41ZgKAe0GvyqMyUNhk8VZd+QSNATSv+lGep3AgMBAAECgYBoKMvDry98z+HUZsb4iQSJK1xrU1SvgftEtXSnq7Fn6sZquABMTry2aXt/qqTJadAu653hvW5/Av1mICKEyBV3aT4OjQRGPMgp6WhXvQepUIuyi9qlfUVsJy/+J0zGKZeKsCFlwZ2e2j4Un7Bb//pgUfjJrbPtwC7U85oHjtJb6QJBAOdcm07ThSXFbicj2MuX9Gh7geMjncf6aqnrOwUFjO0d5OxfYRAxrZD1GghygHyoJ4ZOHgJ0s6HVEYjg/u6DBdsCQQDhrb4IOVdSew2cW15ft/5DAKUXRRQBfz0OxOs0Uv5k7zqI+YmysWVRGaZgj8oMZ7gYxN1eYNOKTwVjiuwbuyaVAkEA0OGSMpPT1WsvbVT26bFyb1Z6yTihvif/XxPKgFknh/kCcsoWFwnS+1nevBusl181+BLVE0CL4aM9pogEghB3GwJAWJTVzmyTdfCO+xxyAqg5yRrrsiKPI7dJxA5PNA6PhBbSpwkrn1Q6LIcg4y4NZKkhfbdoHK9s2REDUHsrCgd/sQJAALEe+PCXhcHWnwbm4kRFyJCO4dWkii7o28ohTRourlNsoEmiu1+7lt7PY1+C3D+6A4FFCY/HpGM0i0lJue8rZA==";
          String cipherText = "KjcaDKLnBbvxRzuKMysqoz9MHRXCUNIH67+XDiFGTJbM8Rjw4Cei0CzjAPjk2jgAR37Kgh6lX2+Xg8AI9wEmzWr08bt8i2FFxVMrcfOCs5zI1y+2T7G9034f5b0gNx/Pc4dDz+1k453vo0AhCC0vrtb1OfbsRu5oOFns0TqoAMY=";
          // 解密后的明文应该为test
          String plainText = CryptoUtils.decryptByRSA(privateKey, cipherText);
          System.out.println("解密后的明文为:" + plainText);
          

          控制台输出如下:

          解密后的明文为:test
          
          备注:
          • 从前端复制过来的密钥需要去掉-----BEGIN PRIVATE KEY-----前缀。
          • 从前端复制过来的密钥带有换行,记得去掉中间的换行符。

            2、后端加密,前端解密

            这里我们用后端生成的密钥对做测试。

            (1) 后端代码
            AsymmetricKeyPair keyPair = CryptoUtils.generateAsymmetricKeyPair(Encryption.RSA_ECB_PKCS1);
            String privateKey = keyPair.getPrivateKey();
            String publicKey = keyPair.getPublicKey();
            System.out.println("生成的私钥为:\n" + privateKey);
            System.out.println("生成的公钥为:\n" + publicKey);
            String cipherText = CryptoUtils.encryptByRSA(publicKey, "test");
            System.out.println("test加密后的内容为:\n" + cipherText);
            

            控制台输出为:

            生成的私钥为:
            MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJPh8WiDo3dhKHW9w86D4DX84pHAG03PIeOHCSezbyKeyKsuHA3qayGJ7JqQgWis557uawz95/EbBzzwmO0oy+l16fkiRXcRppU/UbW6PUBbIpJNqCjnKcw+DA5UXVDmIv0xXOP34jlnkY1DxnimqMAkgut8gncgdFxO0ap0us6lAgMBAAECgYBzlVpQ/OqMCRVNiYd8ZxicOc6Aaq0skKOFKWsfa6CGZ6KiIMTun3UiXqHeYOm0fcf/MYvcOKvLh/uNRuPQIV3WKAWJ6r+dXQ+LjzmrH4QDMcmkMn0OKxxbe56MASPWka7/08GLiE1FJLDo8DEkBQnlDHqnt4e7BoZSgYVhWv52AQJBAMcr0O7xiB/Ge9aQzqYQJvdQ4JI53pM0lEx5HPzQjbrMjC1flb572js1ajKckkuTX+nxzyTzC3JtfvGCcMqaaoECQQC+E9LoSfZZHpVFCx4ZIh2VgzrGYnelktb6MenILhdji2j9i6ZyAqyg8TjL+W9/kAKnaNAV2j6GF8/bOTX0UGolAkAcUWiFcKXwDqJw4WngRo+jvkYPxFaXC3TCYr3yXByqoIaVtO9vg+CFZpTQ2V4bjLqoYo8XK89G17ai0+8Bf28BAkA+BGvRHKjDJSZg86KrYqUybjHUHraZEFMSKQz1IozBDvB/oXv6QQMgM/RrIQSPI2aqRpl2N9IkoEpSZdVD1KT9AkBtoz4Eg3Nuy1XdCCrTqTMioY0hP74xCcgURpooxmL2xhNUYu6PJr+g4lkaiq8e/2Cr0ZQlps/pEDgaPdHDf3Et
            生成的公钥为:
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCT4fFog6N3YSh1vcPOg+A1/OKRwBtNzyHjhwkns28insirLhwN6mshieyakIForOee7msM/efxGwc88JjtKMvpden5IkV3EaaVP1G1uj1AWyKSTago5ynMPgwOVF1Q5iL9MVzj9+I5Z5GNQ8Z4pqjAJILrfIJ3IHRcTtGqdLrOpQIDAQAB
            test加密后的内容为:
            RgJxG+VSizKgfLnXjsqzTl9h0cUzm460EyHhdL3/qZLNbd6IVcU1Am+OOsbFd9W8GtNhJiCERybgjCucr4c3/EQLXtF8vNHVMFp9ycDW4T+8FMmFQn0f/+oJ7/i9uEoNd9W8nWJcSRHuTw1+rl4Mc7KnmwvdaTV2ZLOxBG6oAK8=
            
            (2) 前端代码
            const privateKey = `MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJPh8WiDo3dhKHW9w86D4DX84pHAG03PIeOHCSezbyKeyKsuHA3qayGJ7JqQgWis557uawz95/EbBzzwmO0oy+l16fkiRXcRppU/UbW6PUBbIpJNqCjnKcw+DA5UXVDmIv0xXOP34jlnkY1DxnimqMAkgut8gncgdFxO0ap0us6lAgMBAAECgYBzlVpQ/OqMCRVNiYd8ZxicOc6Aaq0skKOFKWsfa6CGZ6KiIMTun3UiXqHeYOm0fcf/MYvcOKvLh/uNRuPQIV3WKAWJ6r+dXQ+LjzmrH4QDMcmkMn0OKxxbe56MASPWka7/08GLiE1FJLDo8DEkBQnlDHqnt4e7BoZSgYVhWv52AQJBAMcr0O7xiB/Ge9aQzqYQJvdQ4JI53pM0lEx5HPzQjbrMjC1flb572js1ajKckkuTX+nxzyTzC3JtfvGCcMqaaoECQQC+E9LoSfZZHpVFCx4ZIh2VgzrGYnelktb6MenILhdji2j9i6ZyAqyg8TjL+W9/kAKnaNAV2j6GF8/bOTX0UGolAkAcUWiFcKXwDqJw4WngRo+jvkYPxFaXC3TCYr3yXByqoIaVtO9vg+CFZpTQ2V4bjLqoYo8XK89G17ai0+8Bf28BAkA+BGvRHKjDJSZg86KrYqUybjHUHraZEFMSKQz1IozBDvB/oXv6QQMgM/RrIQSPI2aqRpl2N9IkoEpSZdVD1KT9AkBtoz4Eg3Nuy1XdCCrTqTMioY0hP74xCcgURpooxmL2xhNUYu6PJr+g4lkaiq8e/2Cr0ZQlps/pEDgaPdHDf3Et`;
            const cipherText = `RgJxG+VSizKgfLnXjsqzTl9h0cUzm460EyHhdL3/qZLNbd6IVcU1Am+OOsbFd9W8GtNhJiCERybgjCucr4c3/EQLXtF8vNHVMFp9ycDW4T+8FMmFQn0f/+oJ7/i9uEoNd9W8nWJcSRHuTw1+rl4Mc7KnmwvdaTV2ZLOxBG6oAK8=`;
            const plainText = CryptoUtils.decryptByRSA(privateKey, cipherText);
            console.log(`解密后的内容为:${plainText}`);
            

            控制台输出:

            解密后的内容为:test
            

            3、前端加签,后端验签

            这里我们用前端生成的密钥对做测试。

            (1) 前端代码
            console.log(`生成的私钥为:\n${privateKey}`);
            console.log(`生成的公钥为:\n${publicKey}`);
            const signature = CryptoUtils.signBySHA256WithRSA(privateKey, "test");
            console.log(`生成的签名:\n${signature}`);
            

            控制台输出如下:

            生成的私钥为:
            -----BEGIN PRIVATE KEY-----
            MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALxzJ+U/N/lE+AZO
            zMbzx+5WrWDu31oKlH+jth54APGHY+Cmqvi0dUeuSbv238tkem95GTy1kQRz9CMB
            WvvVdNYC78E8maBaMKb+GZKVIScZ6eo8gkXxEoB7a3/y6a8L/SpQGgaI78JsnVGJ
            ijf+GaGtfbl+dIQMxIc6gbNZOM/PAgMBAAECgYEAiAKE1Mwf1eSVLdhJq33e2oHs
            eH1u7kmci9LYan0qESgqScWAuCdmTenYhbTUKLPIOhQoxsw0cgZOOcWMqR2SSHW/
            CK0Ql/5jP94wyX5Gw62jFXpLTVKQ1piiNjoujTKOsnkdtfh/JnjdeWh7GEcHhGGw
            yin0F1aHZgex4MkG0EECQQDsqJJxESC5QjZl5f6EDIbkSznmeV0IPfgIs8uN1QID
            jFbS97QhClSrsev4LXxuGkltOpWbJUkG+nmjZhDPnV1nAkEAy9nzQndYxiv/XLpd
            +Id2pB+SBRs/panFFEIoEEb4UoY7QPLfQ6ZOQQ9+vC5r1tWQtIhwiEjhYaGhnBZ1
            TASRWQJBAOADNRMnxlT2Uu2jhobSIMFqX7VEvgY2Ollqb0yjC1P2fJ0X8X6w+7LG
            KPnzfGvwH/7vzHteEME1SPydeV48tBMCQQCtCsZElar2DkMnI8zBO7yqdWIuk4Lj
            zclN+RqpNpV0+B00dPaxJmsnL1AVzhIcvA2qMmfUSImJpvrY1PedIAOBAkAvjVU0
            3USMjvv5iudWyqqRy8Q9s2qeWYEiv+bFeLExiqGj9kmOkIfPcm96ElZr1F7PlaQb
            wj2A+D2oaQGcQqUm
            -----END PRIVATE KEY-----
            生成的公钥为:
            -----BEGIN PUBLIC KEY-----
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8cyflPzf5RPgGTszG88fuVq1g
            7t9aCpR/o7YeeADxh2Pgpqr4tHVHrkm79t/LZHpveRk8tZEEc/QjAVr71XTWAu/B
            PJmgWjCm/hmSlSEnGenqPIJF8RKAe2t/8umvC/0qUBoGiO/CbJ1RiYo3/hmhrX25
            fnSEDMSHOoGzWTjPzwIDAQAB
            -----END PUBLIC KEY-----
            生成的签名:
            Q9Mtq3gxi2YJ07FQtbry5zxGljomzKQNewhj10Ba10b3roAAdQUzqd+QyP7rqARdPQgt0ClDgvtaL2TNYLc4URh7E3Kgx8T6pSFlPnU/b3cfCoVRPrr/gJBrsCkbNMITNXpVQpwIYe3P1z+OrCUHuaQR82yCVUz3y43oOiE6qIY=
            
            (2) 后端代码
            String signature = "Q9Mtq3gxi2YJ07FQtbry5zxGljomzKQNewhj10Ba10b3roAAdQUzqd+QyP7rqARdPQgt0ClDgvtaL2TNYLc4URh7E3Kgx8T6pSFlPnU/b3cfCoVRPrr/gJBrsCkbNMITNXpVQpwIYe3P1z+OrCUHuaQR82yCVUz3y43oOiE6qIY=";
            boolean isVerified = CryptoUtils.verifyBySHA256WithRSA(publicKey, "test", signature);
            System.out.println("是否验签通过:" + isVerified);
            

            控制台输出:

            是否验签通过:true
            

            4、后端加签,前端验签

            这里我们用后端生成的密钥对做测试。

            (1) 后端代码
            AsymmetricKeyPair keyPair = CryptoUtils.generateAsymmetricKeyPair(Encryption.RSA_ECB_PKCS1);
            String privateKey = keyPair.getPrivateKey();
            String publicKey = keyPair.getPublicKey();
            System.out.println("生成的私钥为:\n" + privateKey);
            System.out.println("生成的公钥为:\n" + publicKey);
            String signature = CryptoUtils.signBySHA256WithRSA(privateKey, "test");
            System.out.println("生成的签名为:\n" + signature);
            

            控制台输出如下:

            生成的私钥为:
            MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKx1iT0ObyRKC06wf+WN1v9LTQwd68X1b8U5+ZAh9Qf7m77HiQtLg1y91v0b70Dr/HRP7juGLFTnK5+NJMcjGNqDfDFqCQtA3eam2UABbwHS76qRFQbSg5QKApvDcfOZtqWmbwwSDMkI5GnYKbSO3EZZCYBBXzplabKQCKcmGKOjAgMBAAECgYAovOb7RkKYxuje4LCFkDjeO3Jqz1KXg3+wjh5Wnr7b8OJ8cXP8+AyCxtFXHtcoddY/v3XeF7a3I5hZayTp6W+AI1OTYhWs9Eqas8B7bNV2rJPFnK9nTiF727bgptJfGuUG8mYxRzIQleHoWqpV9i/ttcEUPM4GGcIfpnwb16NBAQJBANPBLuTCyeDbSW79MmsiTNUeCnljM/UQYUfIpygviNX1iVbsh1lI/l85bN47niIt66j4c5MPOKJOv2Hf3yYqvIECQQDQfmzfLo7deqsizkJAFKggH99ab24iC+VEDtsHlsl212NC36xenoWwuIcP8fJd1UyWY5lwzzCdBKsrt0UeSd4jAkBwrv3AWHPLh4YFXRHGdyNBydGzFPpiL8xEwd9KADml+hqSuh2wgqpyjAGGJV2aPKuKaGRAXro5jQRFFjgOfHGBAkEAsq22ViqLa0nmgmSrqElLsIRAITvf8bOqHwJwOXfDXmLGgZg5G7nVLxdlQIgEQuA6y6O960zVB6vpmgRtasC5awJBAJkwLiKikvPxC8vwhZvkjr+UrbDorUKcuCyDVYxXsSNW8SNs+AV54wEI1Mem5LOhNPKbum6bwwfTf74gC/l4jtw=
            生成的公钥为:
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsdYk9Dm8kSgtOsH/ljdb/S00MHevF9W/FOfmQIfUH+5u+x4kLS4Ncvdb9G+9A6/x0T+47hixU5yufjSTHIxjag3wxagkLQN3mptlAAW8B0u+qkRUG0oOUCgKbw3Hzmbalpm8MEgzJCORp2Cm0jtxGWQmAQV86ZWmykAinJhijowIDAQAB
            生成的签名为:
            JQ2FWaAbHWIkl4uSIxyMNbARFzSNKc7mOtXidm7hCRN85D8DVgZll02DYcWRSnn/ejOOxOrEPF8AcYHWx1repHh/jHcwv2focjF3Yne7NkQ4yGvgILDD2s1BIEfU0EH3tFLMIebyU8V54eMMtjDLQ65LZB6PH+5X8s3F6yAPI70=
            
            (2) 前端代码
            const publicKey = `
                  -----BEGIN PUBLIC KEY-----
                  MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsdYk9Dm8kSgtOsH/ljdb/S00MHevF9W/FOfmQIfUH+5u+x4kLS4Ncvdb9G+9A6/x0T+47hixU5yufjSTHIxjag3wxagkLQN3mptlAAW8B0u+qkRUG0oOUCgKbw3Hzmbalpm8MEgzJCORp2Cm0jtxGWQmAQV86ZWmykAinJhijowIDAQAB
                  -----END PUBLIC KEY-----`;
            const signature = `JQ2FWaAbHWIkl4uSIxyMNbARFzSNKc7mOtXidm7hCRN85D8DVgZll02DYcWRSnn/ejOOxOrEPF8AcYHWx1repHh/jHcwv2focjF3Yne7NkQ4yGvgILDD2s1BIEfU0EH3tFLMIebyU8V54eMMtjDLQ65LZB6PH+5X8s3F6yAPI70=`;
            const message = `test`;
            const isVerified = CryptoUtils.verifyBySHA256WithRSA(publicKey, signature, message);
            console.log(`是否验签通过:${isVerified}`);
            

            控制台输出如下:

            是否验签通过:true
            ————————————————
            
            备注:因为我们在前端解析密钥时读取的是标准pem格式密钥,所以从后端复制过来的公钥一定要加上

            -----BEGIN PUBLIC KEY-----前缀和-----END PUBLIC KEY-----后缀,否则会报错。