Skip to content

服务端 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
    _nstring是防重放随机字符串(Nonce),推荐使用 16-32 字节的 Base64 随机字符串
    _gameidstring是游戏唯一标识 GameID,必须与 AAD 中的 gameid 一致
    _timestampnumber是加密操作的时间戳(秒级),必须与 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
  • 准备原始业务数据

    json
    {
        "env": 0,
        "out_trade_no": "order_202607220001"
    }
  • 生成 IV 并拼接 AAD

    生成 12 字节随机数据,并进行 Base64 编码作为 iv:

    text
    base64_encode(iv) = fmW/zNxXlytUZBgj

    按照 urlpath|gameid|timestamp|sn 格式严格拼接 AAD:

    text
    aad = 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 编码:

    text
    base64_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 的值)。
    dataHTTP 请求的 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 连接。

    text
    https://cn-cpapi.sudbiz.cloud/v1/pay/order/query
    game_10010
    1710500000
    {"iv":"fmW/zNxXlytUZBgj","data":"OIDVdrPtSPF/0e2CTXCV2vVNPbVJdJlP2WaTMQnoYLh5iCrrSNfQFh25EnStDMf0hLlVNBCZQtf9NaV0m4aRA4AAYIO7oR/Ge+4yY4EmZp5EVPB42xjScgMx5X3D4VdLCfynXIUKUtZHZvk1zmLVE3RauzJgiM1BB1C","authtag":"5qeM/2vZv+6KtScN94IpMg=="}
  • 计算签名并编码

    对上述字符串 M 使用游戏私钥进行签名:

    text
    base64_encode(Signature) = wcSSWHZunjz9VKl9q+If9deiyECXDAELFAJNZ4+5T+NhFr8zfhkwdQtlgQ7nM...
  • 最终发送的完整 HTTP 请求

    http
    POST /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 的值。
dataHTTP 响应的 Body 原始密文字符串。

4.2 响应密文解密 ​

解密使用的对称密钥、密钥编号与 Game SVR 请求时一致。AAD 拼接格式为 urlpath|gameid|timestamp|sn。解密后同样需要强校验明文中的 _gameid 与 _timestamp。

五、常见错误码及解决方案 ​

错误码错误码取值解决方案
64101API_Missing_Sud_Timestamp请求头缺失:缺少 X-Sud-Timestamp 字段。请补充秒级时间戳,并确保其与加密报文内部的 _timestamp 完全一致。
64102API_Missing_Sud_Signature请求头缺失:缺少 X-Sud-Signature 字段。请检查私钥签名逻辑,并将生成的签名进行 Base64 编码后放入该 Header。
64103API_Missing_Sud_GameId请求头缺失:缺少 X-Sud-GameId 字段。请检查 HTTP Header 是否正确注入了游戏唯一标识 GameID。
64104API_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)。
64105API_Invalid_Encrypt加解密失败:1. 检查 AES-256-GCM 的 AAD 拼接格式(urlpath|gameid|timestamp|sn)是否正确。2. 确认传入的 Cipher 密文是否完整。在部分语言底层库中,需要将加密输出的 CipherText 与 AuthTag 拼接后传递;若分离输出,需确保 JSON 报文中的 authtag 字段无误。
64106API_Invalid_Sud_GameIdGameID 无效或未生效:平台无法识别 X-Sud-GameId。请核对 GameID,或确认该游戏在 SUD OpenPaaS 后台的状态是否正常。
64107API_Invalid_Sud_GameIdMatch请求已过期(防重放拦截):请求头中的 X-Sud-Timestamp 与服务器当前时间的差值超过了允许的阈值(300 秒,即 5 分钟),或者报文内部的 _n (Nonce) 已被使用过。请确保业务服务器开启了 NTP 时间同步。
64108API_NoExist_DevSecretSym缺少对称密钥:请于后台配置对称密钥
64109API_NoExist_DevSecretAsym缺少非对称密钥:请于后台配置非对称密钥

六、生产环境安全架构最佳实践(必读) ​

  1. 强防重放机制(Replay Attack Protection): 服务端接收到任何请求后,必须严格校验时间戳 timestamp,如果与系统当前时间差值超过 300 秒(5 分钟),直接拒绝。对于 300 秒内的请求,必须将解密出的 _n (Nonce) 结合 _gameid 存入 Redis 等分布式缓存中(TTL 设置为 300 秒)。任何新请求在处理前,若在 Redis 中命中相同的 Nonce,视为重放攻击直接阻断。
  2. 私钥绝对隔离与备份: 游戏私钥、对称密钥属于最高级别凭证,严禁硬编码在代码或配置文件中。由于管理后台生成的私钥仅可下载一次,研发团队必须建立安全的内网 KMS 系统或密文配置中心进行托管与备份。
  3. SUD OpenPaaS 证书平滑轮转: 当检测到响应头包含 X-Sud-Serial-Deprecated 时,说明 SUD OpenPaaS 平台正在进行平台证书更替轮转。游戏服务应触发告警或自动化脚本,及时拉取最新的平台证书。
  4. 游戏密钥无缝切换(规划中规范): 若游戏方发生私钥泄露疑虑需更换密钥,为避免业务中断,建议在请求时可携带 X-Sud-Game-Serial 请求头指定密钥版本(视 SUD OpenPaaS 平台具体版本支持情况而定),以实现灰度平滑切换。

七、适用接口范围 ​

本机制为强制性要求。下表列出了服务端 API 各接口的加密签名要求:标注为「强制」的接口必须加密签名,标注为「不支持」的接口以标准 HTTPS 明文方式调用。

接口调用方向加密签名处理要点
auth.getAccessTokenGame SVR → SUD OP SVR(开放接口)不支持明文调用,响应为明文 JSON
auth.code2sessionGame SVR → SUD OP SVR(开放接口)不支持明文调用,响应为明文 JSON
pay.orderQueryGame SVR → SUD OP SVR(开放接口)强制请求先加密、再对密文签名;响应先验签、再解密
pay.notifySUD OP SVR → Game SVR(回调接口)强制Game SVR 必须先验签、解密,通过后方可处理发货/退款
ad.rewardNotifySUD OP SVR → Game SVR(回调接口)强制Game SVR 必须先验签、解密,通过后方可发放奖励

说明:

  • 强制:请求必须加密并签名、响应必须验签并解密;未加密、未签名或验签失败的请求会被直接拒绝。
  • 不支持:auth.getAccessToken 与 auth.code2session 不加密、不签名,请求参数(含 secret)以明文传递,响应为标准 JSON 明文,无需构造加密报文、签名请求头,也无需处理响应验签与解密。
  • 后续新增的开放接口与回调接口默认适用本机制;如需以明文方式调用,以对应接口文档的显式说明为准。

本机制处理的是 HTTP 报文层面的加密与签名,与登录流程返回的 session_key 及用户敏感信息加解密是两套独立机制,请勿混用。