由于等保和多个系统间的数据传输加密, 写了一个共有的前端与后端, 后端与后端,的通用算法SM2简单加密, 不需要验签, 几行代码搞定.

引包, 测试好几遍, 这个包适合jdk1.8使用

1、后端代码示例

引包,

org.bouncycastlebcprov-jdk18on1.72

没有意外就应该直接能用下面代码了

import org.bouncycastle.asn1.gm.GMNamedCurves;import org.bouncycastle.asn1.x9.X9ECParameters;import org.bouncycastle.crypto.engines.SM2Engine;import org.bouncycastle.crypto.params.*;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.math.ec.ECPoint;import org.bouncycastle.util.encoders.Hex;import org.junit.Test;import java.math.BigInteger;import java.security.*;import java.security.spec.ECGenParameterSpec;/** * 简单的sm2 */public class SimpSM2Util {/** * SM2加密算法 * @param publicKey 公钥 * @param data明文数据 * @return */public static String encrypt(String publicKey, 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(publicKey));// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);// 设置sm2为加密模式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) {System.out.println("SM2加密时出现异常:"+e.getMessage());}return Hex.toHexString(arrayOfBytes);}/** * SM2解密算法 * @param privateKey私钥 * @param cipherData密文数据 * @return */public static String decrypt(String privateKey, String cipherData) {// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上if (!cipherData.startsWith("04")){cipherData = "04" + cipherData;}byte[] cipherDataByte = Hex.decode(cipherData);BigInteger privateKeyD = new BigInteger(privateKey, 16);//获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);// 设置sm2为解密模式sm2Engine.init(false, privateKeyParameters);String result = "";try {byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);return new String(arrayOfBytes);} catch (Exception e) {System.out.println("SM2解密时出现异常:"+e.getMessage());}return result;}/*@Test// 生成密钥public void createKey() throws Exception{//String M="encryption standard111111111111111111111111111111";SimpSM2Util sm2 = new SimpSM2Util();ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");// 获取一个椭圆曲线类型的密钥对生成器KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());// 使用SM2参数初始化生成器kpg.initialize(sm2Spec);// 获取密钥对KeyPair keyPair = kpg.generateKeyPair();PublicKey publicKey = keyPair.getPublic();BCECPublicKey p=(BCECPublicKey)publicKey;System.out.println("publicKey:"+Hex.toHexString(p.getQ().getEncoded(false)));PrivateKey privateKey = keyPair.getPrivate();BCECPrivateKey s=(BCECPrivateKey)privateKey;System.out.println("privateKey:"+Hex.toHexString(s.getD().toByteArray()));}*/}

publicKey:04aa909915f87880507e3de515220cc8f82b1c5693f56a0475b3b48ff7448c229734cd724e603000dd78569faa9fbc1eeb93a6d836190a0ee734e2d9e74d804f28
privateKey:00a684d832831d5371d2ff9de6a021a33fb396451e74e0ffe0d415c298b36876fe

加密: SimpSM2Util.encrypt(“共有密钥”, “明文”);

解密: SimpSM2Util.decrypt(“私有密钥”, “密文”);

后端代码使用一个工具类, 可参考

import org.bouncycastle.crypto.engines.SM2Engine;/** * 简单单例SM2加解密, 配合前端 */public class LoSM2 {private static String ECNameModel = "sm2p256v1";private static SM2Engine.Mode CipherMode = SM2Engine.Mode.C1C3C2;private final String privateKey;private final String publicKey;private LoSM2(String privateKey, String publicKey){this.privateKey = privateKey;this.publicKey = publicKey;}private volatile static LoSM2 instance = null;public static LoSM2 getInstance(){if(instance == null){throw new RuntimeException("请InitKey初始化密钥!!!");}return instance;}/** * SM2初始密钥(私钥,公钥) * @param privateKey * @param publicKey * @return */public static LoSM2 InitKey(String privateKey, String publicKey) {if(instance == null){synchronized(LoSM2.class){if(instance == null){instance = new LoSM2(privateKey, publicKey);}}}return instance;}private static class SM2SimpSelfLoader {private static final LoSM2 instance = InitKey("", "");}private static boolean IsInitKey(){if(instance == null) {throw new RuntimeException("请InitKey初始化密钥!!!");}else {return true;}}/** * SM2加密 * @param cleartext明文数据 */public String encrypt(String cleartext) {if(!IsInitKey())return "";return SimpSM2Util.encrypt(instance.publicKey, cleartext);}/** * SM2解密 * @param cipherData密文数据 */public static String decrypt(String cipherData) {if(!IsInitKey())return "";return SimpSM2Util.decrypt(instance.privateKey, cipherData);}}

2、前端代码示例

安装sm-crypto包: npm install –savesm-crypto

前端加解密更简单, 引包后就能用

let txt='简单加密不要验签'//要加密的字段const sm2 = require('sm-crypto').sm2const cipherMode = 1;// 1 - C1C3C2,0 - C1C2C3,默认为1var publicKey ="04aa909915f87880507e3de515220cc8f82b1c5693f56a0475b3b48ff7448c229734cd724e603000dd78569faa9fbc1eeb93a6d836190a0ee734e2d9e74d804f28";// 加密结果let encryptData = sm2.doEncrypt(txt, publicKey, cipherMode);// 解密sm2.doDecrypt(txt, privateKey, cipherMode);

适合简单的传输加密

3、避坑指南

在引用bcprov-jdk18on 包的时候, 有可能存在版本冲突, 或本地安全问题, 这个是我在返回切换工程遇到的问题

可以尝试把bcprov-jdk18on-1.72.jar 加入mvn的import的sdks里面

还可以试试mvn idea:module

或者试试 mvn: idea:idea