SpringBoot 国密 SM4 配置加密(工具类实现)
·
SpringBoot 国密 SM4 配置加密(工具类实现)
前言
在企业级 SpringBoot 项目中,配置文件(application.yml)往往存储着大量敏感信息(比如:数据库密码、Redis 密码、MQ 账号密码、第三方密钥等)。如果明文存储,一旦配置文件泄露,会直接导致核心服务被入侵,属于等保、密评、PIPL 合规高风险项。因此,配置文件敏感信息必须加密,且生产环境必须使用国密 SM4 算法,满足国产化、合规要求。
本文基于 Java8 + SpringBoot2.3.7,采用纯工具类方式实现国密 SM4 配置加密解密。工具类方式:
- 无需启动 Spring 容器,静态调用即可使用;
- 自带 main 方法,本地直接运行生成密文、解密测试;
- 支持单层 / 多层配置读取解析;
- 自动识别 ENC(加密串) 格式,无感自动解密;
- 无侵入、轻量、可直接集成到现有项目。
一、核心依赖
项目依赖 Hutool 国密工具包 + BouncyCastle 加密库:
<!-- Hutool 国密加密(SM4) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.8.20</version>
</dependency>
<!-- BouncyCastle 密码学提供方 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
二、YML 配置文件(application.yml)
密钥规则:SM4 密钥必须是 16 位字符(数字 / 字母均可),生产环境严禁硬编码在配置文件,建议放到 Nacos / 密钥管理系统。加密格式固定:ENC(SM4 加密后的密文)
# ====================== 国密 SM4 配置 ======================
sm4:
key: 1234567890123456 # 必须16位,测试使用,生产环境请放配置中心
# ====================== 测试加密配置 ======================
## 单层配置
testPassword: ENC(1bb11e4cf9eebd2538d53ebcaacb9cfe)
## 多层配置
test:
password: ENC(1bb11e4cf9eebd2538d53ebcaacb9cfe)
三、国密 SM4 加密解密工具类(Sm4Utils)
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
/**
* 国密 SM4 对称加密工具类
* 提供明文加密、密文解密 (本地可直接运行生成配置文件密文)
*
*/
public class Sm4Utils {
/**
* 从配置文件读取 SM4 密钥(16位)
* 生产环境:密钥不要硬编码,建议从配置中心/密钥系统获取
*/
private static final String SM4_KEY = ApplicationConfigUtils.getProperty("sm4.key");
/**
* 初始化 SM4 加密对象(全局单例,避免重复创建)
*/
private static final SM4 SM4_INSTANCE = SmUtil.sm4(SM4_KEY.getBytes());
/**
* SM4 加密(明文 → 16进制密文)
* @param plainText 明文字符串
* @return 可直接放入 ENC() 的密文
*/
public static String encrypt(String plainText) {
if (plainText == null || plainText.isEmpty()) {
throw new IllegalArgumentException("加密明文不能为空");
}
return SM4_INSTANCE.encryptHex(plainText);
}
/**
* SM4 解密(16进制密文 → 明文)
* @param cipherText 去除 ENC() 后的密文
* @return 原始明文
*/
public static String decrypt(String cipherText) {
if (cipherText == null || cipherText.isEmpty()) {
throw new IllegalArgumentException("解密密文不能为空");
}
return SM4_INSTANCE.decryptStr(cipherText);
}
public static void main(String[] args) {
// 待加密的明文(如数据库密码、Redis密码)
String originalData = "123456";
System.out.println("=== SM4 加解密测试 ===");
System.out.println("加密前明文:" + originalData);
// 加密(结果直接复制到配置文件 ENC() 中)
String encryptData = Sm4Utils.encrypt(originalData);
System.out.println("加密后密文:" + encryptData);
// 解密验证
String decryptData = Sm4Utils.decrypt(encryptData);
System.out.println("解密后明文:" + decryptData);
}
}
四、配置文件读取解密工具类(ApplicationConfigUtils)
无需启动 Spring,可静态直接调用,自动识别 ENC(xxx) 并解密,支持单层 / 多层配置。
/**
* SpringBoot 配置文件读取工具类
* 无需启动Spring容器,读取 application.yml 并自动解密 ENC(xxx)
* 支持单层、多层级配置读取 + 国密 SM4 自动解密
*
*/
public class ApplicationConfigUtils {
/** YAML 解析器 */
private static final Yaml YAML = new Yaml();
/** 配置文件缓存 Map */
private static final Map<String, Object> YML_CONFIG_MAP;
/** 加密内容标识前缀、后缀 */
private static final String ENCRYPT_PREFIX = "ENC(";
private static final String ENCRYPT_SUFFIX = ")";
// 静态代码块:项目启动时一次性加载配置文件到内存
static {
try {
// 读取 resources 下的 application.yml
Resource resource = new ClassPathResource("application.yml");
if (!resource.exists()) {
throw new RuntimeException("未找到配置文件:application.yml,请检查路径");
}
try (InputStream inputStream = resource.getInputStream()) {
YML_CONFIG_MAP = YAML.load(inputStream);
}
if (YML_CONFIG_MAP == null || YML_CONFIG_MAP.isEmpty()) {
throw new RuntimeException("application.yml 配置文件内容为空");
}
} catch (Exception e) {
throw new RuntimeException("【配置加载失败】读取 application.yml 异常", e);
}
}
/**
* 读取配置(自动解密 ENC 格式)
* @param key 配置项(如:test.password、testPassword)
* @return 解密后的真实值
*/
public static String getProperty(String key) {
if (key == null || key.isBlank()) {
throw new IllegalArgumentException("配置项 key 不能为空");
}
String[] keys = key.split("\\.");
Object value = getNestedValue(YML_CONFIG_MAP, keys, 0);
// 自动识别并解密
return autoDecrypt(value);
}
/**
* 读取配置(带默认值,避免报错)
*/
public static String getProperty(String key, String defaultValue) {
try {
return getProperty(key);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 递归获取多层配置值(如 a.b.c)
*/
private static Object getNestedValue(Map<String, Object> currentMap, String[] keys, int index) {
if (index == keys.length - 1) {
return currentMap.get(keys[index]);
}
Object nextNode = currentMap.get(keys[index]);
if (!(nextNode instanceof Map)) {
throw new RuntimeException("配置节点不存在:" + keys[index]);
}
return getNestedValue((Map<String, Object>) nextNode, keys, index + 1);
}
/**
* 自动解密(判断是否为 ENC(xxx) 格式,是则解密,否则直接返回)
*/
private static String autoDecrypt(Object value) {
if (value == null) {
return null;
}
String strValue = String.valueOf(value);
// 匹配加密格式,自动解密
if (strValue.startsWith(ENCRYPT_PREFIX) && strValue.endsWith(ENCRYPT_SUFFIX)) {
String cipherText = strValue.substring(ENCRYPT_PREFIX.length(), strValue.length() - ENCRYPT_SUFFIX.length());
return Sm4Utils.decrypt(cipherText);
}
// 非加密格式直接返回
return strValue;
}
public static void main(String[] args) {
System.out.println("=== 配置文件解密测试 ===");
// 单层配置
String pwd1 = getProperty("testPassword");
System.out.println("单层配置 testPassword 解密结果:" + pwd1);
// 多层配置
String pwd2 = getProperty("test.password");
System.out.println("多层配置 test.password 解密结果:" + pwd2);
}
}
五、运行测试(直接执行 main 方法)
1. 生成密文(运行 Sm4Utils中main方法)
=== SM4 加解密测试 ===
加密前明文:123456
加密后密文:1bb11e4cf9eebd2538d53ebcaacb9cfe
解密后明文:123456
复制加密后的密文,直接填入 application.yml 的 ENC(xxx) 中。
2. 配置解密(运行 ApplicationConfigUtils工具类中main方法)
=== 配置文件解密测试 ===
单层配置 testPassword 解密结果:123456
多层配置 test.password 解密结果:123456
六、业务代码调用
无需注入、无需配置,可直接静态调用,即可获取解密后的真实值。如:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// 获取配置信息
String pwd2 = ApplicationConfigUtils.getProperty("test.password");
System.out.println("pwd2: " + pwd2);
}
}
七、问题解决
- 运行ApplicationConfigUtils中main方法报错,找不到配置文件
原因:项目是多模块项目,项目有多个子模块(api,service,mapper,pojo,common),ApplicationConfigUtils工具类位置放到common子模块中,和application.yml配置文件不在同一模块。 - 报错:Unchecked cast: ‘java.lang.Object’ to ‘java.util.Map’
原因:配置解析时类型转换不规范。解决方案:ApplicationConfigUtils工具类中getSafeMap方法中将 Object 安全强转为 Map<String, Object>,消除 Unchecked cast 警告。 - 报错:NullPointerException(空指针)
原因1:application.yml文件不存在或路径错误 。解决方案:检查文件是否在resources目录下,文件名是否正确(application.yml)。
原因2:SM4密钥未配置或配置错误。解决方案:检查yml中sm4.key是否配置,是否为16位。
八、总结
本文实现了一套轻量、无侵入、合规的 SpringBoot 国密 SM4 配置加密方案:
- 纯工具类实现,不依赖 Spring 容器,本地可直接测试;
- 自动识别 ENC(xxx) 格式,业务代码无感解密;
- 支持单层 / 多层 YML 配置,满足企业级项目需求;
- 完全符合等保、密评、国产化要求,可直接用于生产环境。
集成后彻底解决配置文件敏感信息明文泄露风险,一步到位满足安全合规要求。
更多推荐
所有评论(0)