目录
一、序言
二、关于PKCS#1和PKCS#8格式密钥
1、简介
2、区别
二、关于JSEncrypt
三、关于jsrsasign
四、前端RSA加解密、加验签示例
1、相关依赖
2、cryptoUtils工具类封装
3、测试用例
五、Java后端RSA加解密、加验签
1、CryptoUtils工具类封装
2、测试用例
六、前后端加解密、加验签交互测试
1、前端加密,后端解密
(1) 前端代码
(2) 后端代码
2、后端加密,前端解密
(1) 后端代码
(2) 前端代码
3、前端加签,后端验签
(1) 前端代码
(2) 后端代码
4、后端加签,前端验签
(1) 后端代码
(2) 前端代码
一、序言
最近有一些安全性要求比较高的场景,我们提供API给第三方商户用于收单,其中有几个功能是绑卡、ATM/POS密码变更。
出于合规和监管要求,第三方商户不能保存卡号、CVV、密码等敏感信息,所以相关的敏感操作必须在接口提供方H5页面中完成。为了最大程度保证安全性,我们决定对敏感信息进行加密传输,同时对请求参数进行加签处理。
由于前端并不需要解密操作,最终我们选择RSA非对称加密,前端这块主要采用jsencrypt进行加解密,jsrsasign用来生成密钥对、加签验签。
二、关于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-----
二、关于JSEncrypt
JSEncrypt是前端RSA加解密、密钥生成的一个实现方案,API也比较简单,更多请参考:JSEncrypt官网。
官网有个线上的Demo,可以生成非对称密钥,还有加解密。
Home页有相关的使用示例,还是很简单的。
// Create the encryption object and set the key. var crypt = new JSEncrypt(); crypt.setKey(__YOUR_OPENSSL_PRIVATE_OR_PUBLIC_KEY__); //You can use also setPrivateKey and setPublicKey, they are both alias to setKey //Eventhough the methods are called setPublicKey and setPrivateKey, remember //that they are only alias to setKey, so you can pass them both a private or //a public openssl key, just remember that setting a public key allows you to only encrypt. var text = 'test'; // Encrypt the data with the public key. var enc = crypt.encrypt(text); // Now decrypt the crypted text with the private key. var dec = crypt.decrypt(enc); // Now a simple check to see if the round-trip worked. if (dec === text){ alert('It works!!!'); } else { alert('Something went wrong....'); }
备注:Download页有JSEncrypt
官网demo的源代码。
三、关于jsrsasign
jsrsasign是一个开源的纯JavaScript加密库,支持RSA/RSAPSS/ECDSA/DSA签名和验签,PKCS#1/5/8公私钥生成等等。
在Github上有两篇文章,一篇是关于如何使用Signature类进行加签、验签的,一篇是关于jsrsasign和Java互用性的,里面有通过Java加签,通过jsrsasign验签的demo。
更多相关类和方法的使用和描述可以参考,jsrsasign API文档说明。
四、前端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。 * 更多模式参考:<a href="https://kjur.github.io/jsrsasign/api/symbols/KEYUTIL.html">https://kjur.github.io/jsrsasign/api/symbols/KEYUTIL.html</a> * @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<Algorithm, KeyFactory> KEY_FACTORY_CACHE = new ConcurrentHashMap<>(); private static final Map<Algorithm, Cipher> 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; } /** * 算法分为加密算法和签名算法,更多算法实现见:<br/> * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">jdk8中的标准算法</a> */ 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) 前端代码
const {privateKey, publicKey} = CryptoUtils.generateRsaKeyWithPKCS8(); 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 publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8cyflPzf5RPgGTszG88fuVq1g7t9aCpR/o7YeeADxh2Pgpqr4tHVHrkm79t/LZHpveRk8tZEEc/QjAVr71XTWAu/BPJmgWjCm/hmSlSEnGenqPIJF8RKAe2t/8umvC/0qUBoGiO/CbJ1RiYo3/hmhrX25fnSEDMSHOoGzWTjPzwIDAQAB"; 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-----
后缀,否则会报错。