MySQL敏感数据加密-Java实现方案

一、准备工作(环境要求)

1、版本要求

​ mysql必须是mysql5.6以上版本,通过以下命令查看是否版本支持

lua 复制代码
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.24    |
+-----------+

2、字段类型要求

​ 数据表字段类型必须是以下类型中的其中一个

sql 复制代码
 varbinary、binary、blob

二、数据库演示

1、创建演示表

sql 复制代码
mysql> CREATE TABLE `user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `mobile` varbinary(50) NOT NULL,
  `id_card` varbinary(155) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

注意: mobile(手机号) 和 id_card(身份证号) 的字段类型为varbinary,我们将对这两个字段进行加密存储。

2、插入加密数据

sql 复制代码
mysql> insert into user(username, mobile, id_card) 
	values('Tom', aes_encrypt('15101123111', 'test_key'), aes_encrypt('372522195710100019','test_key'));
	
mysql> insert into user(username, mobile, id_card) values('Mary', aes_encrypt('15101123111', 'key'), aes_encrypt('372522195710100019','key'));

注意:这里在使用 **insert** 插入时,将需要 加密的字段使用aes_encrypt函数并指定秘钥key。

3、查看插入数据

sql 复制代码
mysql> select * from user;

-----------++----+----------+------------------------------------+---------------------------------------------------------
| id | username | mobile                             | id_card                                                            |
+----+----------+------------------------------------+--------------------------------------------------------------------+
|  1 | Tom      | 0x28E415A075D38ECFEE0AFB86027231BD | 0x45397D31A49C50EEFE669D2145110F134A90D6E834084FBA38E4B5098F2EDAED |
|  2 | Mary     | 0xC932282B9EBC4FC82A225A56FB12BB63 | 0xF748DFDB3C0994DB9C06F4FB863AAAE5DC1685EA49D8AAA5C28952D5BFDD8A35 |
+----+----------+------------------------------------+--------------------------------------------------------------------+

**提示:**此处是直接查询,返回的是加密后的值,神仙也看不出是啥,如果猜测的话首先要猜出加密方式,其次是秘钥。

4、解密数据

sql 复制代码
mysql> select id, username, cast(aes_decrypt(mobile, 'test_key') as char charset utf8 ) as mobile from user where id = 1;
+----+----------+-------------+
| id | username | mobile      |
+----+----------+-------------+
|  1 | Tom      | 15101123111 |
+----+----------+-------------+

mysql> select id, username, cast(aes_decrypt(id_card, 'key') as char charset utf8 ) as id_card from user where id = 2;
+----+----------+--------------------+
| id | username | id_card            |
+----+----------+--------------------+
|  2 | Mary     | 372522195710100019 |
+----+----------+--------------------+

注意:

​ 1)、id为1的那条数据的加密密钥为key 为 test_key,id 为2的加密密钥为key 为 key 。 解密时如果密钥不对将查询结果为null

​ 2)、解密 函数aes_decrypt 外层需要使用cast 进行处理,否则将返回二进制值而不是解密后的值。

三、Java + mybatis 实现加解密

1、Java代码中什么都不需要动

提示: 此处java使用的是mybatis持久化数据,如果使用hibernatemybatis-plus等框架,请自行研究,理论上通用但需要变通一下

2、mybatis中插入和更新用法

ini 复制代码
<!-- 插入 -->
<insert id="saveCertificate" parameterType="com.test.candidate.bean.Certificate">
        insert into certificate (
            oa_serial,
            begin_date,
            end_date,
            certificate_type,
            certificate_code,
            certificate_code_again,
            address,
            country,
            created_at,
            updated_at
        ) values (
            #{oaSerial},
            #{beginDate},
            #{endDate},
            #{certificateType},
            AES_ENCRYPT(#{certificateCode,jdbcType=VARCHAR},'perinfo'),
            AES_ENCRYPT(#{certificateCodeAgain,jdbcType=VARCHAR},'perinfo'),
            #{address},
            #{country},
            #{createdAt},
            #{updatedAt}
        );
    </insert>
    
    <!-- 更新 -->
     <update id="updateCertificate" parameterType="com.test.candidate.bean.Certificate">
        update certificate set
            begin_date = #{beginDate},
            end_date = #{endDate},
            certificate_type = #{certificateType},
            certificate_code = AES_ENCRYPT(#{certificateCode,jdbcType=VARCHAR},'perinfo'),
            certificate_code_again = AES_ENCRYPT(#{certificateCodeAgain,jdbcType=VARCHAR},'perinfo'),
            address = #{address},
            country = #{country},
            updated_at = #{updatedAt}
        where oa_serial = #{oaSerial} and id = #{id}
    </update>

注意:插入和更新是加密:AES_ENCRYPT(#{certificateCode,jdbcType=VARCHAR},'perinfo')

3、mybatis中查询用法

sql 复制代码
<!-- 查询 -->
<select id="findList" resultMap="PartCertificateMap">
        SELECT
            begin_date,
            end_date,
            certificate_type,
            cast(AES_DECRYPT(certificate_code,'perinfo') as char charset utf8) certificate_code,
            cast(AES_DECRYPT(certificate_code_again,'perinfo') as char charset utf8) certificate_code_again,
            address,
            country
         FROM certificate
        where
         oa_serial = #{oaSerial}
    </select>

**注意:**查询时解密:cast(AES_DECRYPT(certificate_code,'perinfo') as char charset utf8) certificate_code,

四、其他说明

网上有人说无需更改数据类型,直接将加密数据存储到carchar类型的字段中,然后将字符集改成latin1, 虽然此种方式确实也能实现加解密,但是mysql中insert 时,会报warning, 并且可能会带来隐患,使用以下命令查看警告信息

csharp 复制代码
mysql> show warnings();

并不推荐此种方式:

sql 复制代码
mysql> CREATE TABLE t_passwd_3(pass varchar(32)) CHARSET latin1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> INSERT INTO t_passwd_3 SELECT AES_ENCRYPT('text', 'key3');
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
 
mysql> SELECT AES_DECRYPT(pass, 'key3') FROM t_passwd_3;
+---------------------------+
| AES_DECRYPT(pass, 'key3') |
+---------------------------+
| text   |
+---------------------------+
1 row in set (0.00 sec)

MySQL官方给出的描述信息如下:

Many encryption and compression functions return strings for which the result might contain arbitrary byte values. If you want to store these results, use a column with a VARBINARY or BLOB binary string data type. This will avoid potential problems with trailing space removal or character set conversion that would change data values, such as may occur if you use a nonbinary string data type (CHAR, VARCHAR, TEXT).

大意是,如果用此方法,直接将加密后的串存入char/varchar/text类型中,在做字符转换的时或空格被删除时,可能会带来潜在的影响。

相关推荐
宋小黑5 分钟前
JDK 6到25 全版本网盘合集 (Windows + Mac + Linux)
java·后端
念何架构之路14 分钟前
Go进阶之panic
开发语言·后端·golang
先跑起来再说17 分钟前
Git 入门到实战:一篇搞懂安装、命令、远程仓库与 IDEA 集成
ide·git·后端·elasticsearch·golang·intellij-idea
码农阿豪1 小时前
Flask应用上下文问题解析与解决方案:从错误日志到完美修复
后端·python·flask
威迪斯特1 小时前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Springboot的扶贫众筹平台为例,包含答辩的问题和答案
java·spring boot·后端
程序员良许2 小时前
三极管推挽输出电路分析
后端·嵌入式
Java水解2 小时前
【JAVA 进阶】Spring AOP核心原理:JDK与CGLib动态代理实战解析
后端·spring
Java水解2 小时前
Spring Boot 4 升级指南:告别RestTemplate,拥抱现代HTTP客户端
spring boot·后端
宫水三叶的刷题日记2 小时前
工商银行今年的年终奖。。
后端