加密基础回顾:对称加密、非对称加密、哈希函数

在做密文查询之前,我得先跟你把加密基本功捋一遍。你想想看,密码学就像一把锁——你连锁的种类都不知道,怎么谈得上"在锁上做查询"?

我刚开始接触密文查询那会儿,以为只要会把数据加密存起来就行。后来踩了不少坑,才意识到:选错加密算法,查询性能可能差几百倍。这一章我们就聚焦三类核心加密工具:对称加密、非对称加密和哈希函数,以及它们各自在密文查询里的"用武之地"。

对称加密:快,但密钥管理是硬伤

对称加密,说白了就是加密和解密用同一把钥匙。好比你家大门,一把钥匙既能锁也能开。典型算法有 AES、SM4、ChaCha20。

我在项目中用过 AES-256-GCM 做全字段加密。速度确实快,在普通服务器上每秒能处理几百 MB 的数据。但问题也跟着来——

避坑指南: 对称加密在密文查询中最常见的问题是:你怎么知道某条密文对应哪个明文?如果每次加密都生成随机 IV(初始化向量),相同明文也会产出不同密文,那数据库里根本没法直接比对。我曾经在这个问题上卡了两周,后来改用确定性加密才把查询接上。

来看一个 AES-CBC 加解密的示例(Python + PyCryptodome):

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os

key = os.urandom(32)  # 256-bit key
iv = os.urandom(16)   # 随机IV,每次不同

def encrypt(plaintext):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ct = cipher.encrypt(pad(plaintext.encode(), 16))
    return iv + ct  # 把IV拼在密文前面

def decrypt(ciphertext):
    iv = ciphertext[:16]
    ct = ciphertext[16:]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return unpad(cipher.decrypt(ct), 16).decode()

你看,如果每次 IV 都不同,encrypt("alice") 每次返回的密文都不一样。直接 WHERE ciphertext = ? 肯定查不到。但如果放弃随机 IV,用固定 IV 做确定性加密,又会引入一定的安全性风险。这是个经典权衡。

特性概率性加密(随机 IV)确定性加密(固定 IV)
安全性高(相同明文 → 不同密文)中(模式可被分析)
直接等值查询不支持支持
典型场景数据存储密文索引/查询

非对称加密:安全分发,但慢得让人心疼

非对称加密有两把钥匙——公钥加密,私钥解密。RSA 和 ECC 是主力。你想想看,这在密文查询里怎么用?

我习惯把非对称加密当作"密钥交换的桥梁",而不是直接加密大量数据。原因很简单:RSA-2048 加密 256 字节就要花几毫秒,而 AES 加密同样数据只要几微秒,差了两到三个数量级。

但在某些密文查询方案中(比如非对称可搜索加密),公钥加密正好派上用场:

经验之谈: 假设你要把一组关键字加密后上传到服务器,服务器想查询某个关键字是否在集合中。用对称加密的话,服务器必须持有密钥才能做比对——这不就暴露了数据吗?而非对称加密可以设计成:服务器只持有公钥,能对查询令牌做运算,但解不了密。我曾经用 ElGamal 加密做关键字陷门生成,效果还行,就是计算量比对称加密大了几十倍。

核心痛点总结:

  • 加密/解密速度比对称加密慢 100-1000 倍
  • 密文膨胀严重(RSA-2048 加密 256 字节 → 输出 256 字节,但实际有效载荷只有 32 字节左右)
  • 抗量子攻击能力弱(Shor 算法基本可以瓦解 RSA/ECC)

哈希函数:单向的指纹机

哈希函数把任意长度的数据映射成固定长度的摘要。SHA-256、SHA-3、SM3 都是常客。它有三个关键特性:不可逆、抗碰撞、雪崩效应(输入改一位,输出面目全非)。

在密文查询里,哈希函数最常见的作用就是构建关键字索引。举个例子:

import hashlib

keyword = "username:alice"
# 直接用哈希——不安全,因为字典攻击
h = hashlib.sha256(keyword.encode()).hexdigest()

# 加盐哈希——我推荐这种做法
salt = os.urandom(16)
h_salted = hashlib.pbkdf2_hmac('sha256', keyword.encode(), salt, 100000)

你想想看,如果直接用 SHA256("alice") 做密文索引,坏人直接穷举常见用户名,很快就能把数据库里的密文和明文对应起来。所以我在项目里只要涉及到哈希索引,一定会加盐或者做哈希拉伸(比如 PBKDF2、bcrypt)。

核心认知: 哈希函数本身不是加密——你没法解密。但在密文查询里,它正好可以用来生成"单向可搜索索引"。服务器可以用哈希后的值做等值查找,却得不到原始的明文关键字。这就是"可搜索加密"最朴素的实现方式。

三者如何协同——一张关系图

光讲概念容易晕。我把这三个工具在密文查询里的职责梳理成下面这张图:

密文查询中三大加密工具的协同关系 对称加密(AES/SM4) · 加密实际数据 · 速度最快,用于大块数据 非对称加密(RSA/ECC) · 密钥分发 & 陷门生成 · 安全但慢,查少量关键数据 哈希函数(SHA-256/SM3) · 生成不可逆指纹 · 索引构建 & 完整性校验 密钥交换 陷门哈希 典型密文查询架构: 数据用 AES 加密存储 → 关键字用加盐哈希构建索引 → 查询时用 RSA/ECC 生成陷门 加密明文 创建索引 生成陷门 各角色在查询流程中的位置: 1. 客户端用对称密钥加密原始数据 → 密文上传到服务器 2. 客户端对关键字做加盐哈希 → 得到可搜索索引(不可逆) 3. 查询时,客户端用自己持有的私钥对要查的关键字生成陷门 4. 服务端用陷门与索引做匹配 → 返回匹配的密文 → 客户端解密

实战中的选型建议

说了这么多,你可能会问:我的密文查询系统到底该用哪个?

我一般这样判断:

  • 如果只需要等值查询,优先考虑确定性对称加密 + 加盐哈希索引。性能和安全性都说得过去。
  • 如果需要范围查询或模糊匹配,对称加密基本帮不上忙,得靠保序加密或可搜索加密的变体(那又是后面的章节了)。
  • 如果需要多用户写入,非对称加密的陷门机制更合适——每个用户用自己的私钥生成查询令牌,服务端不需要知道明文。
我个人的惯用组合: 数据体量不大(百万行以内)时,我用 AES-256-GCM 加密明文 + HMAC-SHA256 做关键字索引。体量大到千万级以上时,我会考虑引入非对称可搜索加密来分担陷门生成的计算量。没有银弹,每换一个场景我都得重新权衡。

嗯,加密基础就聊到这儿。你先把这几种工具的差异吃透,后面章节里我们会一步步把它们搭成真正的密文查询系统。