1. 首页
  2. 秒信通MiCPaaS
  3. 🗊 国内短信
  4. 🗊 API接口
  5. 🗎 API 签名生成规则
菜单
本页目录

API 签名生成规则文档

API 签名生成规则文档

一、文档概述

本文档统一规范秒信通平台所有接口请求签名(signature)生成逻辑,所有业务接口请求必须携带合法签名,服务端通过相同规则校验签名合法性,防止参数篡改、非法请求;同时补充CURL请求兼容配置说明及多语言完整签名工具代码。

二、签名整体流程

  1. 整理请求参数,剔除signature字段
  2. 参数按键升序排序
  3. 对键、值执行URL编码+指定字符替换
  4. 拼接基础参数字符串(QueryString)
  5. 拼接请求方式、路径、基础参数,生成待加密原始串
  6. 使用产品密钥通过HmacSHA1加密原始串
  7. 加密结果Base64编码,最终得到signature签名

三、分步详细生成规则

步骤1:构造基础QueryString

1.1 参数筛选

取出接口全部业务请求参数,移除signature签名字段,剩余所有参与签名计算。

1.2 参数排序

对剩余参数的key(参数名)按照ASCII字典升序排序。

1.3 键值编码与字符替换

分别对排序后每一组keyvalue执行标准URLEncode,编码完成后执行3项字符替换:

原字符 替换目标字符
+ %20
* %2A
%7E ~

1.4 拼接基础QueryString

单组参数格式:编码后key=编码后value

多参数使用&分隔拼接,示例:

name=%E5%BC%A0%E4%B8%89&age=25

1.5 拼接请求方式+接口路径,生成待加密完整串

拼接格式:

HTTP请求方法 + "&" + URLEncode("/") + "&" + 上一步基础QueryString
  • HTTP请求方法大写:GET/POST/PUT/DELETE等;
  • 固定拼接&%2F&/URL编码后为%2F);

示例(POST请求,参数name=张三、age=25):

POST&%2F&name=%E5%BC%A0%E4%B8%89&age=25

步骤2:计算最终签名signature

  1. 加密算法:HmacSHA1
  2. 加密密钥:平台分配的产品密码(SecretKey)

操作顺序:

  1. 将步骤1生成的完整待加密字符串作为加密原文;
  2. 使用产品密钥执行HmacSHA1哈希,得到二进制摘要;
  3. 对二进制摘要做Base64编码,编码结果即为接口请求参数signature

四、请求头兼容特殊说明

重要提示:平台全部接口不支持CURL 100-Continue协议,若请求接口响应超时、卡顿,需关闭该协议

PHP CURL示例配置

$ch = curl_init($apiUrl);
// 关闭100-Continue协议头
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:"));
// 其他curl配置...

其他语言(Java/Python/Go)同理,请求头移除/清空Expect字段即可。

五、调用示例(伪代码逻辑)

输入条件

  • 请求方式:POST
  • 产品密钥:123456Secret
  • 请求参数:name=张三&sex=男&signature=xxx

1. 剔除signature,剩余参数

name=张三、sex=男

2. key升序排序

name、sex

3. URL编码+字符替换拼接基础QueryString

name=%E5%BC%A0%E4%B8%89&sex=%E7%94%B7

4. 拼接待加密原始串

POST&%2F&name=%E5%BC%A0%E4%B8%89&sex=%E7%94%B7

5. HmacSHA1+Base64

使用密钥123456Secret加密上述字符串,Base64输出结果作为signature放入请求参数。

六、各语言完整签名工具代码

所有代码内置字符替换、排序、HmacSHA1、Base64逻辑,复制即可直接调用测试

6.1 PHP 完整签名工具

sign.php
<?php
/**
 * 生成接口签名 signature
 * @param string $method 请求方式大写 GET/POST
 * @param array $params 全部请求参数数组
 * @param string $secret 产品密钥
 * @return string
 */
function buildSign(string $method, array $params, string $secret): string
{
    // 1. 移除签名字段
    unset($params['signature']);
    // 2. 按键升序排序
    ksort($params);

    // 3. URL编码 + 字符替换
    $kvList = [];
    foreach ($params as $k => $v) {
        $encodeK = urlencode($k);
        $encodeV = urlencode((string)$v);
        // 规则替换
        $encodeK = str_replace(['+', '*', '%7E'], ['%20', '%2A', '~'], $encodeK);
        $encodeV = str_replace(['+', '*', '%7E'], ['%20', '%2A', '~'], $encodeV);
        $kvList[] = "{$encodeK}={$encodeV}";
    }
    $queryStr = implode('&', $kvList);

    // 4. 拼接待加密原文
    $rawStr = $method . "&" . urlencode("/") . "&" . $queryStr;

    // 5. HmacSHA1 + base64
    $hmac = hash_hmac('sha1', $rawStr, $secret, true);
    return base64_encode($hmac);
}

// ========== 测试示例 ==========
$secretKey = "123456Secret";
$reqMethod = "POST";
$reqParams = [
    'name' => '张三',
    'sex' => '男',
    'signature' => ''
];
$sign = buildSign($reqMethod, $reqParams, $secretKey);
echo "生成签名:" . $sign;
?>

6.2 Java 完整签名工具(JDK8+)

SignUtil.java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class SignUtil {
    private static final String ALGORITHM = "HmacSHA1";

    /**
     * 生成签名
     * @param method 请求大写 GET/POST
     * @param params 请求参数Map
     * @param secret 产品密钥
     * @return signature
     * @throws Exception
     */
    public static String buildSign(String method, Map<String, String> params, String secret) throws Exception {
        // 1. 移除signature
        params.remove("signature");
        // 2. key升序排序
        TreeMap<String, String> sortedMap = new TreeMap<>(params);

        List<String> kvArr = new ArrayList<>();
        for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
            String k = entry.getKey();
            String v = entry.getValue();
            // url编码
            String ek = URLEncoder.encode(k, StandardCharsets.UTF_8.name());
            String ev = URLEncoder.encode(v, StandardCharsets.UTF_8.name());
            // 字符替换
            ek = ek.replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
            ev = ev.replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
            kvArr.add(ek + "=" + ev);
        }
        String queryStr = String.join("&", kvArr);
        // 拼接原始串
        String raw = method + "&" + URLEncoder.encode("/", StandardCharsets.UTF_8.name()) + "&" + queryStr;

        // HmacSHA1
        SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Mac mac = Mac.getInstance(ALGORITHM);
        mac.init(keySpec);
        byte[] hmacByte = mac.doFinal(raw.getBytes(StandardCharsets.UTF_8));
        // Base64
        return Base64.getEncoder().encodeToString(hmacByte);
    }

    // 测试入口
    public static void main(String[] args) throws Exception {
        String secret = "123456Secret";
        String method = "POST";
        Map<String, String> param = new HashMap<>();
        param.put("name", "张三");
        param.put("sex", "男");
        param.put("signature", "");

        String sign = buildSign(method, param, secret);
        System.out.println("签名结果:" + sign);
    }
}

6.3 Python3 完整签名工具

sign.py
import hmac
import hashlib
import base64
from urllib.parse import quote

def build_sign(method: str, params: dict, secret: str) -> str:
    """
    生成接口签名
    :param method: 请求大写 GET/POST
    :param params: 请求参数字典
    :param secret: 产品密钥
    :return: signature
    """
    # 1. 删除签名字段
    params.pop("signature", None)
    # 2. 按键升序
    sorted_items = sorted(params.items(), key=lambda x: x[0])

    kv_list = []
    for k, v in sorted_items:
        # url quote 等价urlencode
        ek = quote(str(k), safe='')
        ev = quote(str(v), safe='')
        # 字符替换规则
        ek = ek.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
        ev = ev.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
        kv_list.append(f"{ek}={ev}")
    query_str = "&".join(kv_list)

    # 拼接待加密字符串
    slash_encode = quote("/", safe='')
    raw_str = f"{method}&{slash_encode}&{query_str}"

    # HmacSHA1
    hmac_obj = hmac.new(secret.encode("utf-8"), raw_str.encode("utf-8"), hashlib.sha1)
    digest = hmac_obj.digest()
    # base64编码返回
    return base64.b64encode(digest).decode("utf-8")

# 测试示例
if __name__ == "__main__":
    secret_key = "123456Secret"
    req_method = "POST"
    req_params = {
        "name": "张三",
        "sex": "男",
        "signature": ""
    }
    sign_result = build_sign(req_method, req_params, secret_key)
    print("生成签名:", sign_result)

七、校验注意事项

  • 参数大小写敏感:参数key区分大小写,排序严格区分ASCII码;
  • 空值参数:参数值为空字符串也必须参与签名计算,不可丢弃;
  • 编码标准:使用RFC3986标准URL编码,必须执行规定的3种字符替换;
  • 密钥安全:产品密钥仅服务端与业务方后端持有,禁止前端明文泄露;
  • 签名时效性:建议接口搭配timestamp时间戳防重放(可拓展接入参数);
  • 多语言统一:PHP/Java/Python编码字符集统一UTF-8,否则签名不一致。

八、常见错误排查

1. 签名校验失败

  • 未移除signature参数参与加密;
  • 参数未按key升序;
  • URL编码后未执行+/*/~字符替换;
  • 请求方法大小写错误(小写post不匹配);
  • HmacSHA1密钥使用错误、字符编码非UTF-8、Base64格式异常。

2. 接口请求超时

CURL未清空Expect请求头,触发100-Continue等待。