Bootstrap

加密后的敏感字段进行模糊查询解决办法

在现代信息化社会中,数据安全问题越来越受到人们的关注。尤其是一些敏感字段,如用户密码、身份证号码、银行卡号等,如果直接存储在数据库中,一旦数据库被攻破,则这些敏感数据就会被窃取。为了保障数据安全,许多应用程序会将这些敏感字段进行加密存储。但是,加密后的敏感字段如何进行模糊查询呢?本篇博客将介绍加密后的敏感字段进行模糊查询的最佳解决办法。

加密算法的选择

在进行加密存储之前,我们需要选择合适的加密算法。一般来说,加密算法可以分为对称加密算法和非对称加密算法两种。

对称加密算法

对称加密算法是指加密和解密使用相同的密钥。常见的对称加密算法有DES、3DES、AES等。对称加密算法的优点是加密解密速度快,适合对大量数据进行加密。但是,由于加密和解密使用相同的密钥,如果密钥泄露,则数据将无法保密。

非对称加密算法

非对称加密算法是指加密和解密使用不同的密钥。常见的非对称加密算法有RSA、DSA等。非对称加密算法的优点是密钥管理方便,不容易泄露。但是,由于加密和解密使用不同的密钥,加密解密速度较慢,适合对少量数据进行加密。

哈希算法

除了对称加密算法和非对称加密算法,还有一种支持模糊查询的哈希算法。哈希算法是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。常见的哈希算法有MD5、SHA1、SHA256等。哈希算法的优点是可以将任意长度的消息压缩成固定长度的摘要,且不可逆,不同的消息会得到不同的摘要。因此,可以将敏感数据进行哈希处理后存储在数据库中,即使数据库被攻破,攻击者也无法得到原始数据。

数据库存储方式的选择

在选择加密算法之后,我们需要选择合适的数据库存储方式。常见的数据库存储方式有明文存储和加密存储两种。

明文存储

明文存储是指将原始数据直接存储在数据库中。这种方式的优点是存储和查询速度快,但是安全性较差,容易被攻击者窃取敏感数据。

加密存储

加密存储是指将原始数据进行加密后存储在数据库中。这种方式的优点是安全性较高,即使数据库被攻破,攻击者也无法得到原始数据。但是,由于加密解密需要消耗一定的计算资源,因此存储和查询速度较慢。

模糊查询的实现

在选择加密算法和数据库存储方式之后,我们需要考虑如何进行模糊查询。常见的模糊查询方式有通配符匹配和正则表达式匹配两种。

通配符匹配

通配符匹配是指使用通配符来匹配字符串。常见的通配符有“”和“?”。“”表示匹配任意字符序列(包括空字符序列),“?”表示匹配任意单个字符。例如,如果要查询所有以“abc”开头的字符串,可以使用“abc*”进行匹配。

通配符匹配的优点是简单易用,适合对少量数据进行查询。但是,由于通配符匹配需要遍历所有的数据,因此查询速度较慢。

正则表达式匹配

正则表达式匹配是指使用正则表达式来匹配字符串。正则表达式是一种描述字符序列特征的语言,可以用来匹配、查找和替换字符串。例如,如果要查询所有以“abc”开头的字符串,可以使用“^abc.*”进行匹配。

正则表达式匹配的优点是灵活性高,可以匹配各种复杂的字符串模式。但是,由于正则表达式匹配需要消耗大量的计算资源,因此查询速度较慢。

加密算法和数据库存储方式的优化

在实际应用中,我们需要根据实际需求和安全性要求进行权衡,选择合适的解决方案。同时,我们还可以通过优化加密算法和数据库存储方式来提高查询速度。

索引优化

索引是一种用于加速数据库查询的数据结构。常见的索引包括B树、B+树、哈希表等。如果我们选择了哈希算法进行加密存储,可以使用哈希表来优化查询速度。哈希表是一种基于哈希函数实现的数据结构,可以快速地进行查找操作。同时,我们还可以使用数据库的索引功能来优化查询速度,例如使用B+树索引来加速查询。

哈希表优化

如果我们选择了哈希算法进行加密存储,可以使用哈希表来优化查询速度。哈希表是一种基于哈希函数实现的数据结构,可以快速地进行查找操作。在哈希表中,每个数据项都有一个对应的索引值,通过索引值可以快速地查找到对应的数据项。如果查询条件中包含通配符,可以将通配符转换为正则表达式,并使用正则表达式匹配来优化查询速度。

例子

首先,我们需要使用非对称加密算法对敏感字段进行加密。在本例中,我们使用RSA算法对字符串进行加密。

import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.Cipher;

public class RSAUtil {
    // 生成公钥和私钥对
    public static KeyPair generateKeyPair(int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(keySize);
        return keyPairGenerator.generateKeyPair();
    }

    // 加密
    public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    // 解密
    public static byte[] decrypt(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
}

然后,我们需要使用SHA-256哈希算法对加密后的敏感字段进行哈希处理。在本例中,我们使用Java自带的MessageDigest类实现SHA-256哈希算法。

import java.security.MessageDigest;

public class HashUtil {
    // 哈希处理
    public static byte[] hash(byte[] data) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(data);
        return messageDigest.digest();
    }
}

接下来,我们需要使用Java的JDBC API连接数据库,并将加密后的敏感字段存储在数据库中。在本例中,我们使用MySQL数据库,并使用JDBC API实现数据库连接和数据存储。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBUtil {
    // 数据库连接信息
    private static final String URL = "jdbc:mysql://localhost:3306/test";
    private static final String USER = "root";
    private static final String PASSWORD = "123456";

    // 数据库连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }

    // 数据库查询
    public static ResultSet query(String sql, Object... params) throws SQLException {
        Connection connection = getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) {
            preparedStatement.setObject(i + 1, params[i]);
        }
        return preparedStatement.executeQuery();
    }

    // 数据库插入
    public static int insert(String sql, Object... params) throws SQLException {
        Connection connection = getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) {
            preparedStatement.setObject(i + 1, params[i]);
        }
        return preparedStatement.executeUpdate();
    }
}

然后,我们需要实现模糊查询功能。在本例中,我们使用通配符匹配的方式实现模糊查询。

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class SearchUtil {
    // 模糊查询
    public static List<String> search(String keyword) throws Exception {
        byte[] hash = HashUtil.hash(RSAUtil.encrypt(keyword.getBytes(), publicKey));
        ResultSet resultSet = DBUtil.query("SELECT * FROM user WHERE hash LIKE ?", "%" + bytesToHexString(hash) + "%");
        List<String> result = new ArrayList<>();
        while (resultSet.next()) {
            byte[] encryptedName = hexStringToBytes(resultSet.getString("name"));
            byte[] name = RSAUtil.decrypt(encryptedName, privateKey);
            result.add(new String(name));
        }
        return result;
    }

    // 字节数组转十六进制字符串
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                stringBuilder.append("0");
            }
            stringBuilder.append(hex);
        }
        return stringBuilder.toString();
    }

    // 十六进制字符串转字节数组
    public static byte[] hexStringToBytes(String hexString) {
        Pattern pattern = Pattern.compile("^([0-9a-fA-F]{2})+$");
        if (!pattern.matcher(hexString).matches()) {
            throw new IllegalArgumentException("Invalid hexadecimal string");
        }
        byte[] bytes = new byte[hexString.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16);
        }
        return bytes;
    }
}

最后,我们需要在程序入口处生成公钥和私钥,并使用公钥加密敏感字段,将加密后的敏感字段存储在数据库中。在本例中,我们使用Java的main方法作为程序入口。

import java.security.KeyPair;

public class Main {
    private static PublicKey publicKey;
    private static PrivateKey privateKey;

    public static void main(String[] args) throws Exception {
        // 生成公钥和私钥对
        KeyPair keyPair = RSAUtil.generateKeyPair(2048);
        publicKey = keyPair.getPublic();
        privateKey = keyPair.getPrivate();

        // 加密敏感字段并存储到数据库中
        String name = "张三";
        byte[] encryptedName = RSAUtil.encrypt(name.getBytes(), publicKey);
        byte[] hash = HashUtil.hash(encryptedName);
        DBUtil.insert("INSERT INTO user (name, hash) VALUES (?, ?)", bytesToHexString(encryptedName), bytesToHexString(hash));

        // 模糊查询敏感字段
        String keyword = "三";
        List<String> result = SearchUtil.search(keyword);
        System.out.println(result);
    }

    // 字节数组转十六进制字符串
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                stringBuilder.append("0");
            }
            stringBuilder.append(hex);
        }
        return stringBuilder.toString();
    }
}

总结

加密后的敏感字段进行模糊查询是一个比较复杂的问题,需要综合考虑加密算法、数据库存储方式和查询方式等多个因素。在实际应用中,我们需要根据实际需求和安全性要求进行权衡,选择合适的解决方案。同时,我们还可以通过优化加密算法和数据库存储方式来提高查询速度。

公众号请关注"果酱桑", 一起学习,一起进步!

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;