第3章:加密算法基础——对称加密、非对称加密、哈希函数的角色

说到字段级加密,你首先得搞清楚一件事:到底该用哪种加密算法?

很多新手一上来就问我:“师傅,直接上AES不就完事了吗?”嗯,如果你只是要把一整块数据(比如整个硬盘)锁起来,AES确实够了。但字段级加密不一样——我们是在数据库的行和列里操作,要保证能精确查询、能部分解密、还要管好密钥。这活儿,单靠一种算法搞不定。

我个人的习惯是:把加密算法分成三类来理解——对称加密、非对称加密、哈希函数。每一类在字段级加密里都有自己的位置,谁替谁都不行。下面我掰开揉碎了讲。


3.1 对称加密:字段加密的主力

说白了,对称加密就是加密和解密用同一把钥匙。你想想看,这把钥匙要是丢了,数据就全完了;但优点是——速度快,非常适合批量加密字段。

在字段级加密里,主流选择是 AES-GCM。为什么是它?我总结了几点:

  • 认证加密(AEAD): 不光加密数据,还能验证数据有没有被篡改。你想想,如果攻击者改了你数据库里的一个密文,解密时GCM会直接报错。我在项目中遇到过有人用AES-CBC模式,结果被填充预言攻击搞崩了……从那以后,只要是新项目,我必推GCM。
  • 不需要填充: AES-GCM是流密码模式,明文多长密文就多长。对比一下AES-CBC,还得搞PKCS#7填充,字段一短就一堆麻烦。
  • 自带唯一性: GCM每次加密都要用不同的初始向量(IV)。这就意味着,同一个字段值(比如“张三”)加密两次,出来的密文完全不同。攻击者想通过密文重复来判断字段内容?没门。

下面是一个简单的Python示例,展示如何用AES-GCM加密一个字段值(比如用户的手机号):

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

key = AESGCM.generate_key(bit_length=256)   # 生成256位密钥
aesgcm = AESGCM(key)
nonce = os.urandom(12)                       # 96位随机IV
data = b"13800138000"                        # 手机号明文
aad = b"field:phone"                         # 关联数据(可选)

ciphertext = aesgcm.encrypt(nonce, data, aad)
# 存储时:nonce + ciphertext 一起存

这里有个细节:nonce是明文存的。每次加密都生成一个新的nonce,然后和密文拼接在一起存入数据库。解密时先拆出nonce,再解密。

我的建议: 非对称加密只在两种场景用:① 客户端加密、服务端解密(公钥在客户端);② 加密对称密钥本身。如果你能控制整个加解密流程,优先用对称加密+AES-GCM,性能差距是数量级的。

3.2 非对称加密:密钥分发的救星

那为什么还需要非对称加密呢?你想想看,如果你的微服务有10个节点,每个节点都要加密字段,那把对称密钥分给这10个节点怎么办?用明文发?疯了。用网络传输?万一被截获呢?

非对称加密解决的就是这个问题:公钥随便分,私钥自己藏着。

在字段级加密里,我主要用两类非对称算法:

算法 密钥长度 性能 典型用途
RSA 2048 / 4096 bit 慢 加密对称密钥、数字签名
ECC(椭圆曲线) 256 bit(≈ RSA 3072) 快 密钥交换、轻量级加密

RSA 我一般只用来加密短数据——比如加密一个AES密钥。原因很简单:RSA的明文长度不能超过密钥长度,你想直接用RSA加密一个长字段?那得分成很多块,性能直接崩掉。

ECC 我个人更推荐。同样的安全强度,ECC的密钥短得多,计算也快。我在一个IoT项目里用过 ECC + ECDH 做密钥协商,终端设备和服务端各持一对密钥,双方能安全地算出一个共享密钥,然后再用这个共享密钥做AES字段加密。这套流程在威胁建模里很常见,叫做混合加密。

注意: 非对称加密绝不用于直接加密字段值!我见过有人用RSA加密整条身份证号,结果:① 密文太长(RSA 2048加密后是256字节,而原始字段才18字节);② 性能极差,几千条数据就卡死了。正确的做法是用RSA/ECC加密一个临时的AES密钥,再用AES密钥加密字段。

3.3 哈希函数:防篡改与完整性校验

哈希函数不是用来加密的,它不可逆。你在字段级加密里可能不太直接用哈希来“隐藏”数据(除非是密码),但它有两个关键角色:

  • 完整性校验: 配合AES-GCM的认证标签,验证密文是否被篡改。
  • 盲索引: 当你想对加密字段做等值查询(比如查手机号),但数据库里存的是密文没法直接比较时,可以对原文做哈希,然后把哈希值作为索引列。查询时先算哈希,再匹配索引。当然,这只能用等值查询,范围查询不行。

我建议用 HMAC-SHA256 而不是裸的 SHA256。为什么?SHA256容易遭受长度扩展攻击,而HMAC通过加盐(密钥)解决了这个问题。在字段级加密里,我们一般把哈希的“盐”和一个全局密钥绑定,这样攻击者就算拿到了索引列,也无法反向推算出原始字段值。

一个常见的字段级加密表结构:
| id | phone_enc (BLOB) | phone_hmac (VARCHAR) |
| —— | ———————————— | ——————————————————— |
| 1 | nonce+ciphertext | 3f7b...a2c1 |
查询时:
1. 输入手机号 → 计算 HMAC(key, phone) → 匹配 phone_hmac 列
2. 找到对应行 → 取出 phone_enc → 用 AES-GCM 解密

3.4 三种算法如何配合?

你看,单一算法都有短板。对称加密快但密钥分发难,非对称加密能管密钥但性能差,哈希不能解密但能快速索引。 在字段级加密的威胁模型里,三者的配合是这样的:

  1. 非对称加密 保护对称密钥的传输(公钥加密对称密钥,私钥解密)。
  2. 对称加密(AES-GCM) 负责实际的字段加密,保证机密性和认证性。
  3. 哈希(HMAC) 提供索引能力和完整性校验,让查询不依赖解密。

我记得以前给一个金融系统做威胁建模时,客户坚持只用 AES 一把密钥跑到底。结果密钥轮换时,所有历史密文都要重新解密再加密,中间停机窗口根本不够。后来改成了非对称加密包裹对称密钥的结构,每次轮换只需要重新加密对称密钥(很小),字段密文原地不动。这个坑我踩过,希望你别踩。

字段级加密的算法协作框架 对称加密(AES-GCM) 非对称加密(RSA/ECC) 哈希(HMAC-SHA256) 加密字段值 加密/分发对称密钥 索引列 + 完整性校验 🔑 协作逻辑 1. 非对称加密保护对称密钥的生成和分发 2. 对称加密为主体字段数据提供高速加密/解密 3. 哈希函数为加密字段提供可查询的索引和篡改检测

3.5 算法选择决策要点

最后,我整理了一个快速决策指南,帮你判断在字段级加密里该用哪种算法:

  • 如果你的场景是加密大量字段值,并且加解密都在同一个信任域内 → 直接上 AES-GCM。
  • 如果有多方(客户端、服务端、第三方)需要安全传递密钥 → 用 ECC + ECDH 协商共享密钥,再用共享密钥做对称加密。
  • 如果你需要对加密字段做 等值查询,且不能解密全文 → 用 HMAC-SHA256 做盲索引。
  • 如果遇到合规审查(比如GDPR要求字段级加密) → 用 AES-256-GCM,这是目前最被审计机构认可的组合。
  • 如果你只需要不可逆地存储某些敏感信息(如密码、密钥),且永远不需要解密回来 → 用 bcrypt / scrypt / Argon2 等专用密码哈希,不要用HMAC。
一个容易忽略的点: 字段级加密里,算法本身不是最脆弱的,密钥管理才是。我用过各种加密模式,最后发现99%的安全事件都不是算法被破解,而是密钥被泄漏或轮换策略没做好。所以,先想好密钥怎么存、怎么换、怎么销毁,再选算法。

嗯,这一章的内容差不多就这些。你只要把对称加密、非对称加密、哈希函数这三者的定位搞清楚,它们分别承担什么角色,未来你在设计字段级加密方案时就不会乱。我记得刚开始自己做方案时也总想把复杂化,后来发现:一个加密方案好不好,不在于用了多复杂的算法,而在于每种算法用在了对的地方。