加密工具类1
1、实现前端加密,后端解密,前端需要引入js文件
<script src="/business/libs/crypto-js.js"></script>// js调用方式var encryptData = sm2Encrypt('明文', '公钥', 0);
2、后端SM2生成公私钥及解密
package wst.st.site.tools;import java.math.BigInteger;import java.nio.charset.StandardCharsets;import java.security.SecureRandom;import org.apache.commons.codec.binary.Base64;import org.bouncycastle.asn1.gm.GMNamedCurves;import org.bouncycastle.asn1.x9.X9ECParameters;import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.engines.SM2Engine;import org.bouncycastle.crypto.generators.ECKeyPairGenerator;import org.bouncycastle.crypto.params.ECDomainParameters;import org.bouncycastle.crypto.params.ECKeyGenerationParameters;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.crypto.params.ParametersWithRandom;import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.bouncycastle.jce.spec.ECParameterSpec;import org.bouncycastle.jce.spec.ECPrivateKeySpec;import org.bouncycastle.math.ec.ECPoint;import org.bouncycastle.util.encoders.Hex;import wst.st.site.constant.SiteWebConstant;/*** SM2工具类** @author wst 2023年8月21日 上午10:32:33**/public class SM2Util {public static void main(String[] args) throws Exception {}/*** 生成密钥** @author wst 2023年8月21日 上午10:37:37*/public static void getPKs() {try {X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),sm2ECParameters.getG(), sm2ECParameters.getN());ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();// 私钥,16进制格式,自己保存BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();String privateKeyHex = privatekey.toString(16);System.out.println("private Key :" + privateKeyHex);// 公钥,16进制格式,发给前端ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));System.out.println("public Key :" + publicKeyHex);} catch (Exception e) {e.printStackTrace();}}/*** 解密,前端登录信息解码及解码** @author wst 2024年9月12日 下午1:41:47* @param cipherData* @return*/public static String decrypt(String cipherData) {// 得到明文String data = null;try {byte[] cipherDataByte = Hex.decode(cipherData);// sm2私钥BigInteger privateKeyD = new BigInteger(SiteWebConstant.Common.Config.Sm2_Private_Key, 16);X9ECParameters sm2ECParameters1 = GMNamedCurves.getByName("sm2p256v1");ECDomainParameters domainParameters1 = new ECDomainParameters(sm2ECParameters1.getCurve(),sm2ECParameters1.getG(), sm2ECParameters1.getN());ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters1);// 用私钥解密SM2Engine sm2Engine = new SM2Engine();sm2Engine.init(false, privateKeyParameters);// 前端传入的密文解密后的processBlock得到Base64格式,记得解码// 如果只用下面给出的加密方法加密则不需要这个解码流程byte[] arrayOfBytes = Base64.decodeBase64(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));data = new String(arrayOfBytes);} catch (Exception e) {e.printStackTrace();}return data;}/*** 解密* @author wst 2024年9月12日 下午2:44:39* @param cipherData* @return*/public static String sm2decrypt(String cipherData) {String result = null;try {// 加密算法String algorithm = "EC";// 私钥Hex,还原私钥BigInteger privateKeyD = new BigInteger(SiteWebConstant.Common.Config.Sm2_Private_Key, 16);// 使用标准名称创建EC参数生成的参数规范X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");// 获取私钥的基本域参数ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(),sm2ECParameters.getN());ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);// 获取私钥对象BCECPrivateKey bcecPrivateKey = new BCECPrivateKey(algorithm, ecPrivateKeySpec,BouncyCastleProvider.CONFIGURATION);byte[] cipherDataByte = Hex.decode(cipherData);ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),ecParameterSpec.getG(), ecParameterSpec.getN());// 通过私钥值和私钥基本参数创建私钥参数对象ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(),ecDomainParameters);// 通过解密模式创建解密引擎并初始化SM2Engine sm2Engine = new SM2Engine();sm2Engine.init(false, ecPrivateKeyParameters);try {byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);return new String(arrayOfBytes, StandardCharsets.UTF_8);} catch (Exception e) {}} catch (Exception e) {}return result;}/*** 加密* @author wst 2024年9月12日 下午2:42:21* @param data* @return*/public static String encrypt(String data){// 获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数NECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());//提取公钥点ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(SiteWebConstant.Common.Config.Sm2_Public_Key));// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);SM2Engine sm2Engine = new SM2Engine();sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));byte[] arrayOfBytes = null;try {byte[] in = data.getBytes();arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);} catch (Exception e) {e.printStackTrace();}return Hex.toHexString(arrayOfBytes);}}
加密工具类2
import java.math.BigInteger;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.PrivateKey;import java.security.PublicKey;import java.security.SecureRandom;import java.security.spec.ECGenParameterSpec;import org.apache.commons.net.util.Base64;import org.bouncycastle.asn1.gm.GMNamedCurves;import org.bouncycastle.asn1.x9.X9ECParameters;import org.bouncycastle.crypto.engines.SM2Engine;import org.bouncycastle.crypto.params.ECDomainParameters;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.crypto.params.ParametersWithRandom;import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.bouncycastle.jce.spec.ECParameterSpec;import org.bouncycastle.jce.spec.ECPrivateKeySpec;import org.bouncycastle.jce.spec.ECPublicKeySpec;import org.bouncycastle.math.ec.ECPoint;import org.bouncycastle.util.encoders.Hex;/**** @author wst 2026年8月5日 下午3:44:15**/public class SM2Utils {/*// 前端调用sm2加密函数// 需要导入 crypto-js.js, sm2.jsfunction sm2EncryptPwd(data) {<!--sm2公钥-->var publicKey = '你生成的公钥';return sm2Encrypt(data, publicKey, 1);}*/public static final String PUBLIC_KEY = "0469b67671fca561ca5383adf320f42700a686034d6bc3ecafca4e27b0560870549288ccb36b6ca7326d9d0497f49a6ac4ee04fd302f0f29f6e942cc7edb6a8152";private static final String PRIVATE_KEY = "5f2382a0722be0df1983593d20fb32307f3524e58d4f58bddf8f6dd77cfe565d";private static final SM2Engine.Mode MODE = SM2Engine.Mode.C1C3C2;//椭圆曲线ECParameters ASN.1 结构private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");//椭圆曲线公钥或私钥的基本域参数。private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());/*** 生成秘钥对* @author wst(shi-tao.wen) 2026年7月16日 16:51:10* @return*/public static KeyPair createECKeyPair() {//使用标准名称创建EC参数生成的参数规范final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");// 获取一个椭圆曲线类型的密钥对生成器final KeyPairGenerator kpg;try {kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());// 使用SM2算法域参数集初始化密钥生成器(默认使用以最高优先级安装的提供者的 SecureRandom 的实现作为随机源)// kpg.initialize(sm2Spec);// 使用SM2的算法域参数集和指定的随机源初始化密钥生成器kpg.initialize(sm2Spec, new SecureRandom());// 通过密钥生成器生成密钥对return kpg.generateKeyPair();} catch (Exception e) {e.printStackTrace();return null;}}/*** 解密,并且获取解密后的字符串* @author wst(shi-tao.wen) 2026年7月16日 16:50:56* @param cipherData* @return*/public static String sm2decrypt(String cipherData) {byte[] cipherDataByte = Hex.decode(cipherData);BigInteger privateKeyD = new BigInteger(PRIVATE_KEY, 16);X9ECParameters sm2ECParameters1 = GMNamedCurves.getByName("sm2p256v1");ECDomainParameters domainParameters1 = new ECDomainParameters(sm2ECParameters1.getCurve(), sm2ECParameters1.getG(), sm2ECParameters1.getN());ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters1);//用私钥解密,SM2Engine.Mode.C1C3C2得加,不然就会报错invalid cipher textSM2Engine sm2Engine = new SM2Engine(MODE);sm2Engine.init(false, privateKeyParameters);//processBlock得到Base64格式,记得解码byte[] arrayOfBytes = null;try {arrayOfBytes = Base64.decodeBase64(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));} catch (Exception e) {e.printStackTrace();return null;}//得到明文:SM2 Encryption Testreturn new String(arrayOfBytes);}/*** 加密* @author wst 2024年9月12日 下午2:42:21* @param data* @return*/public static String sm2encrypt(String data){// 获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数NECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());//提取公钥点ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(PUBLIC_KEY));// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);SM2Engine sm2Engine = new SM2Engine(MODE);sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));byte[] arrayOfBytes = null;try {byte[] in = Base64.encodeBase64(data.getBytes());arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);} catch (Exception e) {e.printStackTrace();}return Hex.toHexString(arrayOfBytes);}/*** 公钥加密* @author wst(shi-tao.wen) 2026年7月16日 16:51:30* @param publicKeyHex 公钥* @param data 明文* @return*/public static String encrypt(String publicKeyHex, String data) {return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);}/*** 公钥加密* @author wst(shi-tao.wen) 2026年7月16日 16:51:46* @param data 明文* @return*/public static String encrypt(String data) {return encrypt(getECPublicKeyByPublicKeyHex(PUBLIC_KEY), data, 1);}/*** 公钥加密* @author wst(shi-tao.wen) 2026年7月16日 16:52:03* @param publicKey 公钥* @param data 明文* @param modeType 加密模式* @return*/public static String encrypt(BCECPublicKey publicKey, String data, int modeType) {//加密模式SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;if (modeType != 1) {mode = SM2Engine.Mode.C1C2C3;}//通过公钥对象获取公钥的基本域参数。ECParameterSpec ecParameterSpec = publicKey.getParameters();ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),ecParameterSpec.getG(), ecParameterSpec.getN());//通过公钥值和公钥基本参数创建公钥参数对象ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);//根据加密模式实例化SM2公钥加密引擎SM2Engine sm2Engine = new SM2Engine(mode);//初始化加密引擎sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));byte[] arrayOfBytes = null;try {//将明文字符串转换为指定编码的字节串byte[] in = data.getBytes("utf-8");//通过加密引擎对字节数串行加密arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);} catch (Exception e) {System.out.println("SM2加密时出现异常:" + e.getMessage());e.printStackTrace();}//将加密后的字节串转换为十六进制字符串return Hex.toHexString(arrayOfBytes);}/*** 私钥解密* @author wst(shi-tao.wen) 2026年7月16日 16:52:23* @param privateKeyHex 私钥* @param cipherData 密文* @return*/public static String decrypt(String privateKeyHex, String cipherData) {return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);}/*** 私钥解密* @author wst(shi-tao.wen) 2026年7月16日 16:52:52* @param cipherData 密文* @return*/public static String decrypt(String cipherData) {return decrypt(getBCECPrivateKeyByPrivateKeyHex(PRIVATE_KEY), cipherData, 1);}/*** 私钥解密* @author wst(shi-tao.wen) 2026年7月16日 16:53:15* @param privateKey 私钥* @param cipherData 密文* @param modeType 解密模式* @return*/public static String decrypt(BCECPrivateKey privateKey, String cipherData, int modeType) {//解密模式SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;if (modeType != 1) {mode = SM2Engine.Mode.C1C2C3;}//将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)byte[] cipherDataByte = Hex.decode(cipherData);//通过私钥对象获取私钥的基本域参数。ECParameterSpec ecParameterSpec = privateKey.getParameters();ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),ecParameterSpec.getG(), ecParameterSpec.getN());//通过私钥值和私钥钥基本参数创建私钥参数对象ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),ecDomainParameters);//通过解密模式创建解密引擎并初始化SM2Engine sm2Engine = new SM2Engine(mode);sm2Engine.init(false, ecPrivateKeyParameters);String result = null;try {//通过解密引擎对密文字节串进行解密byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);//将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)result = new String(arrayOfBytes, "utf-8");} catch (Exception e) {System.out.println("SM2解密时出现异常" + e.getMessage());}return result;}/*** 公钥字符串转换为 BCECPublicKey 公钥对象* @author wst(shi-tao.wen) 2026年7月16日 16:53:53* @param pubKeyHex 64字节十六进制公钥字符串(如果公钥字符串为65字节首个字节为0x04:表示该公钥为非压缩格式,操作时需要删除)* @return SM2公钥对象*/public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {//截取64字节有效的SM2公钥(如果公钥首个字节为0x04)if (pubKeyHex.length() > 128) {pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);}//将公钥拆分为x,y分量(各32字节)String stringX = pubKeyHex.substring(0, 64);String stringY = pubKeyHex.substring(stringX.length());//将公钥x、y分量转换为BigInteger类型BigInteger x = new BigInteger(stringX, 16);BigInteger y = new BigInteger(stringY, 16);//通过公钥x、y分量创建椭圆曲线公钥规范ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecDomainParameters);//通过椭圆曲线公钥规范,创建出椭圆曲线公钥对象(可用于SM2加密及验签)return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);}/*** 私钥字符串转换为 BCECPrivateKey 私钥对象* @author wst(shi-tao.wen) 2026年7月16日 16:54:17* @param privateKeyHex 32字节十六进制私钥字符串* @return SM2私钥对象*/public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {//将十六进制私钥字符串转换为BigInteger对象BigInteger d = new BigInteger(privateKeyHex, 16);//通过私钥和私钥域参数集创建椭圆曲线私钥规范ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters);//通过椭圆曲线私钥规范,创建出椭圆曲线私钥对象(可用于SM2解密和签名)return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);}public static void main(String[] args) {String publicKeyHex = null;String privateKeyHex = null;KeyPair keyPair = createECKeyPair();PublicKey publicKey = keyPair.getPublic();if (publicKey instanceof BCECPublicKey) {//获取65字节非压缩缩的十六进制公钥串(0x04)publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));System.out.println("---->SM2公钥:" + publicKeyHex);}PrivateKey privateKey = keyPair.getPrivate();if (privateKey instanceof BCECPrivateKey) {//获取32字节十六进制私钥串privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);System.out.println("---->SM2私钥:" + privateKeyHex);}/*** 公钥加密*/String data = "=========待加密数据=========";//将十六进制公钥串转换为 BCECPublicKey 公钥对象String encryptData = encrypt(publicKeyHex, data);System.out.println("---->加密结果:" + encryptData);/*** 私钥解密*///将十六进制私钥串转换为 BCECPrivateKey 私钥对象data = decrypt(privateKeyHex, encryptData);System.out.println("---->解密结果:" + data);String mdata = "04d1a73aa9b169ceaaaefb242f444510958cf5d53f464588f05008cb2f9d3ccc9a9611518e90a425697003eff19bf00937a575851c09b1525aec472645442987135f48a0bf8b91300062b37dc274bc33555a06260c1def986bfb7f082b73921ca2572fe384f944bf3be0d76dcbaa25c40c879e7612d044511d";System.out.println(sm2decrypt(mdata));System.out.println(sm2decrypt(sm2encrypt("20260717_@_admin")));}}