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 当前固定为整数 1n/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;无参数时必须传 {}

推荐 noncerequest_id 使用安全随机 UUID(去掉不允许的字符即可)。服务端在时间窗口内通过 Redis SET NX EX 原子消费 nonce;同一密文不能重放。重试请求必须重新生成 tsnoncerequest_id、IV 并重新加密。