Files
yunhai.netcore.tool/BasePlatformDotnet/contract/sign-protocol.md
T
2026-09-02 09:38:45 +08:00

3.5 KiB
Raw Blame History

yh-platform 签名协议规范(v1.0)

权威源:Java yh-utils-core utils.crypto.SignUtil + yh-app-auth-spring-boot-starter SignCanonical。 本文档为双端(Java / C#)共同遵守的契约工件;任何变更须先改本文,再同步两侧实现。

1. 算法

统一使用 HmacSHA256,密钥与签名串均为 UTF-8 字节。

2. 双签名规则(长期并存,见方案 9.5 裁决 #5 收敛计划)

2.1 数据级签名(业务内容签名)

source      = data + "|" + timestamp
output      = HmacSHA256(source, secret) 的小写十六进制
timestamp   = 毫秒级时间戳字符串
  • 验签忽略大小写比对(equalsIgnoreCase)。
  • verifyWithExpire:先比对签名,再校验 |now - ts| <= expireMs(默认 5 分钟,双向偏差均拒绝)。

2.2 请求级签名(HTTP 请求签名,SDK 出站默认)

canonicalTarget = path + ("?" + sortedQuery   # query 为空则不带
source          = METHOD + "\n" + canonicalTarget + "\n" + timestamp
output          = HmacSHA256(source, secret) 的 Base64
  • METHOD:大写(GET/POST),大小写敏感精确比对(这是既有行为,是否收敛见方案 9.5 裁决 #3)。
  • path:只取路径,不含 host/port/context-path(保证服务发现/反代前后端天然对齐)。
  • basePath 剥离(客户端与服务端共同口径):客户端签名前从 path 剥离部署 basePath(与 UserBase 部署 context-path 一致,如 /userbase,对齐 Java AppSignInterceptor.stripBasePath);服务端验签前剥 request.getContextPath()(对齐 Java AppAuthFilter)。剥离后 canonical 口径统一为 /oapi/**。剥离规则:path 不以 basePath 开头时原样保留;剥离后为空或丢失前导斜杠时补 /(/userbase → /)。
  • sortedQuery:query 按 k=v 整串字典序排序后以 & 连接;k、v 先 URL 解码再参与排序;多值参数的值也排序。
  • 验签为精确比对(Base64 区分大小写)。
  • verifyRequestWithExpire 语义同数据级。
  • 测试向量中的 canonicalTarget 即剥离后的服务端口径(含 fullUrl/basePath 字段可复现剥离前 URL)。

2.3 密钥约束

  • secret 最小长度 16 位(MIN_SECRET_LENGTH),低于此值签名方法抛参数异常。

3. HTTP 头契约(逐字一致)

头名 值
X-App-Key 调用方在 UserBase 注册的 appKey
X-Timestamp 毫秒级时间戳字符串
X-Signature 请求级签名(Base64)

4. 防重放

  • 时间窗口:|now - X-Timestamp| > expireMs(默认 300000ms)即拒绝。
  • nonce(可选扩展):同一 (appKey, timestamp, nonce) 在窗口内只允许提交一次; Redis 键 sign:replay:{appKey}:{timestamp}:{nonce},SETNX + TTL=窗口。

5. 验签密钥解析链(服务端)

1. Redis  key = sign:app:{appKey}   → 值为 SignAppKeyPayload JSON {appKey, secret, enabled}
2. 进程内缓存(Caffeine / IMemoryCache,TTL 5min)
3. 兜底   GET {userbase}/oapi/internal/sign-key/{appKey} → 信封 data 为 SignAppKeyPayload
  • 仅当 enabled=true 且 secret 非空才可用;未知 appKey 直接拒绝。
  • 任一层异常静默降级到下一层(Redis 宕机不阻断业务)。

6. 测试向量

fixtures/sign-vectors.json:≥10 组向量(含空 body、超长参数、中文 UTF-8、query 排序、密钥长度边界、毫秒时间戳边界)。 基线由 Java SignUtil 实际运行产出;CI 中 Java 侧重算 fixture 防漂移,C# 侧逐字节对拍。