1.5 接入方签名计算准则
签名计算准则
1 签名算法
HMAC即基于哈希的消息认证码(Hash-based Message Authentication Code),是一种使用密码散列函数,同时结合一个加密密钥,通过特定计算方式生成的消息认证码(MAC)。SHA-256(Secure Hash Algorithm 256)是散列函数(或哈希函数)的一种,能对一个任意长度的消息,计算出一个 32 字节长度的字符串(又称消息摘要)。
本系统所有接口的签名算法采用的是HMAC-SHA256,它是结合了SHA-256哈希函数和一个密钥的消息认证码算法,用于验证数据的完整性和真实性。
参考:
2 签名参数
以任何方式请求任何接口,必须在URL携带以下三个参数:
access_key:开发者在开放平台申请的调用凭证包括access_key和access_token。其中,access_token开放平台和开发者约定的签名密钥,而access_key作为凭证的 key,通过该 key 匹配对应的access_token。ts:请求调用行为发生时的时间戳(单位毫秒),该参数必须作为签名算法的一部分(后面示例中会讲到),且该参数具备有效期比如 10s 内的调用才是有效的调用。sign:根据ts、接口约定的签名参数以及access_token算出的签名摘要。
其他参与签名计算逻辑的参数因 API 而异,具体可以参考每个 API 的详细介绍。
3 签名示例
- 假如某接口需要的签名参数如下:
| 参数 | 数值 | 类型 | 说明 |
|---|---|---|---|
| app_id | bili123456789 | String | - |
| ss_id | 100052 | Number | - |
| p_name | bili_user_zhang | String | - |
| show_enable | true | Boolean | 布尔类型的参数,一定是小写的 true/false |
| targets | 102,103,89 | List<Number> | 如果待签名参数是 List、Set、Map 等集合类型,则需要先将元素按升序排序,再通过,序列化为单一字符串参与签名计算。数组类型不需要排序。 |
其中,集合类型参数(如上表中的 targets)在参与签名计算前,需对集合内的元素按升序排序(数组类型不需要排序):
- 数值型元素按数值大小升序;
- 字符串型元素按字典序升序。排序完成后,再使用
,将所有元素序列化为单一字符串,作为该参数的签名参与值。 - 调用时时间戳
ts=1736257902605 - 已申请的
access_token=DsI5UxNG5NWuYTJlNDg1NGFkMzRl9Ukp
则计算过程如下:
(1)将每个参数对,拼接为name=value的结构,「app_id=bili123456789,ss_id=100052,p_name=bili_user_zhang,show_enable=true,targets=89,102,103,ts=1736257902605」。
任何需要签名的参数,无论何种数据类型,如果该字段缺省,则该参数不参与签名。需要注意的是,空字符串''和缺省是相同语义的即不参与签名。
(2)将上述参数对按照字典序升序排序,得到排序结果,「app_id=bili123456789,p_name=bili_user_zhang,show_enable=true,ss_id=100052,targets=89,102,103,ts=1736257902605」。
(3)将上述升序的参数对,通过&字符拼接,形成完整的待签名的数据,app_id=bili123456789&p_name=bili_user_zhang&show_enable=true&ss_id=100052&targets=89,102,103&ts=1736257902605。
(4)将access_token=DsI5UxNG5NWuYTJlNDg1NGFkMzRl9Ukp作为密钥创建基于 HMAC-SHA256 的算法实例(签名代码参考中会讲到),然后将步骤(3)中的数据进行散列,经过 Base64 编码&替换(将 URL 不友好的字符+/=替换为B),最终得到摘要sign=t8cgNWwc67cgvpKsiHp3nxnIBxHAPlfO8ANcXvhkCgAB。
4 签名代码参考
JAVA 示例代码:
public class SignGenerator {
private static final String ALGORITHM = "HmacSHA256";
public static String sign(List<Pair<String, String>> parameters, String accessToken) throws Exception {
String data = parameters.stream()
.map(p -> String.format("%s=%s", p.getKey(), p.getValue()))
.sorted(Comparator.comparing(Function.identity()))
.collect(Collectors.joining("&"));
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(new SecretKeySpec(accessToken.getBytes(StandardCharsets.UTF_8), ALGORITHM));
byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
String rawSign = Base64.getEncoder().encodeToString(rawHmac);
//将可能导致URL安全隐患的特殊字符(+、/、=)替换为B
return rawSign.replaceAll("[+/=]", "B");
}
}
PHP 示例代码:
<?php
class SignGenerator {
private const ALGORITHM = 'sha256';
public static function sign(array $parameters, string $accessToken): string {
$mappedParams;
foreach ($parameters as $key => $value) {
$mappedParams[] = $key.'='.$value;
}
sort($mappedParams);
$data = implode('&', $mappedParams);
// Generate HMAC
$rawHmac = hash_hmac(self::ALGORITHM, $data, $accessToken, true);
$rawSign = base64_encode($rawHmac);
// Replace URL unsafe characters
return str_replace(['+', '/', '='], 'B', $rawSign);
}
}
// 需要签名的参数
$parameters = ['order_id' => '1000351', 'app_id' => 'bilixxxxxx', 'ts'=>1754539876259];
$signature = SignGenerator::sign($parameters, 'token');
print($signature);
?>
5 支付签名校验工具
提供了支付签名校验工具,请点击这里访问