Table of Contents
18.4 字节编码、AES-GCM 与信封
18.4.1 Base64URL 无填充
所有 Base64URL 字段均按 RFC 4648 URL-safe 变体处理:
编码:Base64(raw) 后将 + 替换为 -,/ 替换为 _,删除末尾全部 =
解码:将 - 还原为 +,_ 还原为 /,按 4 的倍数补 = 后严格 Base64 解码
不得使用普通 Base64 字符串直接拼 URL,也不得对 Base64URL 信封再次执行表单编码。
18.4.2 AES-GCM 参数
| 项目 | 固定要求 |
|---|---|
| 算法 | AES-256-GCM |
| Key | 严格 32 个原始字节,不截断、不补零 |
| IV | 每次加密随机生成 12 字节 |
| Tag | 16 字节 |
| 明文 | UTF-8 JSON |
| AAD | UTF-8 字节,不加密但参与认证 |
客户端应使用标准 JSON 序列化。服务端 PHP 使用 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;解密本身不依赖 JSON 键顺序,但复现固定测试向量时必须使用完全相同的原始 JSON 字节。
18.4.3 两层信封编码
先将 IV、ciphertext、tag 分别编码为 Base64URL,组成紧凑 JSON:
{"v":1,"n":"<Base64URL(12字节IV)>","c":"<Base64URL(ciphertext)>","t":"<Base64URL(16字节tag)>"}
再将上述信封 JSON 的 UTF-8 字节整体执行一次 Base64URL 无填充编码,最终得到请求中的 <Base64URL信封>。v 当前固定为整数 1,n/c/t 字段名和类型不可改变。
18.4.4 加密明文载荷
握手和业务请求统一使用:
{
"ts": 1786291200,
"nonce": "nonce_fixed_001",
"request_id": "request_fixed_001",
"data": {}
}
| 字段 | 类型 | 约束 |
|---|---|---|
ts |
integer | 当前 Unix 秒时间戳,默认允许服务端时间前后 300 秒 |
nonce |
string | 1~128 字符,仅允许 [A-Za-z0-9._~-],每个请求重新生成 |
request_id |
string | 8~128 字符,仅允许 [A-Za-z0-9._:-],每个请求重新生成 |
data |
object | 原业务 query 或 JSON body;无参数时必须传 {} |
推荐 nonce 和 request_id 使用安全随机 UUID(去掉不允许的字符即可)。服务端在时间窗口内通过 Redis SET NX EX 原子消费 nonce;同一密文不能重放。重试请求必须重新生成 ts、nonce、request_id、IV 并重新加密。