# 格式保留加密 (FPE)：FF1、FF3-1 与令牌化

> 了解格式保留加密的工作原理——FF1 和 FF3-1 算法、信用卡令牌化、PCI DSS 合规，以及何时使用 FPE 而非传统加密。

Canonical: https://hsmkit.com/zh/guides/fpe-format-preserving-encryption/

Last updated: 2026-05-21

格式保留加密（FPE）在加密数据的同时保持相同格式——信用卡号仍然是 16 位数字，社会安全号码仍然是 XXX-XX-XXXX 格式。当您需要加密数据但应用程序、数据库或支付处理商要求特定格式时，这至关重要。

## 什么是格式保留加密？

传统加密算法如 AES 产生的密文是二进制格式，与原始明文完全不同。如果您加密信用卡号 `4111111111111111`，会得到类似 `a3f2b8c1d4e5...` 的内容——长度和格式完全不同的十六进制字符串。

FPE 解决了这个问题。它接受特定格式的输入，并产生**相同格式**的输出：

```
输入:  4111111111111111  （16 位数字）
输出: 7392815640293847  （仍然是 16 位数字）
```

加密后的值是一个有效的信用卡号，可以通过 Luhn 校验，适合存储在数据库列中，并能被支付系统处理——同时具有密码学安全性。

## FF1 和 FF3-1 算法

NIST 在 **SP 800-38G** 中标准化了两种 FPE 算法：

### FF1（基于 Feistel 的格式保留加密）

- 使用 10 轮非平衡 Feistel 网络
- 支持**任意进制**（基数 2 到 2^16）
- 最小输入长度：**6 个字符**（使用默认 tweak）
- 比 FF3-1 更灵活但略慢
- 推荐用于大多数场景

### FF3-1（FF3 修订版）

- 使用 8 轮平衡 Feistel 网络
- 同样支持任意进制
- 最小输入长度：**8 个字符**（使用默认 tweak）
- 更快但长度限制更严格
- 修订版解决了 2017 年对原始 FF3 的攻击问题

两种算法都需要：
- **对称密钥**（AES 密钥：128、192 或 256 位）
- **Tweak**（额外输入，使相同明文产生不同密文）

## 理解进制（Radix）

进制是字符集使用的数制基数：

| 进制 | 字符集 | 示例用例 |
|------|--------|---------|
| 2 | 0, 1 | 二进制字符串 |
| 10 | 0-9 | 信用卡号、社会安全号码 |
| 26 | A-Z | 类名称的令牌 |
| 36 | 0-9, A-Z | 字母数字标识符 |
| 62 | 0-9, A-Z, a-z | 区分大小写的 ID |

对于信用卡号，进制为 **10**（数字 0-9）。对于字母数字数据，进制为 **36** 或 **62**。

## FPE 工作原理

FPE 使用 Feistel 网络结构：

```
1. 将输入分为左半部分（L）和右半部分（R）
2. 每轮：
   a. 使用轮密钥 + tweak 计算轮函数 F
   b. 将结果与另一半进行 XOR 运算
   c. 交换两半
3. 重新组合产生相同格式的输出
```

轮密钥从主密钥派生，tweak 确保相同明文在不同上下文中加密为不同密文（例如，不同卡号可以使用不同的 tweak）。

## Tweak

Tweak 是 FPE 的重要组成部分：

- 它的作用类似于**初始化向量**（IV），但更灵活
- 相同密钥 + 不同 tweak = 不同密文
- 常见 tweak 来源：数据库记录 ID、交易 ID 或空字符串
- 不要求保密——可以与密文一起存储

```
Encrypt("4111111111111111", key, tweak="order123") → "7392815640293847"
Encrypt("4111111111111111", key, tweak="order456") → "2958471036582914"
```

## 令牌化 vs 加密

FPE 经常与令牌化混淆，但它们是不同的：

| 方面 | FPE | 令牌化 |
|------|-----|--------|
| 可逆性 | 是（需要密钥） | 取决于实现 |
| 密码学性质 | 是 | 否（查找表） |
| 需要密钥 | 是 | 否（需要数据库） |
| 格式保留 | 是 | 是 |
| 标准化 | NIST SP 800-38G | 无标准 |

许多支付系统使用**混合方法**：FPE 用于实际加密，令牌库用于额外的安全层。

## PCI DSS 合规

FPE 在 PCI DSS（支付卡行业数据安全标准）中发挥重要作用：

### 为什么 FPE 有助于 PCI DSS

- **要求 3.4**：在存储任何地方使 PAN 不可读——FPE 满足此要求
- **缩小范围**：如果加密足够强，FPE 加密的数据可以排除在 PCI 范围之外
- **格式兼容性**：现有系统无需修改架构即可处理加密的 PAN
- **Luhn 有效输出**：使用进制 10 的 FF1 可以产生 Luhn 有效的加密 PAN

### PCI DSS 要求

- 使用强密码学（至少 AES-128）
- 使用 HSM 或等效密钥管理系统保护密钥
- 记录密钥管理程序
- 定期轮换密钥

## 实际应用场景

### 信用卡处理

```
原始 PAN:  4111111111111111
加密 PAN: 7392815640293847  （有效的 16 位数字）
数据库:    相同的 VARCHAR(16) 列，无需架构更改
```

### 医疗数据（HIPAA）

加密患者 ID、社会安全号码和医疗记录号码，同时保持数据库兼容性和格式验证规则。

### 遗留系统集成

当您无法修改数据库架构或应用程序逻辑来处理二进制密文时，FPE 让您透明地加密数据。

### 令牌化替代方案

用密码学 FPE 替换脆弱的令牌库——解密不需要数据库查找，只需要密钥。

## 选择 FF1 还是 FF3-1

| 标准 | FF1 | FF3-1 |
|------|-----|-------|
| 最小长度 | 6 个字符 | 8 个字符 |
| 速度 | 较慢 | 较快 |
| 安全边际 | 较高 | 良好（修订版） |
| 建议 | 默认选择 | 速度重要且输入 ≥ 8 个字符时 |

对于大多数应用，**推荐使用 FF1**，因为它有更大的安全边际和更灵活的长度要求。

## 实现注意事项

### 密钥管理

- 使用 AES-128 或 AES-256 密钥
- 在 HSM 或安全密钥管理系统中存储密钥
- 定期轮换密钥，但需要计划重新加密

### Tweak 策略

- **每记录 tweak**：使用数据库记录 ID——提供最大差异化
- **静态 tweak**：更简单但安全差异化较少
- **空 tweak**：最简单，但相同明文产生相同密文

### 长度约束

FF1 和 FF3-1 基于进制和密钥大小有最小和最大长度要求。对于进制 10（数字）：

- FF1：最小 6 位数字
- FF3-1：最小 8 位数字
- 最大长度取决于密钥大小（大约 2^32 个块）

## 在线工具

使用 [FPE 加密工具](/fpe-encryption)：

- 使用 FF1 和 FF3-1 算法进行加密和解密
- 测试不同的进制值（10、26、36、62）
- 自定义 tweak 以适应不同安全上下文
- 验证信用卡号的 Luhn 兼容输出
- 理解格式保留加密在实践中的工作原理

所有处理都在浏览器中进行——您的密钥和数据永远不会离开您的设备。
