做過金融項目或者政府項目的大佬肯定都實現過加密數據的模糊查詢功能,大家都是怎么實現的呢?今天就來簡單舉例一些實現加密數據模糊查詢的方案。方案1:應用層內存過濾
原理
全表查詢加密數據
在內存中解密后過濾
public List<User> unsafeSearch(String keyword) {
List<User> allUsers = userRepository.findAll();
return allUsers.stream()
.filter(user -> decrypt(user.getName()).contains(keyword))
.collect(Collectors.toList());
}
致命缺陷:
方案2:數據庫函數解密
MySQL示例
SELECT * FROM users
WHERE AES_DECRYPT(name_encrypted, 'secret_key') LIKE '%張%';
優缺點:
? 不改動業務代碼
? 密鑰暴露在數據庫
? 無法使用索引(全表掃描)
Java安全寫法:
@Query(nativeQuery = true,
value = "SELECT * FROM users WHERE " +
"CONVERT(AES_DECRYPT(name_encrypted, ?1) USING utf8) LIKE ?2")
List<User> searchByName(String key, String keyword);
方案3:分詞+密文索引(推薦)
1. 核心思路
2. 代碼實現
(1). 增強版分詞策略
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
public class EnhancedTokenizer {
public static List<String> smartTokenize(String text) {
List<String> tokens = new ArrayList<>();
try (StringReader reader = new StringReader(text)) {
IKSegmenter seg = new IKSegmenter(reader, true);
Lexeme lex;
while ((lex = seg.next()) != null) {
tokens.add(lex.getLexemeText());
if (lex.getLength() > 1) {
for (int i = 2; i <= lex.getLength(); i++) {
tokens.add(text.substring(lex.getBeginPosition(),
lex.getBeginPosition() + i));
}
}
}
}
return tokens.stream().distinct().collect(Collectors.toList());
}
}
2. 雙重加密索引表
@Entity
@Table(name = "user_search_index")
public class SearchIndex {
@Id
@GeneratedValue
private Long id;
@Column(name = "user_id")
private Long userId;
@Column(name = "token_hash")
private String tokenHash;
@Column(name = "token_cipher")
private String tokenCipher;
public static SearchIndex create(Long userId, String token) throws Exception {
SearchIndex index = new SearchIndex();
index.setUserId(userId);
index.setTokenHash(AESUtil.encryptWithKey(token, "HASH_KEY"));
index.setTokenCipher(AESUtil.encryptWithKey(token, "CIPHER_KEY"));
return index;
}
}
3. 智能查詢流程
public List<User> secureFuzzySearch(String keyword) throws Exception {
List<String> tokens = EnhancedTokenizer.smartTokenize(keyword);
List<String> hashList = tokens.parallelStream()
.map(t -> {
try {
return AESUtil.encryptWithKey(t, "HASH_KEY");
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
Pageable page = PageRequest.of(0, 100);
List<Long> userIds = indexRepository
.findUserIdsByTokenHashes(hashList, page);
return userRepository.findAllById(userIds).stream()
.filter(user -> {
try {
String decrypted = AESUtil.decrypt(user.getNameCipher());
return decrypted.contains(keyword);
} catch (Exception e) {
return false;
}
})
.collect(Collectors.toList());
}
方案4:同態加密(學術級方案)
基于SEAL庫的實現
<dependency>
<groupId>com.microsoft.seal</groupId>
<artifactId>seal-jni</artifactId>
<version>3.7.2</version>
</dependency>
public class HomomorphicSearch {
public static void demo() throws Exception {
EncryptionParameters params = new EncryptionParameters(SchemeType.bfv);
params.setPolyModulusDegree(4096);
params.setCoeffModulus(CoeffModulus.BFVDefault(4096));
params.setPlainModulus(PlainModulus.Batching(4096, 20));
SEALContext context = new SEALContext(params);
KeyGenerator keyGen = new KeyGenerator(context);
PublicKey publicKey = keyGen.createPublicKey();
SecretKey secretKey = keyGen.secretKey();
Encryptor encryptor = new Encryptor(context, publicKey);
Plaintext plain = new Plaintext("123");
Ciphertext encrypted = new Ciphertext();
encryptor.encrypt(plain, encrypted);
Evaluator evaluator = new Evaluator(context);
Ciphertext result = new Ciphertext();
evaluator.add(encrypted, encrypted, result);
Decryptor decryptor = new Decryptor(context, secretKey);
Plaintext decrypted = new Plaintext();
decryptor.decrypt(result, decrypted);
System.out.println(decrypted);
}
}
現實限制:
僅支持有限運算(加減/乘)
性能差(單次操作需100ms+)
不支持字符串操作
方案5:可信執行環境(TEE)
基于Intel SGX的解決方案
void ecall_fuzzy_search(const char* encrypted_query, size_t len) {
std::string query = decrypt_in_enclave(encrypted_query, len);
sqlite3* db;
sqlite3_open(":memory:", &db);
std::string sql = "SELECT * FROM users WHERE name LIKE '%" + query + "%'";
encrypt_results(db_exec(sql));
}
優勢:
硬件級安全
性能接近明文查詢
挑戰:
需要特定硬件支持
開發復雜度高
閱讀原文:原文鏈接
該文章在 2025/6/14 16:36:35 編輯過