---
title: API签名与非对称验签(时间戳/Nonce/窗口)最佳实践
keywords:
- API签名
- RSA
- ECDSA
- 公钥
- 私钥
- 时间戳
- Nonce
- 重放窗口
- kid
- Base64URL
description: 使用RSA/ECDSA实现非对称API签名与验签,结合时间戳与Nonce重放窗口控制与kid密钥标识,附签名生成与服务端校验示例及参数约束。
categories:
- 文章资讯
- 技术教程
---
一、参数与风险
- 风险:伪造与重放、过期窗口过长、kid失配与密钥轮换不当。
- 基线:固定算法与头字段、严格时间戳窗口与唯一Nonce、kid映射公钥。
二、Base64URL与签名
import crypto from 'crypto'
function b64url(input: Buffer): string { return input.toString('base64').replace(/=/g,'').replace(/\+/g,'-').replace(/\//g,'_') }
function signRsaSha256(privateKeyPem: string, data: string): string {
const s = crypto.createSign('RSA-SHA256')
s.update(data)
s.end()
const sig = s.sign(privateKeyPem)
return b64url(sig)
}
function verifyRsaSha256(publicKeyPem: string, data: string, sigB64u: string): boolean {
const v = crypto.createVerify('RSA-SHA256')
v.update(data)
v.end()
const sig = Buffer.from(sigB64u.replace(/-/g,'+').replace(/_/g,'/'), 'base64')
return v.verify(publicKeyPem, sig)
}
三、头字段与数据格式
type Req = { headers: Record<string, string | undefined>; rawBody: string }
type Res = { status: (n: number) => Res; end: (b?: string) => void }
function canonicalPayload(path: string, body: string): string {
return path + '\n' + body
}
四、Nonce窗口与kid映射
class NonceStore {
keep = new Set<string>()
add(n: string) { this.keep.add(n) }
has(n: string): boolean { return this.keep.has(n) }
}
type KeyMap = Record<string, string>
五、服务端校验
function nowSec(): number { return Math.floor(Date.now()/1000) }
function verifyApiSignature(req: Req, res: Res, keys: KeyMap, windowSec: number, nonces: NonceStore): boolean {
const kid = req.headers['x-kid'] || ''
const sig = req.headers['x-signature'] || ''
const ts = Number(req.headers['x-timestamp'] || '0')
const nonce = req.headers['x-nonce'] || ''
const pub = keys[kid]
if (!pub) return false
if (!Number.isFinite(ts)) return false
if (Math.abs(nowSec() - ts) > windowSec) return false
if (nonces.has(nonce)) return false
const data = canonicalPayload(req.headers['x-path'] || '/', req.rawBody)
const ok = verifyRsaSha256(pub, `${ts}.${nonce}.${data}`, sig)
if (!ok) return false
nonces.add(nonce)
return true
}
六、验收清单
- 固定算法与头字段:
X-Kid/X-Signature/X-Timestamp/X-Nonce/X-Path;窗口≤300。 kid映射到公钥并支持轮换;nonce唯一性通过;数据采用规范化格式。- 验签失败拒绝并记录审计,包含
kid与来源路径。

发表评论 取消回复