Skip to main content

签名方法

本节讲述在调用API时如何生成数字签名,为了保护用户的财产安全,调用API时需要进行签名(Signature)验证

用户可以在飞鱼云短信平台查看访问凭证:

  • AppKey: 用于标识API调用者身份,相当于用户名。
  • AppSecret: 用于验证API调用者的身份,相当于密码。
警告
  • 用户必须妥善保管凭证,不泄露给他人,否则将造成财产安全隐患。如发现泄漏,请立即禁用该凭证。

签名生成过程

API支持 GET 和 POST 请求。对于 GET 方法,支持 Content-Type: application/x-www-form-urlencoded 格式。对于 POST 方式,只支持 Content-Type: application/json 格式,Content-Type: multipart/form-data 只有特定接口支持。

下面详细解释签名计算过程。

1.拼接待签名字符串

按如下伪代码格式拼接规范待签名串(SignatureStr):

String signStr =
RequestURI + '\n' +
RequestTimestamp + '\n' +
RequestQueryString + '\n' +
HashedRequestPayload
字段名称解释
RequestURIURI 参数。此示例取值为 /rest/sms/v3/signature/queryStatus
RequestTimestamp请求时间戳,即请求头部的公共参数 X-FZ-Timestamp 取值。此示例取值为 1713100791403
RequestQueryString发起 HTTP 请求 URL 中的查询字符串,对于 POST 请求,固定为空字符串"",对于 GET 请求,则为 URL 中问号(?)后面的字符串内容,例如:limit=10&id=1。
注意:RequestQueryString 需要参考 RFC3986 进行 URLEncode 编码(特殊字符编码后需大写字母),字符集 UTF-8。推荐使用编程语言标准库进行编码。
HashedRequestPayload请求正文(payload,即 body,此示例为 {"signIdSet":[123239,123240]})的哈希值,计算伪代码为 Lowercase(HexEncode(Hash.SHA256(RequestPayload))),即对 HTTP 请求正文做 SHA256 哈希,然后十六进制编码,最后编码串转换成小写字母。对于 GET 请求,RequestPayload 固定为空字符串。此示例计算结果是 dfb249a560bd4452e1674a77cb41c7e07bc90b72f951b4bc8bce9f62b514f7af。
注意

1.Timestamp 必须是当前系统时间,且需确保系统时间和标准时间是同步的,如果相差超过五分钟则必定失败。如果长时间不和标准时间同步,可能运行一段时间后,请求失败,返回签名过期错误。

根据以上规则,示例中得到的规范请求串如下:

/rest/sms/v3/signature/queryStatus
1713100791403

dfb249a560bd4452e1674a77cb41c7e07bc90b72f951b4bc8bce9f62b514f7af

2.计算签名

计算签名,伪代码如下:

 //先计算加密的密钥 key
String appSecret = "04f229cbba734e22af3f1151a73f8f5d";
byte[] secretKey = HmacUtils.hmacSHA256(appSecret.getBytes(StandardCharsets.UTF_8), timestamp);
String signature = HmacUtils.hmacSHA256Hex(secretKey, signStr);

此示例计算的签名为:0165ab701a71e6ee8907f5282785fae549ed032d67c08fc8a478935fecd15159。

3.拼接 Authorization

伪代码如下:

String authorization = String.format("%s credential=%s,signature=%s", "HmacSHA256", AppKey, signature);

根据以上规则,示例中得到的值为:

HmacSHA256 credential=1kl3pY,signature=0165ab701a71e6ee8907f5282785fae549ed032d67c08fc8a478935fecd15159

最终完整的调用消息如下:

POST https://api.ffrcs.cn/rest/sms/v3/signature/queryStatus
Authorization: HmacSHA256 credential=1kl3pY,signature=783752015052014ccd08b4d43997063e68f02d0efd32d5fa1758a9c87d4a589c
Content-Type: application/json; charset=utf-8
Host: api.ffrcs.cn
X-FZ-Timestamp: 1713100791403

{"signIdSet":[123239,123240]}

签名演示

为了更清楚地解释签名过程,下面以实际编程语言为例,将上述的签名过程完整实现。


import org.bouncycastle.util.encoders.Hex;

import org.apache.commons.codec.digest.DigestUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class FeiyusmsApiDemo {

public static byte[] hmacSHA256(byte[] key, String message) throws Exception {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key, "HmacSHA256");
hmacSha256.init(secret_key);
return hmacSha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
}


public static String hmacSHA256Hex(String key, String message) throws Exception {
byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
return hmacSHA256Hex(bytes, message);
}

public static String hmacSHA256Hex(byte[] key, String message) throws Exception {
byte[] data = hmacSHA256(key, message);
return Hex.toHexString(data);
}

public static void main(String[] args) throws Exception {
String host = "api.ffrcs.cn";
//1.拼接请求串
String canonicalUri = "/rest/sms/v3/signature/queryStatus";
String requestTimestamp = Long.toString(System.currentTimeMillis());
String canonicalQueryString = "";
byte[] requestPayload = "{\"signIdSet\":[123239,123240]}\"".getBytes(StandardCharsets.UTF_8);
String hashedRequestPayload = DigestUtils.sha256Hex(requestPayload);
String signStr = String.format("%s\n%s\n%s\n%s", canonicalUri, requestTimestamp,
canonicalQueryString, hashedRequestPayload);

//2.计算签名
String secretId = "1kl3pY");
String secretKey = "04f229cbb*****1a73f8f5d";
byte[] secretString = HmacUtils.hmacSHA256(secretKey.getBytes(StandardCharsets.UTF_8), requestTimestamp);
String signature = HmacUtils.hmacSHA256Hex(secretString, signStr);

//3.生成 Authorization
String authorization = String.format("%s credential=%s,signature=%s", clientConfig.getSignatureMethod()
, secretId, signature);

Map<String, String> headers = new HashMap<>();
headers.put("Authorization", authorization);
headers.put("Host", host);
headers.put("X-FZ-Timestamp", requestTimestamp);

}

}