分词组合加密实现加密后可模糊查询
之前有次遇到一个数据库敏感字段脱敏的需求,
使用mybaits对数据库内的敏感字段进行加解密,
但是在加密储存之后,业务上但凡涉及到加密的字段都没办法进行模糊搜索
Mybatis插件对指定字段加解密
最近要实现这个字段加密之后支持模糊搜索,经查阅资料大致有以下几种方法(建议先看实现代码,否则可能看的有点绕):
1、创建明文字段:【不可取】给需要加密的字段创建一个保存明文的字段,模糊查询去查明文字段,其他查询去查加密字段。 (吐槽下,这都不能被称之为一个方案,本来就是要加密那些敏感信息,结果库里还是有明文相当于脱了裤子放屁);
2、分词组合加密【常用】:此类加密针对字符类型(数值类型的字段也影响不大,改成字符类型然后程序里用到的地方给转成数值类型就行,或者在解密插件配置操作这张表的时候把解密后的字符串转数值类型),这种方法有个前提,要有一个字段用来储存对应加密字段的分词组合加密密文,原理就是在保存一个字符串之前先按照一定长度给它拆分,然后每拆分一次就加密一次得到一个加密串,最后将所有加密串组合起来储存,在模糊搜索的时候对模糊搜索的条件也进行相同操作然后去比对这个分词组合加密串;
3、特定的算法【没研究明白】:大概意思是采用一个特殊的算法,可以把数据加密,且查询条件加密经过加密后可以直接作为模糊搜索条件去匹配加密的字段,查询资料的时候基本最后都有一句需要专业的算法工程师搞一个算法;
4、先解密后查询【小数据量可以】:如果加密的字段不多且数据量不大,每次查询前先获取所有数据,且对加密字段进行解密,在内存里进行模糊查询;
5、数据库层面加解密【增加数据库压力】:保存数据的时候调用数据库函数进行加密,模糊查询的时候先解密字段再模糊查询:select * fom table where 解密函数(加密字段) like ‘%…%’;如此;
6、分词组合不加密【容易被破解】:相比分词组合加密,用来模糊搜索的分词组合字段直接储存明文,模糊查询的时候查询条件分词后无需加密直接匹配,,性能是高了一点,省去了两步加密操作,且数据库里存储分词查询条件会短一点,,但是安全性折扣了很多,但凡被获取到这个分词查询条件可以经过暴力测试分词长度破译;
【分词组合加密】代码实现:
public class CryptographicFuzzySearch {// 代数据库private static Map<String,String> db = new HashMap<>(64);// 数据加密保存字段private static final String COLUMN_DATA_ENCRYPT = "column_data_encrypt";// 数据分词组合加密保存字段private static final String COLUMN_CUT_DATA_ENCRYPT = "column_cut_data_encrypt";public static void main(String[] args) {String sourceData = "hello,world";String searchData = "hello";int cutLength = 4;// 原数据分词加密组合List<String> sourceDataCutArray = strCut(cutLength, sourceData);String sourceDataCutEncryptStr = sourceDataCutArray.stream().map(item -> AesUtil.encode(item)).collect(Collectors.toList()).stream().collect(Collectors.joining());// 原数据加密String sourceDataEncrypt = AesUtil.encode(sourceData);// 保存db.put(COLUMN_DATA_ENCRYPT,sourceDataEncrypt);db.put(COLUMN_CUT_DATA_ENCRYPT,sourceDataCutEncryptStr);// 打印数据System.out.println(JSONUtil.toJsonStr(db));// 查询条件分词加密组合List<String> searchDataCutArray = strCut(cutLength, searchData);String searchDataCutEncryptStr = searchDataCutArray.stream().map(item -> AesUtil.encode(item)).collect(Collectors.toList()).stream().collect(Collectors.joining());// 查询if (db.get(COLUMN_CUT_DATA_ENCRYPT).contains(searchDataCutEncryptStr)){System.out.println("模糊查询匹配成功,查询条件原文:" + searchData + " 分词加密组合后:" + searchDataCutEncryptStr);System.out.println("原数据分词加密组合:" + db.get(COLUMN_DATA_ENCRYPT));System.out.println("原数据解密:" + AesUtil.decode(db.get(COLUMN_DATA_ENCRYPT)));}else{System.out.println("模糊查询未匹配!!!");}}/*** 分词* @param length 分词长度* @param text 明文数据* @return*/public static List<String> strCut(int length , String text){List<String> result = new LinkedList<>();for(int i = 0; i < text.length()-length+1; i++){String substring = text.substring(i, i + length);result.add(substring);}return result;}}/*** aes加密工具类*/
class AesUtil {public final static byte[] KEY = "thisisencodekey".getBytes();private static SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, KEY);/*** 加密** @param data* @return*/public static String encode(String data) {try {if (StringUtils.isEmpty(data)) {return data;}return aes.encryptHex(data);} catch (Exception e) {e.printStackTrace();}return data;}/*** 解密** @param data* @return*/public static String decode(String data) {try {String decryptStr = aes.decryptStr(data);return ("null").equals(decryptStr) ? "" : decryptStr;} catch (Exception e) {e.printStackTrace();}return data;}
}
经此,和加解密插件结合,就能实现一个自动划加解密且支持模糊搜索的功能了!