个人信息的处理包括个人信息的收集、存储、使用、加工、传输、提供、公开、删除等。 查看完整《个人信息保护法》,请点击关于《个人信息保护法》的相关规定
大致内容:
系统中的用户信息、订单信息、地址信息等等,都应在公司数据安全红线范围之内,并且,如果公司需要申请一些安全资质,数据保密也是必须拿下的一关,如果发生数据泄露,对公司,对用户都是极大的损失。数据安全架构体系包括很多东西,如DB、日志、文件等数据转换的场景等。就需要我们进行:数据库中的数据脱敏。
简单介绍: Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。 标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。 为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充'='号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。 Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。
结论:
在对称加密算法中,数据发信方将明文(原始数据)和加密密钥(mi yao)一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。
PS:通过密钥对原文进行加密,加密后的密文也需要通过密钥才能解密回原文。
常见的对称加解密算法有DES、3DES、AES、Blowfish;
对于较小的内容(不应超过200个字符),推荐使用AES加密,其速度,大小和简单性具有较好的表现。
DES 数据加密标准是IBM提出的,第一个基于Lucifer算法的加密技术。作为第一个加密标准,自然会带有些许瑕疵漏洞使其不是特别的安全。(不推荐) 3DES 三重DES是DES的加强版,提供了DES的三重安全性。他和DES使用同样的算法,只是做了三次加密来提升安全级别。(不推荐) Blowfish Blowfish是一种对称分组密码,可以使用它来代替 DES 或 IDEA 算法。 Blowfish 采用32 位指令处理器设计,比现有的 DES(数据加密标准)快得多。如今,DES 对于大多数现代应用程序来说并不安全。 Blowfish 一次操作 8 个字节。对于具有 8 字节块大小的密码,在大约 32 GB 数据之后重复一个块的可能性很高。这意味着,如果您对大于 32 GB 的单个消息进行加密,这几乎是统计上的保证,您将有一个重复的块。那很不好。出于这个原因,GnuPG 建议您不要使用带有 8 字节数据块的密码来对整个硬盘进行批量加密。如果您将要加密的消息或数据保持在大约 4 GB 以下,那么您不太可能会遇到任何问题。 但是这个算法仍然是相关的,并且有些人使用它,因为它在计算能力有限的情况下很容易实现。Blowfish 是免许可、无专利的,这意味着任何人都可以在他们的应用程序中自由使用它。 Blowfish 是为软件设计的。它使用大量内存并且密钥设置时间相对较长,但之后速度很快。它接受高达 448 位的密钥(这是巨大的过度杀伤力),并一次加密 64 位数据。 AES AES 代表高级加密标准,它是世界上最流行的加密标准之一。它由比利时密码学家 Joan Daemen 和 Vincent Rijmen 于 2001 年创立,因此也被称为Rijndael。
AES 最初在美国使用,但随后在世界范围内广受欢迎,因为它被批准为美国联邦标准,旨在取代DES加密技术。
AES 是一种对称分组密码,因为它使用一个密钥进行解密和加密。
AES 具有三种分组密码,具体取决于用于加密、解密的密钥长度: - AES-128(128 位), - AES-192(192 位), - AES-256(256 位)。 - 这些密码加密和解密 128 位块中的数据。但是,它们使用不同大小的键。
AES 一次处理 16 个字节。对于具有 16 字节块大小的密码,您需要加密包含比整个 Internet 中更多数据的单个消息。换句话说,块的重复不是问题。
AES 旨在加密敏感的政府级数据。它易于在软件和硬件中实施,通常用于保护数据免受网络攻击。
如今,AES 可免费用于公共和私人组织。但是,非政府组织在使用 AES 方面存在一些限制。
主要用于:
- 无线网络安全,
- 文件加密,
- SSL/TLS。
AES 也可以包含在某些商业产品中,例如 Wi-Fi、消息传递应用程序、VPN,以及用于本机处理器支持。AES 包含在许多标准中,例如国际标准化组织 (ISO) 标准和国际电工委员会 (IEC) 标准。
AES 是一种有效且安全的加密方式。流行的应用程序,如 WhatsApp 和 Signal,正在使用这种类型的加密。
AES 为世界各地的许多企业和政府提供安全服务。高级加密标准在不同的加密包中可用。它也是唯一获得美国国家安全局批准的可供公众使用的密码。
AES 在硬件和软件方面都很快。它接受 128、192 或 256 位密钥。它具有相当快的密钥设置时间和相对较小的内存需求,并且一次加密 128 位数据。
BLOWFISH和AES的区别 Blowfish 和 AES 都是对称加密算法,这意味着加密和解密密钥是相同的。这也意味着共享相同的密钥以实现安全通信。
这种类型的加密通常用于批量数据加密。它也可以很容易地通过硬件实现。对称加密的主要问题是拥有解密密钥的人可以解密所有数据。
Blowfish 因其批量加密和解密而运行迅速。Blowfish 使用 64 位的块大小。它甚至比在软件中实现的 AES 还要快,但仍然不如 AES 有效。Blowfish 不受专利保护,这使得这种加密软件的使用如此广泛。
快大小 Blowfish 的块大小为 64 位,而 AES 为 128 位。小块大小可能是一个严重的安全问题;由于块大小较小,Blowfish 更容易受到攻击。 大块大小是 AES 的一个优势;但是,这取决于您如何使用密码,因为它有时会使消息比通常情况下更长。但是,如果大量数据被加密,这也可以增加对某些理论攻击的保护。
硬件加速和 RAM AES 的巨大优势在于它具有硬件加速功能,使密码速度更快,同时仍能免受缓存定时攻击。它基于一个不同的概念:替代置换网络,并且速度非常快。Blowfish 没有硬件加速。 此外,AES 可以在没有 RAM 的硬件中实现,而 Blowfish 的密钥设置需要 RAM,这可能需要一些时间。Blowfish 尝试使密钥耗尽攻击变得困难,因为它使初始密钥设置过程变得缓慢。 低 RAM 要求和高速使 AES 成为许多组织的有利加密选项。它还在不同类型的硬件上表现良好,从小型芯片卡到高性能计算机。
结论
虽然Blowfish 可以支持更大的密钥大小,但是块大小影响密码的强度,不妨使用 AES。
因此,AES 是对称加密标准竞争的赢家,事实上是当今最流行的对称密码。
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
* @Auther: leimingwen
* @desc AES 加密工具类
* @version V1.0
* @Date: 2022/1/26 15:25
*/
public class AESUtil {
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法
/**
* AES 加密操作
*
* @param content 待加密内容
* @param key 加密密码
* @return 返回Base64转码后的加密数据
*/
public static String encrypt(String content, String key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器
byte[] result = cipher.doFinal(byteContent);// 加密
return Base64.encodeBase64String(result);//通过Base64转码返回
} catch (Exception ex) {
Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
/**
* AES 解密操作
*
* @param content
* @param key
* @return
*/
public static String decrypt(String content, String key) {
try {
//实例化
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
//使用密钥初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
//执行操作
byte[] result = cipher.doFinal(Base64.decodeBase64(content));
return new String(result, "utf-8");
} catch (Exception ex) {
Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
/**
* 生成加密秘钥
*
* @return
*/
private static SecretKeySpec getSecretKey(final String password) {
//返回生成指定算法密钥生成器的 KeyGenerator 对象
KeyGenerator kg = null;
try {
kg = KeyGenerator.getInstance(KEY_ALGORITHM);
//AES 要求密钥长度为 128
kg.init(128, new SecureRandom(password.getBytes()));
//生成一个密钥
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public static void main(String[] args) {
//加密
String encode = encrypt("17376752153","nuobeimima");
System.out.println(encode);
//解密
String decode = decrypt(encode,"nuobeimima");
System.out.println(decode);
}
}
缺点: - 不易维护,增加排查问题难度。 - 别的业务需要脱敏时,依然需要对应的业务中进行同样处理。侵入性强,扩展性极低。
- 加密函数:AES_ENCRYPT('待加密串', '自定义秘钥')
- 解密函数:AES_DECRYPT('待解密串', '自定义秘钥')
SQL示例: ①字段类型需要设置为blob
insert into user(mobile) values (AES_ENCRYPT('18812345678', 'nuobeimima'));
select CAST(AES_DECRYPT(mobile, 'nuobeimima') as char) as mobile from user;
②输出HEX,字段类型需要设置为blob
insert into user(mobile) values (HEX(AES_ENCRYPT('18812345678', 'nuobeimima')));
select CAST(AES_DECRYPT(UNHEX(mobile), 'nuobeimima') as char) as mobile from user;
③输出Base64,字段类型可以为blob 也可以为varchar
insert into user(mobile) values (to_base64(AES_ENCRYPT('18812345678', 'nuobeimima')));
select CAST(AES_DECRYPT(from_base64(mobile), 'nuobeimima') as char) as mobile from user;
缺点: - 直接在字段上使用函数急剧拉低MySQL性能
由于项目数据库中间件使用的是Mybatis,所以使用Mybatis中的BaseTypeHandler的一个类型处理器,对数据进行AES加密存入数据
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot</artifactId>
<version>1.1.1</version>
</dependency>
package com.klhtxxzh.common.config.mybatis;
import org.apache.commons.lang.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @Auther: leimingwen
* @Date: 2022/1/26 14:50
*/
public class DataDesensitizationUtils {
private static final String OTHER_LOGIN_KEY = "8ce87b8ec346ff4c80635f667d1592ae";
/**
* 加密
* @param text
* @return
* @throws Exception
*/
public static String encrypt(String text) {
try {
byte[] plaintext = text.getBytes();
IvParameterSpec ivspec = new IvParameterSpec(OTHER_LOGIN_KEY.substring(16).getBytes());
SecretKeySpec keyspec = new SecretKeySpec(OTHER_LOGIN_KEY.substring(0, 16).getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return new BASE64Encoder().encode(encrypted).trim();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解密
* @param text
* @return
*/
public static String decrypt(String text) {
try {
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(text);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keyspec = new SecretKeySpec(OTHER_LOGIN_KEY.substring(0, 16).getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(OTHER_LOGIN_KEY.substring(16).getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString.trim();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 手机号码前三后四脱敏
public static String mobileEncrypt(String mobile) {
if (StringUtils.isEmpty(mobile) || (mobile.length() != 11)) {
return mobile;
}
return mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
}
package com.klhtxxzh.common.config.mybatis;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @Auther: leimingwen
* @Date: 2022/1/26 15:36
*/
public class AESTypeHandler extends BaseTypeHandler<Object> {
/**
* 非空字段加密
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) {
try {
if (StringUtils.isBlank((String) parameter)) return;
ps.setString(i, DataDesensitizationUtils.encrypt((String) parameter));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 非空字段解密
*/
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
String col = rs.getString(columnName);
try {
if (StringUtils.isBlank(col)) return col;
return DataDesensitizationUtils.decrypt(col);
} catch (Exception e) {
e.printStackTrace();
}
return col;
}
/**
* 可空字段加密
*/
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String col = rs.getString(columnIndex);
try {
if (StringUtils.isBlank(col)) return col;
return DataDesensitizationUtils.encrypt(col);
} catch (Exception e) {
e.printStackTrace();
}
return col;
}
/**
* 可空字段解密
*/
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String col = cs.getString(columnIndex);
try {
if (StringUtils.isBlank(col)) return col;
return DataDesensitizationUtils.decrypt(col);
} catch (Exception e) {
e.printStackTrace();
}
return col;
}
}
在原有的type-aliases-package后面拼接上:com.klhtxxzh.common.config.mybatis
# MyBatis
mybatis:
# 搜索指定包别名
type-aliases-package: com.klhtxxzh.**.domain,com.klhtxxzh.common.config.mybatis
type-handlers-package: com.klhtxxzh.common.config.mybatis
返回处设置 typeHandler="AESTypeHandler"
<!--返回模型-->
<resultMap type="User" id="UserResult">
<result property="realName" column="real_name" typeHandler="AESTypeHandler" />
</resultMap>
编辑处设置 typeHandler=AESTypeHandler
<!--插入-->
<insert id="insertUser" parameterType="User">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="realName != null and realName != ''"> real_name,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="realName != null and realName != ''">#{realName, typeHandler=AESTypeHandler},</if>
</trim>
</insert>
<!--修改-->
<update id="updateCareProduct" parameterType="CareProduct">
update user
<trim prefix="SET" suffixOverrides=",">
<if test="realName != null and realName != ''"> real_name = #{realName, typeHandler=AESTypeHandler},</if>
</trim>
where id = #{id}
</update>
数据显示正常
数据库为密文
五、总结
因为Mybatis-Plus的实现可能不会编写mapper文件
所以需要在实体类中加注解实现
1、Mybatis-Plus 不是所有版本都支持相关注解或注解内方法,在 pom.xml 需添加支持对应组件的版本
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
2、在实体类上添加@TableName注解,并设置autoResultMap = true
3、在需要进行处理加解密的字段上添加 @TableField(typeHandler = XxxxxxHandler.class)
如:
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
@TableName(value = "my_table", autoResultMap = true)
public class MyEntity {
@TableField(typeHandler = WstTypeHandler.class)
private String myField;
// 其他字段和方法
}
PS:注意@TableName一定要加上autoResultMap = true,否则字段的@TableField注解typeHandler将不会生效
评论