XD-OP-12 · DOC
凭证与密钥管理(生成 / 轮换 / 停用 / 应急吊销)
私钥只在自己的服务器生成与保存;`kid` 在上传公钥时由平台派生。本篇给出生成、轮换、停用与应急吊销的完整顺序。
小豆支付开放平台是面向已有自有系统的商家的支付接入服务:通过服务器对服务器的接口,把支付宝与微信收款接进商家自己的系统,并以签名事件通知回传支付状态;资金由持牌支付公司收付结算。个人也能申请:主体类型选「个人」,身份证 + 本人银行卡即可完成商户进件。央行持牌机构清算,安全可靠的个人和企业支付接口。
商家最关心的问题是「怎么换钥匙不中断收款」。本篇按实际操作顺序写清楚,包括切换瞬间的真实行为。
1. 你手上的凭证有哪些
| 凭证 | 用途 | 存放位置 |
|---|---|---|
appId | 应用标识(随六头发送) | 你的服务端配置 |
kid(密钥标识) | 标明本次签名用的是哪把钥匙 | 你的服务端配置 |
| RSA2 密钥对 | 请求签名/验签 | 公钥在门户、私钥只在你本地 |
| IP 白名单 + 来源 IP 校验开关 | 请求来源判定(校验可自助开启/关闭) | 门户维护 |
notifyUrl | 事件投递地址 | 应用配置 |
returnUrl | 收银完成后的回跳地址(下单时冻结) | 下单请求 |
1.1 kid 长什么样、从哪来(集成方必读)
- 格式:
mkid_+ 16 位小写十六进制(共 21 字符),例如mkid_5f3c1a7b52e84d06。 - 来源:
kid不是平台预先发放的——在你上传公钥成功的那一刻,平台按公钥指纹派生并回显(第 2 节第 5 步)。没有上传过公钥的应用,任何地方都不存在它的kid;对接面板里留一个「密钥标识」输入框、等你去"找"一个填,本身就是找错了方向:先生成密钥对并在门户上传公钥,kid随即出现在门户「应用凭证」页。 - 本地派生(可自动计算,无需向平台索取):
kid是公钥的确定性函数,工具链可以本地算出同一个值——
- 从你的密钥对导出公钥 SPKI PEM(
-----BEGIN PUBLIC KEY-----头,RSA 2048/4096); - 规范化:CRLF→LF、去掉首尾空白(末尾不留换行);
kid = "mkid_" + SHA-256(第 2 步文本的 UTF-8 字节) 的前 16 位小写十六进制。
只要上传到门户的 PEM 与本地计算用的是同一份文本(即「导出 → 规范化 → 上传」同一路径产出),本地算出的 kid 与平台回显必然一致。
- 两个
kid别混:门户「应用凭证」页同时展示两个—— - 「商户公钥 kid」:你自己的,签名请求头
X-XD-Key-Id填的就是它; - 「平台公钥 kid」:平台的签名密钥标识(用于你核验平台响应/事件签名),不要填进请求头,也不要填进对接配置的「密钥标识」项。
2. 生成密钥对
- 在门户「应用凭证」页发起密钥生成。密钥对在你的浏览器里本地生成,私钥从生成到保存都不经过平台。
- 私钥保存到你自己的受控存储(密钥管理服务或受权限保护的凭证文件),权限最小化。
- 上传公钥时,平台会要求一次持有证明:用新私钥对一段确定性挑战串(与你的应用标识与公钥指纹绑定)签名。这证明你确实持有对应的私钥,而不是只复制了一个公钥。挑战串由门户当场给出、直接复制使用,不要自行拼装(同一应用 + 同一公钥永远得到同一串,所以「先算好再签」是可复现的)。
- 提交后需通过操作二次验证(发到你绑定手机的操作码)——这是为了防止会话被盗后被人悄悄换钥匙。
- 成功后平台回显公钥指纹与
kid;请把kid记到你的配置里。
挑战串是确定性的:同样的应用与公钥指纹永远得到同一段挑战串。所以「先算好再签」是可复现的——这也是你本地自检的办法。
3. 轮换密钥(换钥匙)
切换语义(必须准确理解,直接决定你会不会掉单):
- 轮换是原子的:提交成功后,新
kid立即生效,你的请求必须立刻改用新私钥签名。 - 默认态下旧密钥在同一瞬间失效:平台只接受当前生效的
kid与公钥。历史kid在门户「密钥历史」里保留记录,用于审计与追溯,默认不能继续用于签名。
⚠️ 门户的历史条目只显示「已轮换」这一事实,不代表宽限窗已开启——宽限窗只能由平台运营开启,且门户目前不展示窗口状态与到期时间(如需确认,请与对接人核对)。看到历史条目就去删旧配置的前提是「没开宽限窗」,不要反过来假设。
- 因此轮换会有一个从平台切换完成到你服务端改配置之间的短窗口,期间用旧私钥签名的请求会得到
401。 - 可选的过渡宽限窗:如果这个短窗口对你不可接受,可在轮换之前联系平台运营申请开启「旧密钥验签宽限窗」——
- 窗口内,你已轮换下线的旧
kid仍可通过签名验证;窗口到期自动失效,旧密钥立即恢复拒绝; - 宽限窗只允许验签:它不恢复任何管理/配置权限,也不改变应用状态;
- 时长由平台按固定上限核定(不超过 72 小时),平台可随时立即关闭;窗口到期或关闭后旧
kid立即恢复拒绝,且不会再有任何提示; - 密钥疑似泄露时不适用:那种情况下先轮换止血,不要用宽限窗延长旧钥匙的寿命。
推荐操作顺序(默认态:把窗口压到最小):
若已与平台约定开启宽限窗,第 1 步改为「先确认宽限窗已开启、记下到期时间」,第 4 步的
401重试可以放宽;其余不变。
- 选低峰期,准备好一键切换配置(新私钥 + 新
kid)。 - 在门户完成轮换(生成 → 持有证明 → 二次验证 → 提交)。
- 提交成功后立即切换你服务端的配置并发布(默认态下旧私钥从提交那一刻起就签不通了,切换越慢掉单越多)。
- 切换期间对
401做有限次重试(不要无限重试);确认新配置生效后,观察下单成功率与签名相关告警恢复。 - 确认无异常后,销毁本地旧私钥(避免误用)。
- 把本次轮换的时间、
kid、执行人、结果记进你的变更记录(后续对账时会用到)。
先在预发环境演练一遍上面的顺序(用同一套流程、同一份配置项),再对生产执行。
4. 停用与吊销
| 动作 | 谁触发 | 效果 |
|---|---|---|
| 停用应用(暂停) | 平台侧(含商家申请) | 商家 API 全部不可用:状态非 ACTIVE 时密钥解析在鉴权阶段就中性失败,所以你拿到的是 401 而不是 403,存量订单的查询/关单/退款同样打不通,善后走平台人工通道 |
| 吊销应用 | 平台侧(双人复核) | 永久终态:表现同上(401),退款仅保留平台人工通道 |
| 冻结(风控) | 平台侧风控 | 新增下单/支付尝试被拒(403);存量查询/关单/退款不受影响 |
| 移除 IP 白名单条目 | 门户 | 对应出口的请求立即 403 |
| 关闭来源 IP 校验 | 门户(自助,短信二次验证) | 所有来源立即不再被校验;白名单条目原样保留,随时可一键重新开启。平台边缘的来源判定不受本开关影响 |
停用/吊销期间没有"只读自留地":这两种状态下应用不再
ACTIVE,鉴权阶段就中性拒绝(不区分应用不存在与状态异常,是防枚举的有意设计),因此存量订单也查不到。需要善后时按 错误码与排障 联系平台走人工通道。吊销后不再产生任何事件:该应用的事件不会落库,事后也无法补发。所以吊销前先把在途订单的终态用查单补齐。
5. 私钥丢失或怀疑泄露(应急)
按这个顺序做,别先到处找原因:
- 立即在门户轮换密钥(见第 3 节)——不需要停机、不需要等平台人工介入。
- 检查是否有人用旧密钥发起过请求:看你自己的调用日志与平台的
401/403告警;有异常调用请一并反馈。 - 若怀疑
appId也已被滥用(例如出现在不该出现的地方),走官网反馈渠道申请停用应用并说明情况。 - 轮换完成后,销毁本地旧私钥与任何携带它的临时文件/镜像层/构建缓存。
- 顺手确认来源 IP 校验处于开启(门户「IP 白名单」页)——它在关闭状态下不会拦住任何人,而泄露场景下它是密钥之外的唯一一道门槛。
6. 多环境与多应用纪律
- 一应用一套密钥;不要用同一套密钥跨应用或跨环境(测试/生产必须分开)。
- 不要复制已有应用的密钥去创建新应用——应当为新应用重新生成。
- 密钥配置项不进代码仓库:用运行时注入或密钥服务;本地开发也不要用生产密钥。
7. 常见误区
- ❌「轮换后新旧钥匙能默认同时用一段时间」——默认不能,切换是原子的,旧密钥立即失效;唯一例外是平台事先显式开启的限时宽限窗(见第 3 节),它不是默认行为,也不由商家自助开启。
- ❌ 在轮换前就把服务端切到新私钥——此时平台还没切,你会一直
401(顺序是:平台切完 → 你立刻切)。 - ❌ 把私钥放进前端或移动端用于「让客户端直连」——私钥永远只在服务端。
- ❌ 用同一个
kid传不同的私钥(例如两台服务器各生成一套)——只有与门户登记公钥匹配的那一份能通过验签。 - ❌ 密钥泄露后先排查后轮换——先轮换(止血),再排查。
- ❌ 到处找不到「以
kid_开头的密钥标识」——真实格式是mkid_…(21 字符),且它在上传公钥那一刻才出现:没上传过公钥的应用本来就没有kid。顺序永远是「生成密钥对 → 门户上传公钥 → 复制回显的kid(或按第 1.1 节本地派生)」。
open-platform/docs/12-credential-key-management.md · 最后更新 2026-10-04
下载本文 .md
在 GitHub 查看