签名算法
游戏服务端和世游服务端之间的网络通信,均需要使用以下的 SEAYOO-HMAC-SHA256
签名算法进行签名及验证。
前置准备
游戏已经从世游侧获取到了 Game ID 和 Secret Key。这些是本签名算法必须使用的两个值。
原语 Function
以下是计算签名过程中会用到的原语 function。
这些原语 function 在各个编程语言中通常都有标准库提供其功能。
Function | Description |
---|---|
Hex() | Lowercase base 16 encoding. See https://en.wikipedia.org/wiki/Hexadecimal |
SHA256Hash() | Secure Hash Algorithm (SHA) cryptographic hash function 256-bit. See https://en.wikipedia.org/wiki/SHA-2 |
HMAC-SHA256() | Computes HMAC by using the SHA256 algorithm with the signing key provided. See https://en.wikipedia.org/wiki/HMAC |
生成签名
步骤一: 构造待签名的 StringToSign
待签名的字符串 StringToSign 是包含以下由换行符 \n
分隔的结构。换行符 \n
的 ASCII 值为 0x0A
。
SEAYOO-HMAC-SHA256\n
<HTTPMethod>\n
<RequestURI>\n
<Timestamp>\n
<HashedPayload>
HTTPMethod 是 HTTP 方法,例如 GET
, POST
RequestURI 是 HTTP 请求中的 URI
部分,不包含协议与域名,如果有 Query String 的话需要包含。示例:
/v1/server/create-order
/foo/bar?prefix=somePrefix&marker=someMarker&max-keys=2
Timestamp 是发起 HTTP 请求时的系统秒级时间戳,使用 UTC 时区的 ISO 8601 格式。示例:
20231117T082149Z
HashedPayload 是对 HTTP request body
的 payload
哈希后的结果。计算的方式是:
Hex(SHA256Hash(<payload>)
如果 HTTP request 中的 body
为空,例如 GET
request,则 payload
为空字符串,此时计算哈希的方式为:
Hex(SHA256Hash(""))
在这种情况下,空字符串的哈希结果固定为:
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
步骤二:计算签名
使用分配给游戏的 Secret Key 作为 SigningKey,对步骤一构造出的 StringToSign 计算 HMAC-SHA256 值:
Hex(HMAC-SHA256(SigningKey, StringToSign))
计算出的签名结果示例:
152e1330eb67c094a3ca93a8e713eb136c24227225cd61901fbb1df0b4e4f56f
步骤三:将签名添加至请求
为 HTTP 请求添加以下 Authorization
header:
Authorization: <Scheme> Game=<Game>, Timestamp=<Timestamp>, Signature=<Signature>
- Scheme 固定为
SEAYOO-HMAC-SHA256
- Game 为分配给游戏的 Game ID,原样填在这里
- Timestamp 为步骤一中发起 HTTP 请求时的系统秒级时间戳,原样填在这里
- Signature 为步骤二中计算出的签名,原样填在这里
示例:
Authorization: SEAYOO-HMAC-SHA256 Game=catsnsoup, Timestamp=20231117T082149Z, Signature=adefae80e1239c6bfd91181f5139441539e21d1f6d947c2822c3ef3afa5d4f95
完整示例
假定输入数据如下:
- Game 为
xcom
- SigningKey 为
sk_secret
- HTTPMethod 为
POST
- HTTP URL 为
https://api.example.com/v1/my-test-api?key=123&value=foobar
- HTTP Request Body 为
{"hello":"world"}
- 签名时的时间戳 Timestamp 为
20231228T065821Z
则签名过程中的示例如下:
- HTTP URL 对应的 RequestURI 为:
/v1/my-test-api?key=123&value=foobar
- HTTP Request Body 对应的 HashedPayload 为:
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
- 待签名的字符串 StringToSign 为:
SEAYOO-HMAC-SHA256
POST
/v1/my-test-api?key=123&value=foobar
20231228T065821Z
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
- 计算出的签名结果 Signature 为:
05f5be3e9f55f8fa2fb027666ec5bb379ff4732181839c28c77662b7e8eb0fea
- 为 HTTP 请求添加的
Authorization
header 为:
Authorization: SEAYOO-HMAC-SHA256 Game=xcom, Timestamp=20231228T065821Z, Signature=05f5be3e9f55f8fa2fb027666ec5bb379ff4732181839c28c77662b7e8eb0fea
验证签名
- 从 HTTP Request 中提取出
Authorization
header,并解析出Scheme
、Game
、Timestamp
、Signature
。如果不能正确解析出上述值则拒绝请求,否则进行下一步。 - 验证
Scheme
。如果不等于SEAYOO-HMAC-SHA256
则拒绝请求,否则进行下一步。 - 验证
Timestamp
。如果和服务器当前系统时间的差异在 5 分钟以上则拒绝请求,否则进行下一步。 - 验证
Game
。Game
与 Secret Key 是固定且已知的。如果 Game 不一致则拒绝请求,否则进行下一步。 - 验证
Signature
。- 解析 HTTP Request,按照生成签名的步骤一,构造出待签名的 StringToSign。
- 使用 Secret Key 作为 SigningKey,按照生成签名的步骤二,对 StringToSign 进行签名计算,得到 Signature。
- 对比 Signature 和
Signature
,如果匹配,则签名验证通过,继续执行业务逻辑,否则应当拒绝请求。