java 敏感数据加密存储,明文查询
·
java 数据加密存储
- 定义注解用来标识字段是否需要加密
package com.common.annotation;
import java.lang.annotation.*;
/**
* @Auther: ZHANG PU
* @Date: 2025/11/13 09:30
* @Description: 需要加密的字段 注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptField {
}
- 定义一个mybatis拦截器,在执行sql之前对需要加密或者解密的数据进行处理
package com.framework.interceptor;
import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.utils.Sm4Util;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Properties;
/**
* @Auther: ZHANG PU
* @Date: 2025/11/13 09:29
* @Description: mybatis 拦截器
*/
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})})
public class EncryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("进入mybatis 加密拦截器------------------------------------");
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
if (ms.getSqlCommandType() == SqlCommandType.INSERT || ms.getSqlCommandType() == SqlCommandType.UPDATE)
{
// 插入/更新时加密字段
encryptField(parameter);
}
// 执行原方法(查询/更新)
Object result = invocation.proceed();
if (ms.getSqlCommandType() == SqlCommandType.SELECT)
{
// 查询时解密字段(结果可能是单个对象或集合)
decryptField(result);
}
// 对查询参数中的加密字段进行加密
if (ms.getSqlCommandType() == SqlCommandType.SELECT)
{
encryptField(parameter);
}
return result;
}
/**
* 功能描述: 加密参数中的标记字段
*
* @param parameter
* @author zhang pu
* @date 15:48 2025/11/13
*/
private void encryptField(Object parameter) throws Exception {
if (parameter == null)
{
return;
}
Field[] fields = parameter.getClass().getDeclaredFields();
for (Field field : fields)
{
if (field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
{
field.setAccessible(true);
String value = (String) field.get(parameter);
if (value != null)
{
// 加密后重新设置
field.set(parameter, Sm4Util.encryptTextSm4(Sm4Util.MY, value));
}
}
}
}
/**
* 功能描述: 解密结果中的标记字段
*
* @param result
* @author zhang pu
* @date 15:48 2025/11/13
*/
private void decryptField(Object result) throws Exception {
if (result == null)
{
return;
}
// 处理集合
if (result instanceof List<?>)
{
for (Object item : (List<?>) result)
{
decryptSingleField(item);
}
} else
{
// 处理单个对象
decryptSingleField(result);
}
}
private void decryptSingleField(Object obj) throws Exception {
if (obj == null)
{
return;
}
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields)
{
if (field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
{
field.setAccessible(true);
String value = (String) field.get(obj);
if (value != null)
{
// 解密后重新设置
field.set(obj, Sm4Util.decodeTextSm4(Sm4Util.MY, value));
}
}
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可配置加密参数(如密钥)
}
}
- 编写国产加密SM4共工具
<!--国产加密-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.54</version>
</dependency>
package com.common.utils;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @description: SM4加密
* @author: 张璞
* @date 10:20 2022/9/13
*/
public class Sm4Util {
// 密文前缀(用于标识已加密)
private static final String ENC_PREFIX = "ENC(";
private static final String ENC_SUFFIX = ")";
static
{
Security.addProvider(new BouncyCastleProvider());
}
private static final String ENCODING = "UTF-8";
public static final String ALGORITHM_NAME ="SM4";
/**
* 功能描述: 加密算法/分组加密模式/分组填充方式\PKCS5Padding-以8个字节为一组进行分组加密\定义分组加密模式使用:PKCS5Paddings
*/
public static final String ALGORITHM_NAME_CBC_PADDING ="SM4/CBC/PKCS5Padding";
/**
* 功能描述: 128-32位16进制;256-64位16进制
*/
public static final int DEFAULT_KEY_SIZE = 128;
/**
* 密钥
*/
public static final String MY="xxx";
/**
* 功能描述: 测试
* @author zhang pu
* @date 10:31 2022/9/13
*/
public static void main(String[] args) throws Exception {
String s = generateKeyString();
System.out.println("密钥:"+s);
String uuid= UUID.randomUUID().toString();
System.out.println("带加密数据:"+uuid);
//sm4对称加密
String s1 = encryptTextSm4(s, uuid);
System.out.println("加密数据:"+s1);
String s2 = decodeTextSm4(s, s1);
System.out.println("解密数据:"+s2);
//sm3加密
// String s3 = encryptTextSm3(uuid);
// System.out.println(verify(uuid,s3));
}
/**
* 功能描述: sm4加密 加密模式:CBC
* 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位
* @author zhang pu
* @date 10:31 2022/9/13
* @param hexKey 16进制密钥(忽略大小写)
* @param paramStr 待加密字符串
* 返回16进制的加密字符串
*/
public static String encryptTextSm4(String hexKey, String paramStr) throws Exception {
if(StringUtils.isBlank(paramStr))
{
return null;
}
// 若已加密(包含前缀),直接返回(避免重复加密)
if (isEncrypted(paramStr))
{
return paramStr;
}
String result = "";
// 16进制字符串-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// String-->byte[]
byte[] srcData = paramStr.getBytes(ENCODING);
// 加密后的数组
byte[] cipherArray = encrypt_Cbc_Padding(keyData, srcData);
// byte[]-->hexString
result = ByteUtils.toHexString(cipherArray);
// 添加密文前缀
return ENC_PREFIX + result + ENC_SUFFIX;
}
/**
* 功能描述: sm4解密 解密模式:采用CBC
* @author zhang pu
* @date 10:31 2022/9/13
* @param hexKey 16进制密钥
* @param text 16进制的加密字符串(忽略大小写)
*/
public static String decodeTextSm4(String hexKey, String text) throws Exception {
if(StringUtils.isBlank(text))
{
return null;
}
// 若未加密(不包含前缀),直接返回,防止反复解密
if (!isEncrypted(text))
{
return text;
}
// 移除密文前缀
String realCiphertext = text.substring(ENC_PREFIX.length(), text.length() - ENC_SUFFIX.length());
String result = "";
byte[] keyData = ByteUtils.fromHexString(hexKey);
byte[] resultData = ByteUtils.fromHexString(realCiphertext);
// 解密
byte[] srcData = decrypt_Cbc_Padding(keyData, resultData);
result = new String(srcData, ENCODING);
return result;
}
/**
* 功能描述: 获取ENC(...) 并进行解密
*
* @author zhang pu
* @date 15:28 2025/11/13
* @param oldStr
*/
public static String getJmStr(String oldStr) {
if (Objects.isNull(oldStr))
{
return null;
}
try
{
// 正则表达式:匹配 ENC(...) 格式,其中 (...) 不含右括号
Pattern pattern = Pattern.compile("ENC\\([^)]*\\)");
Matcher matcher = pattern.matcher(oldStr);
List<String> encList = new ArrayList<>();
while (matcher.find())
{
// 提取匹配到的 ENC(...) 子串
encList.add(matcher.group());
}
for (String s : encList)
{
String s1 = Sm4Util.decodeTextSm4(Sm4Util.MY, s);
oldStr = oldStr.replaceAll(Pattern.quote(s), s1);
}
} catch (Exception e)
{
}
return oldStr;
}
/**
* 功能描述: sm3加密
* 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
* @author zhang pu
* @date 10:39 2022/9/13
* @param text 内容
*/
public static String encryptTextSm3(String text) throws UnsupportedEncodingException {
byte[] bytes = text.getBytes(ENCODING);
byte[] hash = hash(bytes);
String s = ByteUtils.toHexString(hash);
return s;
}
public static byte[] hash(byte[] srcData){
SM3Digest digest=new SM3Digest();
digest.update(srcData,0,srcData.length);
byte[] bytes = new byte[digest.getDigestSize()];
digest.doFinal(bytes,0);
return bytes;
}
/**
* 功能描述:判断元数据与加密数据是否一致
* @author zhang pu
* @date 10:56 2022/9/13
* 参数 srcStr 元数据
* 参数 sm3HexString 加密过的数据
*/
public static boolean verify(String srcStr,String sm3HexString) throws Exception{
boolean flag=false;
byte[] srcStrData = srcStr.getBytes(ENCODING);
byte[] sm3HexStringData = ByteUtils.fromHexString(sm3HexString);
byte[] hash = hash(srcStrData);
if(Arrays.equals(hash,sm3HexStringData))
{
flag= true;
}
return flag;
}
/**
* 功能描述: 生成密钥
* @author zhang pu
* @date 10:30 2022/9/13
*/
public static String generateKeyString() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
kg.init(DEFAULT_KEY_SIZE, new SecureRandom());
byte[] encoded = kg.generateKey().getEncoded();
return ByteUtils.toHexString(encoded);
}
/**
* 加密模式之CBC
* @param key
* @param data
* @return
* @throws Exception
* @explain
*/
public static byte[] encrypt_Cbc_Padding(byte[] key, byte[] data) throws Exception {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
cipher.init(mode, sm4Key, generateIV());
return cipher;
}
/**
* 功能描述: 初始化算法参数
* @author zhang pu
* @date 10:33 2022/9/13
*/
public static AlgorithmParameters generateIV() throws Exception {
byte[] iv = new byte[16];
//设置数据全为0
Arrays.fill(iv, (byte) 0x00);
AlgorithmParameters params = AlgorithmParameters.getInstance(ALGORITHM_NAME);
params.init(new IvParameterSpec(iv));
return params;
}
/**
* 功能描述: cbc模式解密
* @author zhang pu
* @date 10:33 2022/9/13
*/
public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] cipherText) throws Exception {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
/**
* 功能描述: 判断是否已加密 是否包含前缀
*
* @author zhang pu
* @date 10:40 2025/11/13
* @param value
*/
public static boolean isEncrypted(String value) {
return value != null && value.startsWith(ENC_PREFIX) && value.endsWith(ENC_SUFFIX);
}
}
更多推荐
所有评论(0)