鉴权与签名

UTMOS 开放平台的所有 API 都要求 HMAC-SHA256 请求签名。本页说明你需要哪些凭据、签名怎么计算、常见错误怎么排查。

凭据

平台为每个接入方分配:
  • API ID(公开标识)
  • API Key(私密 secret,签名时使用)
如何获取:当前阶段通过运营侧申请。提供你的接入方名称、调用场景与所需 scope。
API Key 必须严格保密,泄漏后请立即联系平台轮换。

与 Web Console 登录的关系

开放平台 API Key 只用于服务端集成签名,不能放入浏览器、移动端本地存储或 Web Console 页面状态。Web Console 使用独立的人类用户登录体系,登录后由 平台设置 httpOnly session cookie,并使用 CSRF token 保护浏览器发起的变更 请求。 Console 用户可以在权限和审批策略允许时创建、轮换或停用开放平台 API credential。新 secret 只会一次性返回给操作者保存,平台不会在 Console 后续页面、浏览器可读 cookie、localStorage 或 sessionStorage 中保存 API secret。 两个身份体系不能互换:
场景使用身份
服务端调用 /api/v1/open/*/api/v1/platform/*X-Api-Id + HMAC signature
浏览器访问 /console/api/v1/console/*Console user session cookie + CSRF
创建或轮换 Open Platform API KeyConsole 审批流或平台管理 HMAC API
在浏览器中执行设备命令不允许直接使用 API Key;应由后端集成服务代签

必填请求头

Header必填说明
X-Api-Id应用标识
X-Api-TimestampUnix 秒时间戳(字符串)
X-Api-Nonce重放保护 nonce;同一 API ID 在重放窗口内不能重复
X-Api-SignatureHMAC-SHA256 签名(小写 hex,长度固定 64 字符)
X-Request-Id请求追踪 ID;未传时平台生成

签名计算

Canonical String

固定 8 行,用 \n 连接:
  1. UTMOS-HMAC-SHA256
  2. METHOD(大写)
  3. PATH(仅 path,不含 query 与 host)
  4. 按 RFC 3986 编码并按 key 字典序排序后的 query string(无 query 时为空字符串)
  5. SHA256_HEX(BODY)(无 body 对空字节哈希,结果为 e3b0c44...
  6. X-Api-Id 原始字符串
  7. X-Api-Timestamp 原始字符串
  8. X-Api-Nonce 原始字符串
示例(POST /api/v1/open/downlink/commands):
UTMOS-HMAC-SHA256
POST
/api/v1/open/downlink/commands

<sha256_of_request_body>
client_abc
1745308800
nonce-001

计算签名

用 API Key 作为 HMAC-SHA256 的 secret 对 canonical string 做哈希,输出小写 hex。
BODY='{"vendor":"dji","device_id":"drone-001","command_type":"camera_mode_switch","payload":{"payload_index":"52-0-0","camera_mode":0},"idempotency_key":"req-001","timeout_seconds":30}'
TS="$(date +%s)"
NONCE="nonce-$TS"
BODY_HASH="$(printf '%s' "$BODY" | shasum -a 256 | awk '{print $1}')"
CANONICAL="UTMOS-HMAC-SHA256
POST
/api/v1/open/downlink/commands

$BODY_HASH
$API_ID
$TS
$NONCE"
SIGNATURE="$(printf '%s' "$CANONICAL" | openssl dgst -sha256 -hmac "$API_KEY" -binary | xxd -p -c 256)"

curl -X POST "https://dev.utmos.dev/api/v1/open/downlink/commands" \
  -H "Content-Type: application/json" \
  -H "X-Api-Id: $API_ID" \
  -H "X-Api-Timestamp: $TS" \
  -H "X-Api-Nonce: $NONCE" \
  -H "X-Api-Signature: $SIGNATURE" \
  -d "$BODY"

时间戳偏差窗口

X-Api-Timestamp 与平台服务器时间允许的偏差由凭据配置决定,默认 ±5 分钟。超出会返回 TIMESTAMP_EXPIRED 务必同步本地时间(NTP)。如果你的系统跨时区,注意提交的是 Unix 秒,与时区无关。

Nonce 重放保护

X-Api-Nonce 必须在当前 API ID 的重放窗口内唯一。平台会记录 nonce 使用事实;重复使用会返回 NONCE_REPLAYED。客户端应为每次请求生成新的 nonce,即使请求体和幂等键相同。

密钥存储

平台不保存明文 API Key。凭据表保存租户绑定、状态、过期时间、轮换时间,以及受 AES-256-GCM 保护的签名密钥密文。密文包含 key ID、nonce 和 ciphertext;解密只发生在签名校验边界。Casbin 只保存授权策略,不保存密钥材料。

API Key 轮换

平台通过 credential 更新接口轮换 API Key。轮换成功后响应只返回一次新 secret,平台不再返回旧 secret。
  1. 调用 credential update/rotate 接口并设置 rotate_secret=true
  2. 保存响应中的一次性 secret
  3. 使用新 secret 重新计算签名
  4. 旧 secret 在轮换后立即失效

排查

现象可能原因
UNAUTHORIZEDX-Api-Id / X-Api-Timestamp / X-Api-Nonce / X-Api-Signature 任一
SIGNATURE_INVALIDAPI Key 错;canonical 串拼错(注意 query 排序、空 body 也要哈希);body 被代理改写(如压缩、改行尾)
TIMESTAMP_EXPIRED本地时间漂移、用了毫秒时间戳、用了 ISO 字符串
NONCE_REPLAYEDnonce 在重放窗口内重复使用
FORBIDDENAPI Key 没有该接口要求的租户域权限(见 Scope 目录
更多错误码见 错误模型