FPE 与传统加密:到底差在哪?
我在刚开始接触 FPE 的时候,脑子里第一个冒出来的问题就是:不就是加个密嘛,AES 不够用?干嘛非要用这个叫格式保留加密的东西?
后来踩了几个坑,才发现这俩其实是完全不同的路子。传统加密是“我要保密,其他不管”。FPE 是“保密是基础,但数据格式你得给我留着”。
下面我带你细细拆开看,这三个核心差异到底意味着什么。
一、数据格式保持 —— 密文还能像明文一样
先讲最直观的。你用 AES 加密一个身份证号:110101199001011234,出来的结果是啥?
嗯,像 9a2f8c...b3e4 这样的十六进制串。长度变长、字母数字混一块,而且符号都可能带出来。放到数据库里,原来存身份证号的字段完全用不了——类型得改,校验规则得重写,下游系统根本认不出来。
但用 FPE 加密同一个身份证号呢?结果可能长这样:556729200305042987。
你看,还是 18 位纯数字,一眼看去就是个“合法的”身份证格式。就像给它换了个身份,但外形完全没变。
核心区别:传统加密输出的密文不关心字符集合和格式;FPE 输出的密文严格限制在与明文相同的格式域内。
| 场景 | 明文 | AES 密文(示例) | FPE 密文(示例) |
|---|---|---|---|
| 身份证号 | 110101199001011234 | 3f7a2b9c...e4f1 | 556729200305042987 |
| 信用卡卡号 | 6222021234567890 | 8d1e2f...ab90 | 6222009876543211 |
| 手机号 | 13912345678 | c4b5a6...7d8e | 13876543210 |
一般这种需求在金融和医疗行业最常见。我曾经帮一家银行改造信用卡号脱敏,他们原来的方案是用 AES 加密后,转存到一个新字段,业务查询时还得等着解密。后来换成 FPE,直接在原字段上替换密文——查询、索引、报表,全都不需要动。
小提示:格式保持是 FPE 的核心卖点,但前提是你得明确“格式”的定义。比如“18位数字”和“带有校验位的18位数字”是两种格式。定义错了,密文可能被下游业务系统当成无效数据。
二、长度保持 —— 字段永远不溢出
很多传统加密算法会带来长度膨胀。比如 AES-128 对 16 字节数据加密,输出还是 16 字节。可如果你的数据不是 16 字节整数倍,就得做填充。填完之后密文长度就变长了。
更头疼的是,有时候你的明文只有 8 位数字(比如银行卡 PIN),AES 加密后变成 16 字节乱码,你是截断还是扩展?哪种都破坏原有数据结构。
FPE 就没有这个烦恼。它保证加密前后长度完全一致。10 位数字进去,10 位数字出来。3 个字母进去,3 个字母出来。不多不少。
注意:FPE 的“长度保持”是严格等长的,但它不支持任意长度。比如 FF1 算法要求明文字符串长度至少为 2(某些场景是 4),且最大长度受限于你选择的加密轮次和中间状态位数。设计时一定要检查你的数据长度落在 FPE 算法的合法范围里。
为什么这个特性这么重要?你想想看,你的数据库表里有一个 varchar(18) 字段存身份证号。如果换成传统加密,长度变成 32 甚至 44,那你就要改表结构、改存储过程、改 ORM 映射。万一耦合了几十个微服务……简直是灾难。
三、加解密一致性 —— 你用的密钥和算法必须对
传统加密和 FPE 在加解密上的逻辑其实一样:用同一个密钥,用同一个算法,就能互相转换。这点上 FPE 并没有搞特殊。
但有个坑特别容易踩:FPE 的算法参数设计比 AES 多了个 tweak(调整值)。
// 传统 AES 加密
cipher_text = AES_encrypt(key, plain_text)
// FPE 加密(FF1算法)
cipher_text = FF1_encrypt(key, plain_text, tweak, radix)
这个 tweak 可以理解成“额外的盐”,用来增加算法的多样性。比如你用同一个密钥加密“张三”和“李四”的身份证号,如果没有 tweak,密文可能会呈现出某种统计规律。加上不同的 tweak(比如每个人的用户 ID),就能打散这种规律。
但 tweak 必须保证加密和解密时完全一致,否则解密后会得到一堆垃圾。
加解密一致性口诀:密钥相同、算法相同、tweak 相同、格式(radix 和字符集合)相同,则加密解密才能正确。
我记得有一次陪客户做 POC,他们拿测试数据跑了一遍,加密成功,解密却报错。排查了半天——发现他们加密时用的 tweak 是 "test_tweak",解密时却传了个空字符串。这种小问题在传统加密里很少遇到,但 FPE 的多了一个参数,就容易犯错。
四、一张图看明白核心差异
我画了个对比图,把三个区别放到一起。竖着看是三种特征维度,横着看是传统加密和 FPE 的做法差异。
五、我的一点选择建议
什么时候该用 FPE?我列了几个判断条件,你对照一下自己的场景:
- 数据库字段不可改 —— 字段长度、类型、校验规则全都固定死了,只能用 FPE。
- 下游系统无法处理乱码 —— 比如日志、报表、第三方接口只接受特定格式。
- 需要可搜索加密 —— 有时候 Ciphertext 需要支持比较、排序,传统加密做不到。
- 合规要求保留格式 —— 像 PCI DSS 中信用卡号的格式保持脱敏,FPE 是标准做法。
反过来,如果只是离线存储、数据不展示给业务系统,或者你能自由改字段结构,那用 AES 就够了。毕竟 FPE 的算法开销比 AES 更大,而且 tweak 的管理确实会增加复杂度。
另一个我踩过的坑:FPE 并不适合加密短随机数或者密码哈希值,因为那些数据本身没有“格式”可言。另外,如果你的数据长度恰好是算法边界(比如 FF1 要求长度 ≥ 2),实测中要注意边界测试。
好了,总结一下:FPE 和传统加密不是谁替代谁的关系。核心区别就在格式保持、长度保持、加解密一致性这三个点上。你在选型的时候,盯着“我的数据格式必须保留吗”这个关键问题来,答案就很清楚了。