MyBatis数据库脱敏

一、国密SM4加密

<dependency>
     <groupId>org.bouncycastlegroupId>
     <artifactId>bcprov-jdk15onartifactId>
     <version>1.70version>
dependency>

1.1 工具类

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Arrays;

public class Sm4Util {

    private static final String ALGORITHM = "SM4";
    private static final String TRANSFORMATION_ECB = ALGORITHM + "/ECB/PKCS7Padding";
    private static final String TRANSFORMATION_CBC = ALGORITHM + "/CBC/PKCS7Padding";
    private static final String TRANSFORMATION_ECB_NoPADDING = ALGORITHM + "/ECB/NoPadding";
    private static final String TRANSFORMATION_CBC_NoPADDING = ALGORITHM + "/CBC/NoPadding";


    private static final String ALGORITHM_HMAC_SM4 = "HmacSHA256";
    private static final String PROVIDER_NAME = "BC";


    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 签名
     */
    public static byte[] sign(byte[] key, byte[] data) throws Exception {
        Mac mac = Mac.getInstance(ALGORITHM_HMAC_SM4, PROVIDER_NAME);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_HMAC_SM4);
        mac.init(secretKeySpec);
        return mac.doFinal(data);
    }

    /**
     * 验证签名
     */
    public static boolean verify(byte[] key, byte[] data, byte[] signature) throws Exception {
        Mac mac = Mac.getInstance(ALGORITHM_HMAC_SM4, PROVIDER_NAME);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_HMAC_SM4);
        mac.init(secretKeySpec);
        byte[] computedSignature = mac.doFinal(data);
        return Arrays.equals(computedSignature, signature);
    }

    /**
     * /CBC/PKCS7Padding 加密
     */
    public static byte[] encryptByCbcPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        // CBC模式需要一个初始化向量
        byte[] iv = new byte[16]; // 随机生成或指定
        Arrays.fill(iv, (byte) 0x00); // 示例中填充为0
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * /CBC/PKCS7Padding 解密
     */
    public static byte[] decryptByCbcPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        byte[] iv = new byte[16]; // 随机生成或指定
        Arrays.fill(iv, (byte) 0x00); // 示例中填充为0
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/PKCS7Padding 加密
     */
    public static byte[] encryptByEcbPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/PKCS7Padding 解密
     */
    public static byte[] decryptByEcbPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/NoPadding 加密
     */
    public static byte[] encryptByEcbPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/NoPadding 解密
     */
    public static byte[] decryptByEcbPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /CBC/NoPadding 加密
     */
    public static byte[] encryptByCbcPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * /CBC/NoPadding 解密
     */
    public static byte[] decryptByCbcPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }


}

1.2 SM4Util使用测试

@SpringBootTest(classes = ServerApplication.class)
public class Sm4UtilTest {

    private static final byte[] KEY = KeyGenUtil.generate128BitKey(); // 16字节128位的密钥
    private static final String DATA = "This is a test message for SM4 encryption.";
    //16字节或16字节的整数倍字符串,无填充模式NoPadding要求
    private static final String DATA16 = "HelloWorldYesYes";

    @Test
    public void testEncryptDecryptEcbPkcs7Padding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByEcbPkcs7Padding(KEY, DATA.getBytes());
        //签名
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        //验证
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByEcbPkcs7Padding(KEY, encrypted);
        System.out.println("解密后数据" + new String(decrypted));
    }

    @Test
    public void testEncryptDecryptCbcPkcs7Padding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByCbcPkcs7Padding(KEY, DATA.getBytes());
        //签名
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        //验证
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByCbcPkcs7Padding(KEY, encrypted);
        System.out.println("解密后数据" + new String(decrypted));
    }


    @Test
    public void testEncryptDecryptEcbPkcs7PaddingNoPadding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByEcbPkcs7PaddingNoPadding(KEY, DATA16.getBytes());
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByEcbPkcs7PaddingNoPadding(KEY, encrypted);
        System.out.println("解密后数据" + new String(decrypted));
    }

    @Test
    public void testEncryptDecryptCbcPkcs7PaddingNoPadding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByCbcPkcs7PaddingNoPadding(KEY, DATA16.getBytes());
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByCbcPkcs7PaddingNoPadding(KEY, encrypted);
        System.out.println("解密后数据" + new String(decrypted));
    }
}

二、MyBatis使用SM4加解密

2.1 创建表结构

DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
	id BIGINT(20) PRIMARY KEY AUTO_INCREMENT COMMENT '自增主键',
	name VARCHAR(128) NOT NULL DEFAULT '' COMMENT '姓名'
);

SELECT * FROM user_info;
INSERT INTO user_info (name) VALUES ('c4f6371b67fac0f224c5b44456d3c71f');

2.2 pom

<dependency>
	<groupId>com.baomidougroupId>
	<artifactId>mybatis-plus-boot-starterartifactId>
	<version>3.5.2version>
dependency>

2.3 配置连接地址

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql:///test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=true
    driver-class-name: com.mysql.cj.jdbc.Driver

2.4 编写mapper和xml

public interface UserInfoMapper {

    UserInfo getUserInfoById(@Param("id") Long id);

    List<UserInfo> list();
}

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zn.opit.demo.mapper.UserInfoMapper">

    <select id="getUserInfoById" resultType="com.zn.opit.demo.entity.UserInfo">
        SELECT id, name FROM user_info WHERE id = #{id}
    select>

    <select id="list" resultType="com.zn.opit.demo.entity.UserInfo">
        SELECT id, name FROM user_info
    select>
mapper>

2.5 编写SQL拦截器对查询字段解密

@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
        }
)
public class EncryptInterceptor implements Interceptor {

    private static final byte[] KEY = Sm4Util.decodeHex("6c376375e8e3979f91f805faa860dd43");// KeyGenUtil.generate128BitKey(); // 16字节128位的密钥

    @SuppressWarnings(value = "unchecked")
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object resultObj = invocation.proceed();
        ArrayList<UserInfo> list = (ArrayList<UserInfo>) resultObj;
        for (UserInfo userInfo : list) {
            byte[] decrypted = Sm4Util.decryptByEcbPkcs7Padding(KEY, Hex.decodeHex(userInfo.getName()));
            userInfo.setName(new String(decrypted));
        }
        return list;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
}

2.6 配置解密拦截器

@Configuration
public class MyBatisConfig {

    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer() {
        return configuration -> {
            // 在这里可以修改MyBatis的全局配置
            configuration.setMapUnderscoreToCamelCase(true);
            // 添加自定义的插件等
            // 加解密插件
            EncryptInterceptor encryptInterceptor = new EncryptInterceptor();

            //添加插件-configuration.addInterceptor(...);
            configuration.addInterceptor(encryptInterceptor);
        };
    }
}

2.7 测试执行

@SpringBootTest(classes = ServerApplication.class)
public class MyBatisInterceptorTest {

    @Resource
    private UserInfoMapper userInfoMapper;

    @Test
    public void test() {
        UserInfo userInfoById = userInfoMapper.getUserInfoById(1L);
        System.out.println(userInfoById.getName());
        System.out.println("====================");
        List<UserInfo> list = userInfoMapper.list();
        list.forEach(item -> System.out.println(item.getName()));
    }
}

MyBatis数据库脱敏_第1张图片
MyBatis数据库脱敏_第2张图片

你可能感兴趣的:(Demo,mybatis,数据库,密码学)