项目外网部署的时候经常会有要求数据加密传输的情况,特别是企事业单位的项目,另为安全或者红头文件计,经常要求使用国密算法,因为涉及交互,所以使用SM2非对称加密。
后端(Springboot)
(1)所需主要依赖(其他如有缺失自行百度即可):
<!-- hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.10</version>
</dependency>
<!-- 第三方加密功能依赖 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.68</version>
</dependency>
(2)SM2功能方法的创建
博主这里在后端把相关的加解密功能做成了功能接口,所以这里直接展示service实现类的代码,里边的统一返回参数改成自己的类型即可
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 com.ruoyi.common.constant.EncryptConstant;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.encrypt.dto.ApiEncryptInfoDTO;
import com.ruoyi.common.utils.encrypt.dto.Result;
import com.ruoyi.common.utils.encrypt.service.ApiEncryptService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.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;
import org.springframework.stereotype.Service;
/**
* 加解密相关功能实现类
*
* @author gbx
*/
@Service
@Slf4j
public class ApiEncryptServiceImpl implements ApiEncryptService {
/**
* SM2加密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
@Override
public Result encrypt2Data(ApiEncryptInfoDTO dto) {
String publicKey = dto.getPublicKey();
// 若为空,使用默认
if (StringUtils.isBlank(publicKey)) {
publicKey = EncryptConstant.PUBLIC_KEY;
}
String data = dto.getData();
//创建sm2 对象
SM2 sm2 = getSM2(null, publicKey);
String dataHex = sm2.encryptBcd(data, KeyType.PublicKey);
dto.setDataHex(dataHex);
return new Result().ok(dto);
}
/**
* SM2解密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
@Override
public Result decrypt2Data(ApiEncryptInfoDTO dto) {
String privateKey = dto.getPrivateKey();
// 若为空,使用默认
if (StringUtils.isBlank(privateKey)) {
privateKey = EncryptConstant.PRIVATE_KEY;
}
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);
throw new BaseException("SM2解密失败");
}
return new Result().ok(dto);
}
/**
* SM4加密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
@Override
public Result encrypt4Data(ApiEncryptInfoDTO dto) {
//指定的密钥
String key = dto.getKey();
// 若为空,使用默认
if (StringUtils.isBlank(key)) {
key = EncryptConstant.SM4_KEY;
}
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);
throw new BaseException("SM4加密数据异常");
}
return new Result().ok(dto);
}
/**
* SM4解密
*
* @param dto 包含加解密相关参数信息的实体
* @return 处理结果
*/
@Override
public Result decrypt4Data(ApiEncryptInfoDTO dto) {
//指定的密钥
String key = dto.getKey();
// 若为空,使用默认
if (StringUtils.isBlank(key)) {
key = EncryptConstant.SM4_KEY;
}
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);
throw new BaseException("SM4解密数据异常");
}
return new Result().ok(dto);
}
/**
* 生成一对 C1C2C3 格式的SM2密钥
*
* @return 处理结果
*/
@Override
public Result 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);
throw new BaseException("获取SM2密钥出错");
}