服务端 API 签名加密
前言
本方案为 Game SVR 与 SUD OP SVR 之间的接口通信提供一套完整的鉴权体系,采用数据加密+数字签名的双重安全机制,有效防止接口数据在传输过程中被泄漏、篡改,同时具备操作行为的不可否认性。游戏开发者可在 SUD OpenPaaS 管理后台的「API 安全配置页」完成密钥、公钥的配置与管理,以此保障 Game SVR 与 SUD OP SVR 之间接口交互的安全性。
本文统一使用
gameid、_gameid和X-Sud-GameId标识当前游戏,字段值均为 SUD OpenPaaS 分配的GameID。「SUD OpenPaaS 证书」在正文中简称为「平台证书」。
一、核心概念解释
1.1 服务签名与验签
Game SVR 调用 SUD OpenPaaS API(如查询支付订单等)时,需通过自身游戏私钥生成请求签名(支持 RSASSA-PSS);SUD OP SVR 接收请求后,通过游戏开发者配置的游戏公钥完成签名验签,验证请求的真实性与数据完整性。
SUD OP SVR 向 Game SVR 发起回调时,Game SVR 需通过平台证书中的公钥完成回调验签,验证平台回调的真实性。
核心名词定义
- 游戏公钥:游戏开发者通过 SUD OpenPaaS 管理后台在线生成,保存在 API 安全配置页中,用于 SUD OP SVR 对 Game SVR 的接口调用进行验签。
- 游戏私钥:游戏开发者通过 SUD OpenPaaS 管理后台生成并下载,仅由 Game SVR 自行保管,用于生成接口请求的数字签名,需严格保障其安全性。
- 平台证书:包含 SUD OpenPaaS 公钥信息的官方证书,Game SVR 用于验证 SUD OP SVR 接口回包和回调的签名,在配置游戏公钥后可从 API 安全配置页下载。
1.2 接口内容加密
SUD OP SVR 支持对接口请求内容和响应内容采用 AES-256-GCM 算法加密,加密后接口报文以密文形式在网络中传输,大幅提升数据传输安全性。
加密与签名的执行顺序:Game SVR 需先对请求明文进行加密得到密文,再对密文生成数字签名,禁止先签名后加密。
二、密钥配置说明
游戏开发者需在 SUD OpenPaaS 管理后台完成对称密钥、非对称密钥对的在线生成与配置,并下载平台证书。
2.1 进入开发管理页
游戏开发者登录 SUD OpenPaaS 管理后台后,进入「开发管理」进行操作。
2.2 配置接口内容加密对称密钥
对称密钥用于接口请求/响应内容的加解密,采用 AES-256-GCM 算法:
- 需在「开发管理」页点击生成对称密钥,系统将生成 43 字符 Base64 字符串,解码后 32 字节;
- 配置完成后可下载密钥(仅可下载一次),需本地妥善保管。
2.3 配置游戏非对称密钥对
非对称密钥对用于接口请求的签名与验签,支持 RSA、SM2 两种算法:
- 需在「开发管理」页在线生成游戏非对称密钥对(生成后,游戏公钥将自动保存在管理后台中);
- 密钥对生成完成后,必须立即下载游戏私钥(仅可下载一次),下载后需在 Game SVR 严格做好保密措施。
2.4 获取 SUD OpenPaaS 证书
游戏密钥配置完成后,可在 API 安全配置页下载平台证书,同时可查看已配置密钥的编号(SN)与基础信息。
三、API 请求处理规范
Game SVR 调用开放接口时,必须按照以下规范处理 API 请求:先对请求明文加密生成密文,再对密文生成签名,最终将加密参数、签名信息、标准请求头一同发送至 SUD OP SVR。未加密、未签名或验签失败的请求,SUD OP SVR 会直接拒绝。
3.1 请求明文加密(AES-256-GCM)
3.1.1 加密后请求参数要求
请求参数
参数 必填 备注 iv 是 初始向量,Base64 编码后为 16 个字符(解码后为 12 字节随机数据) data 是 加密后的请求密文,Base64 编码 authtag 是 GCM 分组模式输出的认证信息,用于验证密文完整性,Base64 编码 GCM 模式额外认证数据(AAD)
需设置 额外认证数据(AAD) 对密文进行认证,统一规定 AAD 格式为:
urlpath|gameid|timestamp|sn(各字段用竖线|分隔)字段 说明 urlpath 当前请求 API 的 URL 路径,包含 URL 协议信息,不包括 URL 参数(URL Query) gameid 游戏唯一标识 GameID timestamp 加密操作的时间戳(秒级),需与请求头 X-Sud-Timestamp的值完全一致sn 本次加密使用的对称密钥编号,从 API 安全配置页获取 请求明文格式要求
待加密的请求明文需采用 JSON 格式,包含原 API 的所有参数,并强制添加三个安全字段:
参数 类型 必填 备注 <原 API 业务字段>原类型 是 原 API 要求的所有参数按原字段名平铺到 JSON 根(不嵌套),不包含 AccessToken _n string 是 防重放随机字符串(Nonce),推荐使用 16-32 字节的 Base64 随机字符串 _gameid string 是 游戏唯一标识 GameID,必须与 AAD 中的 gameid 一致 _timestamp number 是 加密操作的时间戳(秒级),必须与 AAD 中的 timestamp 一致
3.1.2 计算示例
以下示例以 AES-256-GCM 算法为例,演示一次完整的请求加密过程。
准备密钥与上下文信息
- 请求路径 (urlpath):
https://cn-cpapi.sudbiz.cloud/v1/pay/order/query - 对称密钥编号 (sn):
key_v1 - 对称密钥内容 (key):
otUpngOjU+nVQaWJIC3D/yMLV17RKaP6t4Ot9tbnzLY= - 当前时间戳 (timestamp):
1710500000 - 游戏ID (gameid):
game_10010
- 请求路径 (urlpath):
准备原始业务数据
json{ "env": 0, "out_trade_no": "order_202607220001" }生成 IV 并拼接 AAD
生成 12 字节随机数据,并进行 Base64 编码作为
iv:textbase64_encode(iv) = fmW/zNxXlytUZBgj按照
urlpath|gameid|timestamp|sn格式严格拼接 AAD:textaad = https://cn-cpapi.sudbiz.cloud/v1/pay/order/query|game_10010|1710500000|key_v1组装最终待加密明文 (补齐安全字段)
json{ "_gameid": "game_10010", "_n": "o89QaPVsRu1yppIZzvSZc4", "_timestamp": 1710500000, "env": 0, "out_trade_no": "order_202607220001" }压缩 JSON 并执行加密
将上述 JSON 压缩为无多余空格和换行的字符串(作为实际的
plaintext):json{"_gameid":"game_10010","_n":"o89QaPVsRu1yppIZzvSZc4","_timestamp":1710500000,"env":0,"out_trade_no":"order_202607220001"}注意:AES-GCM 加密的是字节流,JSON 字段顺序不同会得到完全不同的密文。为便于跨语言对照测试,本文示例统一按字段名字典序升序排列;生产实现不强制要求字典序,但必须保证用于加密的 plaintext 字节与最终发送的 HTTP Body 字节完全一致(即:先序列化得到字符串,再用同一字符串去加密并作为 Body 发送)。
传入
plaintext、key、iv、aad执行 AES-256-GCM 加密,分别提取密文和认证标签并 Base64 编码:textbase64_encode(enc_data) = OIDVdrPtSPF/0e2CTXCV2vVNPbVJdJlP2WaTMQnoYLh5iCrrSNfQFh25EnStDMf0hLlVNBCZQtf9NaV0m4aRA4AAYIO7oR/Ge+4yY4EmZp5EVPB42xjScgMx5X3D4VdLCfynXIUKUtZHZvk1zmLVE3RauzJgiM1BB1C // 示例已截断,实际长度取决于明文长度 base64_encode(authtag) = 5qeM/2vZv+6KtScN94IpMg==最终作为 HTTP Body 的请求密文
json{ "iv": "fmW/zNxXlytUZBgj", "data": "OIDVdrPtSPF/0e2CTXCV2vVNPbVJdJlP2WaTMQnoYLh5iCrrSNfQFh25EnStDMf0hLlVNBCZQtf9NaV0m4aRA4AAYIO7oR/Ge+4yY4EmZp5EVPB42xjScgMx5X3D4VdLCfynXIUKUtZHZvk1zmLVE3RauzJgiM1BB1C", "authtag": "5qeM/2vZv+6KtScN94IpMg==" }
3.2 加密请求的数字签名
3.2.1 参数说明
请求参数
在 API 请求中,Game SVR 需通过游戏私钥对请求内容生成签名,并通过 HTTP Header 传递给 SUD OP SVR。
Header名 必填 描述 X-Sud-GameId 是 Game SVR 填写的 GameID X-Sud-Timestamp 是 签名时的时间戳(秒级),必须与加密时的一致 X-Sud-Signature 是 签名串(Base64 编码) 待签名串(String-to-Sign)拼接规范
为了彻底消除验签时的换行符歧义,待签名串严格遵循以下拼接公式:
String-to-Sign = urlpath + "\n" + gameid + "\n" + timestamp + "\n" + data参与签名的参数 说明 urlpath 当前请求API的URL,不包括URL参数(URL Query),需要带HTTP协议头。 gameid 当前调用的 GameID(即 X-Sud-GameId的值)。timestamp 签名时的时间戳(即 X-Sud-Timestamp的值)。data HTTP 请求的 Body 原始字符串,即 3.1.2 最终生成的 JSON 字符串(若请求无 Body,此项为空)。 注意:SM2withSM3 签名算法需要用到的 ID 默认为游戏非对称密钥编号(SN)。
3.2.2 计算示例
接续 3.1.2 的加密结果,我们对支付订单查询接口的请求数据进行签名。
私钥信息 (RSASSA-PSS, SHA-256 / MGF1-SHA-256 / salt=32)
注:PSS 签名中包含随机因子 (salt length=32),因此即使原文相同,每次计算出的签名结果也会不同,这是安全且正常的。
json{ "Sn": "rsa_key_v1", "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA3F0Q0m015/CF...\n-----END RSA PRIVATE KEY-----" }拼接待签名串 M
按照规范,将各部分用
\n连接。texthttps://cn-cpapi.sudbiz.cloud/v1/pay/order/query game_10010 1710500000 {"iv":"fmW/zNxXlytUZBgj","data":"OIDVdrPtSPF/0e2CTXCV2vVNPbVJdJlP2WaTMQnoYLh5iCrrSNfQFh25EnStDMf0hLlVNBCZQtf9NaV0m4aRA4AAYIO7oR/Ge+4yY4EmZp5EVPB42xjScgMx5X3D4VdLCfynXIUKUtZHZvk1zmLVE3RauzJgiM1BB1C","authtag":"5qeM/2vZv+6KtScN94IpMg=="}计算签名并编码
对上述字符串 M 使用游戏私钥进行签名:
textbase64_encode(Signature) = wcSSWHZunjz9VKl9q+If9deiyECXDAELFAJNZ4+5T+NhFr8zfhkwdQtlgQ7nM...最终发送的完整 HTTP 请求
httpPOST /v1/pay/order/query HTTP/1.1 Host: cn-cpapi.sudbiz.cloud Content-Type: application/json; charset=utf-8 X-Sud-GameId: game_10010 X-Sud-Timestamp: 1710500000 X-Sud-Signature: wcSSWHZunjz9VKl9q+If9deiyECXDAELFAJNZ4+5T+NhFr8zfhkwdQtlgQ7nM... {"iv":"fmW/zNxXlytUZBgj","data":"OIDVdrPtSPF/0e2CTXCV2vVNPbVJdJlP2WaTMQnoYLh5iCrrSNfQFh25EnStDMf0hLlVNBCZQtf9NaV0m4aRA4AAYIO7oR/Ge+4yY4EmZp5EVPB42xjScgMx5X3D4VdLCfynXIUKUtZHZvk1zmLVE3RauzJgiM1BB1C","authtag":"5qeM/2vZv+6KtScN94IpMg=="}
四、API 响应处理规范
SUD OP SVR 返回的响应数据均为密文+签名形式,Game SVR 需按照先验签、后解密的顺序处理响应数据。
4.1 响应数据验签
4.1.1 验签相关响应头要求
| 响应头名 | 必填 | 备注 |
|---|---|---|
| X-Sud-GameId | 是 | Game SVR 的 GameID,与请求头的 X-Sud-GameId 一致 |
| X-Sud-Timestamp | 是 | SUD OP SVR 生成响应的时间戳(秒级) |
| X-Sud-Serial | 是 | SUD OpenPaaS 证书编号 |
| X-Sud-Signature | 是 | SUD OP SVR 的响应签名结果,Base64 编码 |
| X-Sud-Serial-Deprecated | 否 | 即将失效的证书编号(证书轮换期返回) |
| X-Sud-Signature-Deprecated | 否 | 即将失效的 SUD OpenPaaS 证书签名数据,Base64 编码(证书轮换期返回) |
4.1.2 待验签串拼接规则
为保证签名框架的一致性,SUD OP SVR 响应的待验签串与请求签名规则完全相同: String-to-Verify = urlpath + "\n" + gameid + "\n" + timestamp + "\n" + data
| 参与验签的参数 | 说明 |
|---|---|
| urlpath | 当前请求API的URL,不包括URL参数(URL Query),需要带HTTP协议头。 |
| gameid | 响应头 X-Sud-GameId 的值。 |
| timestamp | 响应头 X-Sud-Timestamp 的值。 |
| data | HTTP 响应的 Body 原始密文字符串。 |
4.2 响应密文解密
解密使用的对称密钥、密钥编号与 Game SVR 请求时一致。AAD 拼接格式为 urlpath|gameid|timestamp|sn。解密后同样需要强校验明文中的 _gameid 与 _timestamp。
五、常见错误码及解决方案
| 错误码 | 错误码取值 | 解决方案 |
|---|---|---|
| 64101 | API_Missing_Sud_Timestamp | 请求头缺失:缺少 X-Sud-Timestamp 字段。请补充秒级时间戳,并确保其与加密报文内部的 _timestamp 完全一致。 |
| 64102 | API_Missing_Sud_Signature | 请求头缺失:缺少 X-Sud-Signature 字段。请检查私钥签名逻辑,并将生成的签名进行 Base64 编码后放入该 Header。 |
| 64103 | API_Missing_Sud_GameId | 请求头缺失:缺少 X-Sud-GameId 字段。请检查 HTTP Header 是否正确注入了游戏唯一标识 GameID。 |
| 64104 | API_Invalid_Signature | 签名验证失败(高频):1. 检查游戏私钥是否与 SUD OpenPaaS 管理后台保存的游戏公钥成对(核对密钥编号 SN);2. 检查待签名串拼接是否严格遵循 urlpath\ngameid\ntimestamp\ndata 格式,分隔符必须为 LF (\n),不可用 CRLF;3. 检查 urlpath 是否带 HTTP 协议头、且不包含 URL Query;4. 检查 data 是否为 HTTP Body 的原始字符串(字节级一致,不允许重新序列化 JSON);5. 检查签名结果是否进行了 Base64 编码后再放入 X-Sud-Signature;6. RSA-PSS 算法需确认 MGF1 摘要、salt length=32 与平台一致;7. SM2withSM3 需确认签名 ID 使用了非对称密钥编号(SN)。 |
| 64105 | API_Invalid_Encrypt | 加解密失败:1. 检查 AES-256-GCM 的 AAD 拼接格式(urlpath|gameid|timestamp|sn)是否正确。2. 确认传入的 Cipher 密文是否完整。在部分语言底层库中,需要将加密输出的 CipherText 与 AuthTag 拼接后传递;若分离输出,需确保 JSON 报文中的 authtag 字段无误。 |
| 64106 | API_Invalid_Sud_GameId | GameID 无效或未生效:平台无法识别 X-Sud-GameId。请核对 GameID,或确认该游戏在 SUD OpenPaaS 后台的状态是否正常。 |
| 64107 | API_Invalid_Sud_GameIdMatch | 请求已过期(防重放拦截):请求头中的 X-Sud-Timestamp 与服务器当前时间的差值超过了允许的阈值(300 秒,即 5 分钟),或者报文内部的 _n (Nonce) 已被使用过。请确保业务服务器开启了 NTP 时间同步。 |
| 64108 | API_NoExist_DevSecretSym | 缺少对称密钥:请于后台配置对称密钥 |
| 64109 | API_NoExist_DevSecretAsym | 缺少非对称密钥:请于后台配置非对称密钥 |
六、生产环境安全架构最佳实践(必读)
- 强防重放机制(Replay Attack Protection): 服务端接收到任何请求后,必须严格校验时间戳
timestamp,如果与系统当前时间差值超过 300 秒(5 分钟),直接拒绝。对于 300 秒内的请求,必须将解密出的_n(Nonce) 结合_gameid存入 Redis 等分布式缓存中(TTL 设置为 300 秒)。任何新请求在处理前,若在 Redis 中命中相同的 Nonce,视为重放攻击直接阻断。 - 私钥绝对隔离与备份: 游戏私钥、对称密钥属于最高级别凭证,严禁硬编码在代码或配置文件中。由于管理后台生成的私钥仅可下载一次,研发团队必须建立安全的内网 KMS 系统或密文配置中心进行托管与备份。
- SUD OpenPaaS 证书平滑轮转: 当检测到响应头包含
X-Sud-Serial-Deprecated时,说明 SUD OpenPaaS 平台正在进行平台证书更替轮转。游戏服务应触发告警或自动化脚本,及时拉取最新的平台证书。 - 游戏密钥无缝切换(规划中规范): 若游戏方发生私钥泄露疑虑需更换密钥,为避免业务中断,建议在请求时可携带
X-Sud-Game-Serial请求头指定密钥版本(视 SUD OpenPaaS 平台具体版本支持情况而定),以实现灰度平滑切换。
七、适用接口范围
本机制为强制性要求。下表列出了服务端 API 各接口的加密签名要求:标注为「强制」的接口必须加密签名,标注为「不支持」的接口以标准 HTTPS 明文方式调用。
| 接口 | 调用方向 | 加密签名 | 处理要点 |
|---|---|---|---|
auth.getAccessToken | Game SVR → SUD OP SVR(开放接口) | 不支持 | 明文调用,响应为明文 JSON |
auth.code2session | Game SVR → SUD OP SVR(开放接口) | 不支持 | 明文调用,响应为明文 JSON |
pay.orderQuery | Game SVR → SUD OP SVR(开放接口) | 强制 | 请求先加密、再对密文签名;响应先验签、再解密 |
pay.notify | SUD OP SVR → Game SVR(回调接口) | 强制 | Game SVR 必须先验签、解密,通过后方可处理发货/退款 |
ad.rewardNotify | SUD OP SVR → Game SVR(回调接口) | 强制 | Game SVR 必须先验签、解密,通过后方可发放奖励 |
说明:
- 强制:请求必须加密并签名、响应必须验签并解密;未加密、未签名或验签失败的请求会被直接拒绝。
- 不支持:
auth.getAccessToken与auth.code2session不加密、不签名,请求参数(含secret)以明文传递,响应为标准 JSON 明文,无需构造加密报文、签名请求头,也无需处理响应验签与解密。 - 后续新增的开放接口与回调接口默认适用本机制;如需以明文方式调用,以对应接口文档的显式说明为准。
本机制处理的是 HTTP 报文层面的加密与签名,与登录流程返回的
session_key及用户敏感信息加解密是两套独立机制,请勿混用。
