FPE 算法概述
咱们先聊一个最基础的问题:什么是保留格式加密?
说白了,就是加密之后,密文跟原文长得一模一样——长度相同、字符集相同、格式也相同。你想想看,一张信用卡号是 16 位纯数字,加密后还是 16 位纯数字。这就叫 Format-Preserving Encryption,简称 FPE。
我刚开始接触这个概念时,第一反应是:为啥要这么干?直接用 AES 加密成一段乱码不就完了吗?后来在项目中吃了亏才明白……嗯,现实世界里的数据库、接口、协议,往往对数据格式有硬性要求。你改不了格式,就只能用 FPE。
FPE 与 AES/GCM 的传统加密对比
传统加密(比如 AES-GCM)会把任意二进制数据变成看起来随机的字节串。而 FPE 把输入限定在某个特定格式里,输出也保持这个格式。下面这张表可以帮你快速看清区别:
| 对比维度 | AES / GCM | FPE |
|---|---|---|
| 输出格式 | 二进制 / Base64 字符串 | 与输入完全一致 |
| 长度变化 | 通常会扩展(IV + 标签) | 长度完全不变 |
| 字符集限制 | 无限制 | 必须指定(如数字、字母集) |
| 可搜索性 | 不支持,密文无序 | 支持保持排序属性(部分方案) |
| 密钥管理 | 标准 | 同 AES 标准,通常基于 AES 构造 |
我个人习惯在需要 保留字段格式 的场景里优先考虑 FPE。比如金融系统的卡号加密,如果数据库字段定义的是数字字符串,你用 AES 加密后插不回去——长度变了,类型也变了,数据库就得改表结构,牵扯一堆下游系统。我用 FPE 就省了这些麻烦。
典型应用场景
实际工作中经常遇到的应用有三个:
- 信用卡号(PAN):卡号一般是 16 位数字,前 6 位是发卡行标识(BIN)。加密后需要保留 BIN,方便银行做路由和风控。FPE 可以做到只加密后 10 位,前 6 位明文保留。我在一个支付网关项目里就是这么干的。
- 社保号(SSN / 身份证号):通常是固定长度的数字或数字+字母。数据库用来做关联查询,如果密文格式变了,索引就废了。FPE 能保持格式,索引照样生效。
- 手机号:11 位数字,经常作为用户唯一标识。加密后还是 11 位数字,前端展示、短信发送都能无缝兼容。记得有一次,我把手机号直接 AES 加密,结果接口要求非空且符合手机号正则校验,整个调用链都断了……后来改用 FPE,一行代码都没改前端。
注意! 保留格式加密不是万能的。它牺牲了一部分安全强度(密文空间变小)来换取格式兼容。如果你的场景不依赖格式,直接用 AES-GCM 更安全。
经验之谈: FPE 通常用于脱敏或等保合规场景。我曾经帮一个银行做信用卡数据迁移,他们要求测试环境保留生产数据的格式但内容脱敏。FPE 一把梭,既满足合规,又不用改应用代码。
FPE 的核心逻辑
它的工作原理其实不复杂——把原始明文映射到与它同长度的密文上,而且这个映射是随机的、可逆的。常见的标准有 NIST SP 800-38G 推荐的 FF1 和 FF3 算法。它们是基于 AES 构造的 Feistel 网络,输入是一个字符串(比如数字串),输出还是一样长的字符串。
下面这张图帮你理清 FPE 在整个数据保护体系中的位置:
一句话总结: FPE = 加密后数据“看起来”和原文一模一样,但内容是保密的。它能无缝插进现存系统,不打破任何格式约束。