签名算法说明

所有接口统一使用 HMAC‑SHA256 进行签名,用于身份校验与防篡改、防重放。签名密钥为 openapi_secret,该密钥仅参与签名计算,不随请求传递。
签名流程步骤
  1. 收集全部请求参数:包含鉴权参数(openapi_key、Timestamp、Nonce)+ 当前接口所有业务表单参数;Signature 参数本身不参与签名拼接
  2. 对参数名按照字典序(ASCII升序)排序;
  3. 按顺序拼接成 key=value 形式,多个参数直接相连,无分隔符;
    示例:openapi_key=AK123Nonce=xxxTimestamp=1769932800DomainName=push.xxx.com
  4. 使用 openapi_secret 作为密钥,对上面拼接好的原始串做 HMAC‑SHA256;
  5. 将得到的二进制结果转为小写十六进制字符串,即为 Signature;
  6. 最后把 Signature 和其他参数一起 POST 提交到接口。
⚠️ 重点规则:
1. 参数值不做URL编码再参与签名(前后端统一规则);
2. 参数值为中文、特殊字符直接原样参与拼接;
3. 时间戳为Unix秒级时间戳;
4. 密钥 openapi_secret 不要传给接口,仅本地用来算签名;
5. 排序是参数名排序,不是参数值排序。
示例演示(用于测试)
测试数据:

openapi_key = "AK001"
openapi_secret = "SKabc123456"
Timestamp = "1769932800"
Nonce = "550e8400-e29b-41d4-a716-446655440000"
业务参数:DomainName="push.chunyuyun.cn",AppName="live",StreamName="test001"排序后的 key 顺序:AppName,DomainName,Nonce,StreamName,Timestamp,openapi_key
拼接原文:
AppName=liveDomainName=push.chunyuyun.cnNonce=550e8400-e29b-41d4-a716-446655440000StreamName=test001Timestamp=1769932800openapi_key=AK001
PHP 签名示例
<?php
function calcSign($params, $secret)
{
    // 移除Signature,不参与签名
    unset($params['Signature']);
    // 按键名字典排序
    ksort($params, SORT_ASC);
    $src = '';
    foreach ($params as $k => $v) {
        $src .= $k . '=' . $v;
    }
    // HMAC-SHA256,转小写16进制
    $sign = hash_hmac('sha256', $src, $secret, false);
    return strtolower($sign);
}// 使用
$secret = "SKabc123456";
$param = [
    "openapi_key" => "AK001",
    "Timestamp" => "1769932800",
    "Nonce" => "550e8400-e29b-41d4-a716-446655440000",
    "DomainName" => "[push.chunyuyun.cn](https://push.chunyuyun.cn)",
    "AppName" => "live",
    "StreamName" => "test001"
];
\(sign = calcSign(\)param, $secret);
echo $sign;
?>
Python 签名示例(Python3)
import hmac
import hashlibdef calc_sign(params: dict, secret: str) -> str:
params = params.copy()
params.pop("Signature", None)

# 按键排序

sorted_items = sorted(params.items(), key=lambda x: x[0])
src = "".join([f"{k}={v}" for k, v in sorted_items])
h = hmac.new(secret.encode("utf-8"), src.encode("utf-8"), hashlib.sha256)
return h.hexdigest().lower()

# 使用

secret = "SKabc123456"
param = {
    "openapi_key": "AK001",
    "Timestamp": "1769932800",
    "Nonce": "550e8400-e29b-41d4-a716-446655440000",
    "DomainName": "[push.chunyuyun.cn](https://push.chunyuyun.cn)",
    "AppName": "live",
    "StreamName": "test001"
}
sign = calc_sign(param, secret)
print(sign)
Golang 签名示例
package mainimport (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "sort"
)

func CalcSign (params map [string] string, secret string) string {
    delete (params, "Signature")
    // 获取 key 并排序
    var keys [] string
    for k := range params {
        keys = append (keys, k)
    }
    sort.Strings (keys)
    var src string
    for _, k := range keys {
        src += k + "=" + params [k]
    }
    h := hmac.New (sha256.New, [] byte (secret))
    h.Write ([] byte (src))
    return hex.EncodeToString (h.Sum (nil))
}

func main() {
    secret := "SKabc123456"
    param := map[string]string{
        "openapi_key": "AK001",
        "Timestamp":   "1769932800",
        "Nonce":       "550e8400-e29b-41d4-a716-446655440000",
        "DomainName":  "[push.chunyuyun.cn](https://push.chunyuyun.cn)",
        "AppName":     "live",
        "StreamName":  "test001",
    }
    sign := CalcSign(param, secret)
    fmt.Println(sign)
}
C# (.NET Framework 4.7.2) 签名示例
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;public static string CalcSign(SortedDictionary paramDic, string secret)
{
    if (paramDic.ContainsKey("Signature"))
    paramDic.Remove("Signature");

    StringBuilder sb = new StringBuilder();
    foreach (var kv in paramDic)
    {
        sb.Append($"{kv.Key}={kv.Value}");
    }
    string src = sb.ToString();
    byte[] keyBytes = Encoding.UTF8.GetBytes(secret);
    byte[] srcBytes = Encoding.UTF8.GetBytes(src);
    using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
    {
        byte[] hash = hmac.ComputeHash(srcBytes);
        return BitConverter.ToString(hash).Replace("-", "").ToLower();
    }
}

// 调用示例
/*
var dic = new SortedDictionary
{
    {"openapi_key","AK001"},
    {"Timestamp","1769932800"},
    {"Nonce","550e8400-e29b-41d4-a716-446655440000"},
    {"DomainName","[push.chunyuyun.cn](https://push.chunyuyun.cn)"},
    {"AppName","live"},
    {"StreamName","test001"}
};
string sign = CalcSign(dic,"SKabc123456");
*/
Java 签名示例
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;public class SignUtil {
   public static String calcSign (Map params, String secret) throws Exception {
        params.remove ("Signature");
        // TreeMap 天然字典升序
        TreeMap sortedMap = new TreeMap<>(params);
        StringBuilder src = new StringBuilder ();
        for (Map.Entry entry : sortedMap.entrySet ()) {
            src.append (entry.getKey ()).append ("=").append (entry.getValue ());
        }
        SecretKeySpec keySpec = new SecretKeySpec (secret.getBytes (StandardCharsets.UTF_8), "HmacSHA256");
        Mac mac = Mac.getInstance ("HmacSHA256");
        mac.init (keySpec);
        byte [] result = mac.doFinal (src.toString ().getBytes (StandardCharsets.UTF_8));
        StringBuilder hexStr = new StringBuilder ();
        for (byte b : result) {
            String hex = Integer.toHexString (b & 0xFF);
            if (hex.length () == 1) hexStr.append ("0");
            hexStr.append (hex);
        }
        return hexStr.toString ().toLowerCase ();
    }

    public static void main(String[] args) throws Exception {
        String secret = "SKabc123456";
        Map param = new HashMap<>();
        param.put("openapi_key", "AK001");
        param.put("Timestamp", "1769932800");
        param.put("Nonce", "550e8400-e29b-41d4-a716-446655440000");
        param.put("DomainName", "[push.chunyuyun.cn](https://push.chunyuyun.cn)");
        param.put("AppName", "live");
        param.put("StreamName", "test001");
        System.out.println(calcSign(param, secret));
    }
}
请求组装说明
算好签名之后,把 Signature 和所有参数一起放入 Form 表单 POST 请求。

POST /接口名称.ashx
Form表单参数:
openapi_key=AK001
Timestamp=1769932800
Nonce=550e8400-e29b-41d4-a716-446655440000
DomainName=push.chunyuyun.cn
AppName=live
StreamName=test001
Signature=【上面代码计算得到的签名字符串】
排查签名不一致最常见坑:
1. 排序错误:是参数名排序,不是值;
2. Signature 参与了签名原文拼接;
3. 一边编码urlencode,另一边没有编码;
4. 大小写问题:最终签名要求小写;
5. 编码不一致,一个UTF8一个GBK;
6. 前后时间戳不一致,时差超过±300s。