前言
在本文中,我们将探讨用于 SM2国密算法的实用工具:Sm2Util,这个工具类可以生成 SM2 加密密钥对(公钥、私钥)、使用 SM2 算法进行加密和解密。
一、SM2 国密算法简介
SM2 国密SM2算法是中国国家密码管理局(CNCA)发布的一种非对称加密算法。它采用椭圆曲线密码体系(Elliptic Curve Cryptography,ECC)进行密钥交换、数字签名和公钥加密等操作。SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。
二、工具类的使用
1.引入Maven依赖
代码如下(示例):
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.77</version>
</dependency>
2.Sm2Util工具类
代码如下(示例):
package com.tg.dp.user.auth.server.util;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
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.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.util.Assert;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* SM2国密加密解密
* @author Administrator
*/
@Slf4j
public class Sm2Util {
public static final String PUBLIC_KEY = "0413b03efd15f560fc6d73965febe39ccc47596088c3653b8efe91e008194c0c50ca8ffeddcddbf48bd05506510af59075edeb651de626765527b217e8a3b6ce10";
public static final String PRIVATE_KEY = "026b435ef1124d82e7ed5521dcf9572ba81c56afa57d8e978a95f9239dfbd31c";
public static final String PREFIX = "04";
/**
* SM2加密算法
*
* @param publicKey 公钥
* @param data 明文数据
* @return
*/
public static String encrypt(String publicKey, String data) {
String result = "";
if (StrUtil.isEmpty(data)) {
return data;
}
Assert.hasText(publicKey, "publicKey不能为空!");
try {
// 获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 设置sm2为加密模式
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] in = data.getBytes();
byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
result = Hex.toHexString(arrayOfBytes);
} catch (Exception e) {
log.error("SM2加密时出现异常:{}", e.getMessage(), e);
}
return result;
}
/**
* SM2解密算法
*
* @param privateKey 私钥
* @param cipherData 密文数据
* @return
*/
public static String decrypt(String privateKey, String cipherData) {
String result = "";
if (StrUtil.isEmpty(cipherData)) {
return cipherData;
}
Assert.hasText(privateKey, "privateKey不能为空!");
// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
try {
if (!cipherData.startsWith(PREFIX)) {
cipherData = PREFIX + 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);
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
return new String(arrayOfBytes);
} catch (Exception e) {
log.error("SM2解密时出现异常:{}", e.getMessage(), e);
}
return result;
}
public static void main(String[] args) {
// //获取公钥、私钥
// Map<String, String> map = generateKey();
// System.out.println("公钥:" + map.get("publicKey"));
// System.out.println("私钥:" + map.get("privateKey"));
String encrypt = Sm2Util.encrypt(PUBLIC_KEY, "123456");
System.out.println("加密结果:");
System.out.println(encrypt);
String decrypt = Sm2Util.decrypt(PRIVATE_KEY, encrypt);
System.out.println("解密结果:");
System.out.println(decrypt);
}
/**
* 获取密钥对
*/
public static Map<String, String> generateKey() {
try {
Map<String, String> map = new LinkedHashMap<>();
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;
String publicKeyStr = Hex.toHexString(p.getQ().getEncoded(false));
log.info("generate-publicKey:{}", publicKeyStr);
//私钥
PrivateKey privateKey = keyPair.getPrivate();
BCECPrivateKey s = (BCECPrivateKey) privateKey;
String privateKeyStr = Hex.toHexString(s.getD().toByteArray());
log.info("generate-privateKey:{}", privateKeyStr);
map.put("publicKey", publicKeyStr);
map.put("privateKey", privateKeyStr);
return map;
} catch (Exception e) {
e.getLocalizedMessage();
log.error("SM2Util生成密钥异常!{}", e.getLocalizedMessage(), e);
}
return null;
}
}
其中,encrypt()方法加密,decrypt()方法解密,generateKey()方法获取加解密密码对。
运行generateKey()方法获取SM2公私钥密码对:
运行main()方法调用加密、解密算法进行加解密:
总结
通过使用 Sm2Util工具类,可以轻松生成 SM2 密钥对,并且通过 Sm2Util,可以使用 SM2 密钥进行数据加密和解密,这些实用工具简化了使用 SM2 算法的过程,简单明了。