org.bouncycastle
bcprov-jdk15to18
1.68
cn.hutool
hutool-all
5.7.2
首先创建一个用于存储加密解密内容的实体:
package com.bw.note.util.SM2.SP;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiEncryptInfoDTO implements Serializable {
private static final long serialVersionUID = 255205006827117733L;
/**
* 加密类型(2:sm2加密,4:sm4加密)
*/
private String type;
/**
* 非对称加密私钥
*/
private String privateKey;
/**
* 非对称加密公钥
*/
private String publicKey;
/**
* 对称加密密钥
*/
private String key;
/**
* 原始数据
*/
private String data;
/**
* 加密后数据
*/
private String dataHex;
/**
* 非对称加密签名
*/
private String sign;
}
接着是具体的加密解密过程工具类:
package com.bw.note.util.SM2.SP;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
@Slf4j
public class SM2Utils {
/**
* SM2加密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
public static ApiEncryptInfoDTO encrypt2Data(ApiEncryptInfoDTO dto){
String publicKey = dto.getPublicKey();
// 若为空,使用默认
if (StringUtils.isBlank(publicKey)) {
publicKey = "04db9629dd33ba568e9507add5df6587a0998361a03d3321948b448c653c2c1b7056434884ab6f3d1c529501f166a336e86f045cea10dffe58aa82ea13d7253763";
}
String data = dto.getData();
//创建sm2 对象
SM2 sm2 = getSM2(null, publicKey);
String dataHex = sm2.encryptBcd(data, KeyType.PublicKey);
dto.setDataHex(dataHex);
return dto;
}
/**
* SM2解密
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
public static ApiEncryptInfoDTO decrypt2Data(ApiEncryptInfoDTO dto){
String privateKey = dto.getPrivateKey();
// 若为空,使用默认
if (StringUtils.isBlank(privateKey)) {
privateKey = "1ebf8b341c695ee456fd1a41b82645724bc25d79935437d30e7e4b0a554baa5e";
}
String dataHex = dto.getDataHex();
try {
//创建sm2 对象
SM2 sm2 = getSM2(privateKey, null);
String data = StrUtil.utf8Str(sm2.decryptFromBcd(dataHex, KeyType.PrivateKey));
dto.setData(data);
} catch (Exception e) {
log.error("SM2解密失败", e);
}
return dto;
}
/**
* SM4加密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
public static ApiEncryptInfoDTO encrypt4Data(ApiEncryptInfoDTO dto) {
//指定的密钥
String key = dto.getKey();
// 若为空,使用默认
if (StringUtils.isBlank(key)) {
key = "zps9yv341b3s90c2";
}
String data = dto.getData();
try {
SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes(CharsetUtil.CHARSET_UTF_8));
String dataHex = sm4.encryptHex(data);
dto.setDataHex(dataHex);
} catch (Exception e) {
log.error("加密数据异常,异常数据:" + data, e);
}
return dto;
}
/**
* SM4解密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
public static ApiEncryptInfoDTO decrypt4Data(ApiEncryptInfoDTO dto) {
//指定的密钥
String key = dto.getKey();
// 若为空,使用默认
if (StringUtils.isBlank(key)) {
key = "zps9yv341b3s90c2";
}
String dataHex = dto.getDataHex();
try {
SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes(CharsetUtil.CHARSET_UTF_8));
String data = sm4.decryptStr(dataHex);
dto.setData(data);
} catch (Exception e) {
log.error("解密数据异常,异常数据:" + dataHex, e);
}
return dto;
}
/**
* 获取SM2加密工具对象
*
* @param privateKey 加密私钥
* @param publicKey 加密公钥
* @return 处理结果
*/
private static SM2 getSM2(String privateKey, String publicKey) {
ECPrivateKeyParameters ecPrivateKeyParameters = null;
ECPublicKeyParameters ecPublicKeyParameters = null;
if (StringUtils.isNotBlank(privateKey)) {
ecPrivateKeyParameters = BCUtil.toSm2Params(privateKey);
}
if (StringUtils.isNotBlank(publicKey)) {
if (publicKey.length() == 130) {
//这里需要去掉开始第一个字节 第一个字节表示标记
publicKey = publicKey.substring(2);
}
String xhex = publicKey.substring(0, 64);
String yhex = publicKey.substring(64, 128);
ecPublicKeyParameters = BCUtil.toSm2Params(xhex, yhex);
}
//创建sm2 对象
SM2 sm2 = new SM2(ecPrivateKeyParameters, ecPublicKeyParameters);
sm2.usePlainEncoding();
sm2.setMode(SM2Engine.Mode.C1C2C3);
return sm2;
}
/**
* 生成一对 C1C2C3 格式的SM2密钥
*
* @return 处理结果
*/
public static ApiEncryptInfoDTO getSM2Key() {
ApiEncryptInfoDTO dto = new ApiEncryptInfoDTO();
//创建sm2 对象
SM2 sm2 = SmUtil.sm2();
byte[] privateKeyByte = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
//这里公钥不压缩 公钥的第一个字节用于表示是否压缩 可以不要
byte[] publicKeyByte = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
try {
String privateKey = HexUtil.encodeHexStr(privateKeyByte);
String publicKey = HexUtil.encodeHexStr(publicKeyByte);
dto.setPublicKey(publicKey);
dto.setPrivateKey(privateKey);
} catch (Exception e) {
log.error("获取SM2密钥出错", e);
}
return dto;
}
/**
* 获取一个随机的SM4密钥
*
* @return 处理结果
*/
public static ApiEncryptInfoDTO getSM4Key() {
String sm4Key = RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER, 16);
ApiEncryptInfoDTO dto = new ApiEncryptInfoDTO();
dto.setKey(sm4Key);
return dto;
}
}
最后是测试工具类
package com.bw.note.util.SM2.SP;
import org.junit.Test;
public class test {
/**
* 测试SM2加密
*/
@Test
public void test01(){
ApiEncryptInfoDTO dto = new ApiEncryptInfoDTO();
dto.setData("123456");
dto = SM2Utils.encrypt2Data(dto);
System.out.println(dto.getDataHex());
}
/**
* 测试SM2解密
*/
@Test
public void test02(){
ApiEncryptInfoDTO dto = new ApiEncryptInfoDTO();
dto.setDataHex("046A331A8227AD1CCC7E33ECEAEF7CF1CD7D3F3EEE2218E4A0AD1BE08ED5E65F0DB6811656FAE4CD1A16D8E79DE66FCF80A08158CD7E34523E76975789B18AE2C9FF9012BD47E0F84BC778538D6A6C17304A83F2F57014EC0C257987D8DA93403D53F193234DB8");
dto = SM2Utils.decrypt2Data(dto);
System.out.println(dto.getData());
}
/**
* 生成一对 C1C2C3 格式的SM2密钥
*/
@Test
public void test03(){
ApiEncryptInfoDTO dto = SM2Utils.getSM2Key();
System.out.println(dto.getPublicKey());
System.out.println(dto.getPrivateKey());
}
/**
* 获取一个随机的SM4密钥
*/
@Test
public void test04(){
ApiEncryptInfoDTO dto = SM2Utils.getSM4Key();
System.out.println(dto.getKey());
}
/**
* 测试SM4加密
*/
@Test
public void test05(){
ApiEncryptInfoDTO dto = new ApiEncryptInfoDTO();
dto.setData("123456");
dto = SM2Utils.encrypt4Data(dto);
System.out.println(dto.getDataHex());
}
/**
* 测试SM4解密
*/
@Test
public void test06(){
ApiEncryptInfoDTO dto = new ApiEncryptInfoDTO();
dto.setDataHex("0c878839ddba1631931ca9e7f9c981eb");
dto = SM2Utils.decrypt4Data(dto);
System.out.println(dto.getData());
}
}