签名方法
最近更新时间:2026-03-19 18:16:28
天翼云智能视图服务 API 会对每个访问请求进行身份验证,所以无论使用 HTTP 还是 HTTPS 协议提交请求,都需要在公共请求参数中包含身份认证信息(Authorization)以验证请求者身份。 签名信息由访问密钥生成,访问密钥包括 Access Key Id 和 Secret Access Key。
1. 签名流程
假设用户的 Access Key Id 和 Secret Access Key 分别是:
Access Key Id: 8FR8VXACHFFQIT33****
Secret Access Key: PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****
注意:这里只是示例,请用户根据实际申请的 Access Key Id 和 Secret Access Key 进行后续操作!
POST 请求以创建设备为例,当用户调用这一接口时,其请求参数可能如下:
curl -X POST https://vssapi.ctyun.cn/devices \
-H "Authorization: CT-HMAC-SHA256 Credential=8FR8VXACHFFQIT33****/2022-02-24/vss, SignedHeaders=content-type;host;timestamp, Signature=4d4683c13a3ab96a4a0b661c364afa8382c18b3968817ba0ab1a22c2825c3475" \
-H "Content-Type: application/json;charset=utf-8" \
-H "Host: vssapi.ctyun.cn" \
-H "Version: 2021-11-25" \
-H "Timestamp: 1645679518" \
-d '{"Region":"0851001","InNetworkType":"public","OutNetworkType":"public","Device":{"DeviceType":"ipc","DeviceVendor":"海康","DeviceName":"设备名称","DeviceChannelSize":1,"InOrgRegion":11000000,"InOrgRegionLevel":1,"DeviceIndustryCode":"54","DeviceNetworkCode":"7"},"Resource":{"Video":[{"BillingMode":"packages","ResourceId":"resource01"}]},"VideoDevice":{"Gb28181Device":{"InVersion":"2016","InUserName":"test","StreamTransProtocol":"tcp","AutoStartStream":true}}}'
GET 请求以查询设备为例,当用户调用这一接口时,其请求参数可能如下:
curl -X GET https://vssapi.ctyun.cn/devices/743780360209498112?IncludeDeviceDir=1&IncludeDeviceStats=0 \
-H "Authorization: CT-HMAC-SHA256 Credential=8FR8VXACHFFQIT33****/2023-03-15/vss, SignedHeaders=host;timestamp, Signature=50c80b3b98c1e1f3e03eb6eafed541cf9f06714314151f0063ceeb61cbc1911f" \
-H "Host: vssapi.ctyun.cn" \
-H "Version: 2021-11-25" \
-H "Timestamp: 1678855875" \
1.1 拼接规范请求串
按如下伪代码格式拼接规范请求串(CanonicalRequest):
CanonicalRequest =
HTTPRequestMethod + '\n' +
CanonicalURI + '\n' +
CanonicalQueryString + '\n' +
CanonicalHeaders + '\n' +
SignedHeaders + '\n' +
HashedRequestPayload
| 字段名称 | 解释 |
|---|---|
| HTTPRequestMethod | HTTP 请求方法,此示例取值为 POST。 |
| CanonicalURI | URI 参数。 |
| CanonicalQueryString | 发起 HTTP 请求 URL 中的查询字符串,对于 POST 请求,固定为空字符串""。 |
| CanonicalHeaders | 参与签名的头部信息,一般包含 content-type, host, timestamp,也可加入自定义的头部参与签名以提高自身请求的唯一性和安全性。拼接规则:头部 key 和 value 统一转成小写,并去掉首尾空格,按照 key:value\n 格式拼接。此示例计算结果是 content-type:application/json;charset=utf-8\nhost:vssapi.ctyun.cn\ntimestamp:1645679518\n。注意:content-type 必须和实际发送的相符合,有些编程语言网络库即使未指定也会自动添加 charset 值,如果签名时和发送时不一致,服务器会返回签名校验失败。 |
| SignedHeaders | 参与签名的头部信息,说明此次请求有哪些头部参与了签名,和 CanonicalHeaders 包含的头部内容是一一对应的。拼接规则:头部 key 统一转成小写,多个头部以分号(;)分隔。此示例为 content-type;host;timestamp。 |
| HashedRequestPayload | 请求正文(payload,即 body。此示例为:{"Region":"0851001","InNetworkType":"public","OutNetworkType":"public","Device":{"DeviceType":"ipc","DeviceVendor":"海康","DeviceName":"设备名称","DeviceChannelSize":1,"InOrgRegion":11000000,"InOrgRegionLevel":1,"DeviceIndustryCode":"54","DeviceNetworkCode":"7"},"Resource":{"Video":[{"BillingMode":"packages","ResourceId":"resource01"}]},"VideoDevice":{"Gb28181Device":{"InVersion":"2016","InUserName":"test","StreamTransProtocol":"tcp","AutoStartStream":true}}}。GET 请求,body 为"")的哈希值,计算伪代码为 Lowercase(HexEncode(Hash.SHA256(RequestPayload))),即对 HTTP 请求正文做 SHA256 哈希,然后十六进制编码,最后编码串转换成小写字母。此示例计算结果是 33ae944e2ea9875823994339826707985f4f54f062cc5533aab72d6afe959a36。 |
根据以上规则,POST 请求示例中得到的规范请求串如下:
POST
/devices
content-type:application/json;charset=utf-8
host:vssapi.ctyun.cn
timestamp:1645679518
content-type;host;timestamp
33ae944e2ea9875823994339826707985f4f54f062cc5533aab72d6afe959a36
GET 请求示例中得到的规范请求串如下:
GET
/devices/743780360209498112
IncludeDeviceDir=1&IncludeDeviceStats=0
host:vssapi.ctyun.cn
timestamp:1678855875
host;timestamp
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
1.2 拼接待签名字符串
按如下格式拼接待签名字符串:
StringToSign =
Algorithm + \n +
RequestTimestamp + \n +
CredentialScope + \n +
HashedCanonicalRequest
| 字段名称 | 解释 |
|---|---|
| Algorithm | 签名算法,目前固定为 CT-HMAC-SHA256。 |
| RequestTimestamp | 请求时间戳,即请求头部的公共参数 Timestamp 取值,取当前时间 UNIX 时间戳,精确到秒。此示例取值为 1645679518。 |
| CredentialScope | 凭证范围,格式为 Date/vss,包含日期、所请求的服务。Date 为 UTC 标准时间的日期,取值需要和公共参数 Timestamp 换算的 UTC 标准时间日期一致;vss 为产品名,必须与调用的产品域名一致。此示例计算结果是 2022-02-24/vss。 |
| HashedCanonicalRequest | 前述1.1步骤拼接所得规范请求串的哈希值,计算伪代码为 Lowercase(HexEncode(Hash.SHA256(CanonicalRequest)))。此示例计算结果是 33ae944e2ea9875823994339826707985f4f54f062cc5533aab72d6afe959a36。 |
Date 必须从时间戳 Timestamp 计算得到,且时区为 UTC+0。如果加入系统本地时区信息,例如东八区,将导致白天和晚上调用成功,但是凌晨时调用必定失败。假设时间戳为 1551113065,在东八区的时间是 2019-02-26 00:44:25,但是计算得到的 Date 取 UTC+0 的日期应为 2019-02-25,而不是 2019-02-26。
Timestamp 必须是当前系统时间,且需确保系统时间和标准时间是同步的,如果相差超过五分钟则必定失败。如果长时间不和标准时间同步,可能导致运行一段时间后,请求必定失败,返回签名过期错误。
根据以上规则,POST 请求示例中得到的待签名字符串如下:
CT-HMAC-SHA256
1645679518
2022-02-24/vss
d3af0c0a5f7b1cf0df8e04803f9faed217cfeebe325e4d69c22a59e385e367a6
GET 请求示例中得到的待签名字符串如下:
CT-HMAC-SHA256
1678855875
2023-03-15/vss
d5df9af00882183ffb399dbfc6f4bbd24905efc965da026da8cabe1626217203
1.3 计算签名
计算派生签名密钥,伪代码如下:
SecretAccessKey = "PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****"
SecretDate = HMAC_SHA256("CT" + SecretAccessKey, Date)
SigningKey = HMAC_SHA256(SecretDate, "vss")
派生出的密钥 SecretDate 和 SigningKey 是二进制的数据,可能包含不可打印字符,此处不展示中间结果。
请注意,不同的编程语言,HMAC 库函数中参数顺序可能不一样,请以实际情况为准。此处的伪代码密钥参数 key 在前,消息参数 data 在后。通常标准库函数会提供二进制格式的返回值,也可能会提供打印友好的十六进制格式的返回值,此处使用的是二进制格式。
| 字段名称 | 解释 |
|---|---|
| SecretAccessKey | 原始的 SecretAccessKey,即 PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****。 |
| Date | 即 Credential 中的 Date 字段信息。此示例取值为 2022-02-24。 |
| vss | 即 Credential 中的 vss。 |
计算签名,伪代码如下:
Signature = HexEncode(HMAC_SHA256(SigningKey, StringToSign))
此示例创建设备请求的签名计算结果是 4d4683c13a3ab96a4a0b661c364afa8382c18b3968817ba0ab1a22c2825c3475
此示例查询设备请求的签名计算结果是 50c80b3b98c1e1f3e03eb6eafed541cf9f06714314151f0063ceeb61cbc1911f
1.4 拼接 Authorization
按如下格式拼接 Authorization:
Authorization =
Algorithm + ' ' +
'Credential=' + AccessKeyId + '/' + CredentialScope + ', ' +
'SignedHeaders=' + SignedHeaders + ', ' +
'Signature=' + Signature
| 字段名称 | 解释 |
|---|---|
| Algorithm | 签名方法,固定为 CT-HMAC-SHA256。 |
| AccessKeyId | 访问密钥对中的 AccessKeyId,即 8FR8VXACHFFQIT33****。 |
| CredentialScope | 见上文,凭证范围。此示例计算结果是 2022-02-24/vss。 |
| SignedHeaders | 见上文,参与签名的头部信息。此示例取值为 content-type;host;timestamp。 |
| Signature | 签名值。此示例计算结果是 4d4683c13a3ab96a4a0b661c364afa8382c18b3968817ba0ab1a22c2825c3475。 |
根据以上规则,POST 请求示例中得到的值为:
CT-HMAC-SHA256 Credential=8FR8VXACHFFQIT33****/2022-02-24/vss, SignedHeaders=content-type;host;timestamp, Signature=4d4683c13a3ab96a4a0b661c364afa8382c18b3968817ba0ab1a22c2825c3475
GET 请求示例中得到的值为:
CT-HMAC-SHA256 Credential=8FR8VXACHFFQIT33****/2023-03-15/vss, SignedHeaders=host;timestamp, Signature=50c80b3b98c1e1f3e03eb6eafed541cf9f06714314151f0063ceeb61cbc1911f
终完整的调用信息(POST)如下:
POST https://vssapi.ctyun.cn/devices
Authorization: CT-HMAC-SHA256 Credential=8FR8VXACHFFQIT33****/2022-02-24/vss, SignedHeaders=content-type;host;timestamp, Signature=4d4683c13a3ab96a4a0b661c364afa8382c18b3968817ba0ab1a22c2825c3475
Content-Type: application/json;charset=utf-8
Host: vssapi.ctyun.cn
Version: 2021-11-25
Timestamp: 1645679518
{"Region":"0851001","InNetworkType":"public","OutNetworkType":"public","Device":{"DeviceType":"ipc","DeviceVendor":"海康","DeviceName":"设备名称","DeviceChannelSize":1,"InOrgRegion":11000000,"InOrgRegionLevel":1,"DeviceIndustryCode":"54","DeviceNetworkCode":"7"},"Resource":{"Video":[{"BillingMode":"packages","ResourceId":"resource01"}]},"VideoDevice":{"Gb28181Device":{"InVersion":"2016","InUserName":"test","StreamTransProtocol":"tcp","AutoStartStream":true}}}
终完整的调用信息(GET)如下:
GET https://vssapi.ctyun.cn/devices/743780360209498112?IncludeDeviceDir=1&IncludeDeviceStats=0
Authorization: CT-HMAC-SHA256 Credential=8FR8VXACHFFQIT33****/2023-03-15/vss, SignedHeaders=host;timestamp, Signature=50c80b3b98c1e1f3e03eb6eafed541cf9f06714314151f0063ceeb61cbc1911f
Host: vssapi.ctyun.cn
Version: 2021-11-25
Timestamp: 1678855875
2. 签名演示
2.1 Java
POST 请求以创建设备为例。
package org.example;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.TreeMap;
public class ChinaTelecomCloudAPIDemo {
private final static Charset UTF8 = StandardCharsets.UTF_8;
private final static String AccessKeyId = "8FR8VXACHFFQIT33****";
private final static String SecretAccessKey = "PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****";
private final static String CT_JSON = "application/json;charset=utf-8";
public static byte[] hmac256(byte[] key, String msg) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
mac.init(secretKeySpec);
return mac.doFinal(msg.getBytes(UTF8));
}
public static String sha256Hex(String s) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] d = md.digest(s.getBytes(UTF8));
return DatatypeConverter.printHexBinary(d).toLowerCase();
}
public static void main(String[] args) throws Exception {
String service = "vss";
String host = "vssapi.ctyun.cn";
String version = "2021-11-25";
String algorithm = "CT-HMAC-SHA256";
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 注意时区,否则容易出错
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));
// ************* 步骤 1:拼接规范请求串 *************
String httpRequestMethod = "POST";
String canonicalUri = "/devices";
String canonicalQueryString = "";
String canonicalHeaders = "content-type:application/json;charset=utf-8\n" + "host:" + host + "\n" + "timestamp:" + timestamp + "\n";
String signedHeaders = "content-type;host;timestamp";
String payload = "{\"Region\":\"0851001\",\"InNetworkType\":\"public\",\"OutNetworkType\":\"public\",\"Device\":{\"DeviceType\":\"ipc\",\"DeviceVendor\":\"海康\",\"DeviceName\":\"设备名称\",\"DeviceChannelSize\":1,\"InOrgRegion\":11000000,\"InOrgRegionLevel\":1,\"DeviceIndustryCode\":\"54\",\"DeviceNetworkCode\":\"7\"},\"Resource\":{\"Video\":[{\"BillingMode\":\"packages\",\"ResourceId\":\"resource01\"}]},\"VideoDevice\":{\"Gb28181Device\":{\"InVersion\":\"2016\",\"InUserName\":\"test\",\"StreamTransProtocol\":\"tcp\",\"AutoStartStream\":true}}}";
String hashedRequestPayload = sha256Hex(payload);
String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
System.out.println(canonicalRequest);
// ************* 步骤 2:拼接待签名字符串 *************
String credentialScope = date + "/" + service;
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
System.out.println(stringToSign);
// ************* 步骤 3:计算签名 *************
byte[] secretDate = hmac256(("CT" + SecretAccessKey).getBytes(UTF8), date);
byte[] SigningKey = hmac256(secretDate, service);
String signature = DatatypeConverter.printHexBinary(hmac256(SigningKey, stringToSign)).toLowerCase();
System.out.println(signature);
// ************* 步骤 4:拼接 Authorization *************
String authorization = algorithm + " " + "Credential=" + AccessKeyId + "/" + credentialScope + ", "
+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
System.out.println(authorization);
TreeMap<String, String> headers = new TreeMap<String, String>();
headers.put("Authorization", authorization);
headers.put("Content-Type", CT_JSON);
headers.put("Host", host);
headers.put("Version", version);
headers.put("Timestamp", timestamp);
StringBuilder sb = new StringBuilder();
sb.append("curl -X POST https://").append(host).append(canonicalUri)
.append(" -H \"Authorization: ").append(authorization).append("\"")
.append(" -H \"Content-Type: application/json;charset=utf-8\"")
.append(" -H \"Host: ").append(host).append("\"")
.append(" -H \"Version: ").append(version).append("\"")
.append(" -H \"Timestamp: ").append(timestamp).append("\"")
.append(" -d '").append(payload).append("'");
System.out.println(sb.toString());
}
}
GET 请求以查询设备为例。
package org.example;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.TreeMap;
public class ChinaTelecomCloudAPIDemo {
private final static Charset UTF8 = StandardCharsets.UTF_8;
private final static String AccessKeyId = "8FR8VXACHFFQIT33****";
private final static String SecretAccessKey = "PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****";
public static byte[] hmac256(byte[] key, String msg) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
mac.init(secretKeySpec);
return mac.doFinal(msg.getBytes(UTF8));
}
public static String sha256Hex(String s) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] d = md.digest(s.getBytes(UTF8));
return DatatypeConverter.printHexBinary(d).toLowerCase();
}
public static void main(String[] args) throws Exception {
String service = "vss";
String host = "vssapi.ctyun.cn";
String version = "2021-11-25";
String algorithm = "CT-HMAC-SHA256";
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 注意时区,否则容易出错
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));
// ************* 步骤 1:拼接规范请求串 *************
String httpRequestMethod = "GET";
String canonicalUri = "/devices/743780360209498112";
String canonicalQueryString = "IncludeDeviceDir=1&IncludeDeviceStats=0";
String canonicalHeaders = "host:" + host + "\n" + "timestamp:" + timestamp + "\n";
String signedHeaders = "host;timestamp";
String payload = "";
String hashedRequestPayload = sha256Hex(payload);
String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
System.out.println(canonicalRequest);
// ************* 步骤 2:拼接待签名字符串 *************
String credentialScope = date + "/" + service;
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
System.out.println(stringToSign);
// ************* 步骤 3:计算签名 *************
byte[] secretDate = hmac256(("CT" + SecretAccessKey).getBytes(UTF8), date);
byte[] SigningKey = hmac256(secretDate, service);
String signature = DatatypeConverter.printHexBinary(hmac256(SigningKey, stringToSign)).toLowerCase();
System.out.println(signature);
// ************* 步骤 4:拼接 Authorization *************
String authorization = algorithm + " " + "Credential=" + AccessKeyId + "/" + credentialScope + ", "
+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
System.out.println(authorization);
TreeMap<String, String> headers = new TreeMap<String, String>();
headers.put("Authorization", authorization);
headers.put("Host", host);
headers.put("Version", version);
headers.put("Timestamp", timestamp);
StringBuilder sb = new StringBuilder();
sb.append("curl -X GET https://").append(host).append(canonicalUri).append("?").append(canonicalQueryString)
.append(" -H \"Authorization: ").append(authorization).append("\"")
.append(" -H \"Host: ").append(host).append("\"")
.append(" -H \"Version: ").append(version).append("\"")
.append(" -H \"Timestamp: ").append(timestamp).append("\"")
.append(" -d '").append(payload).append("'");
System.out.println(sb.toString());
}
}
2.2 Go
POST 请求以创建设备为例。
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
"strconv"
)
func sha256hex(s string) string {
b := sha256.Sum256([]byte(s))
return hex.EncodeToString(b[:])
}
func hmacsha256(s, key string) string {
hashed := hmac.New(sha256.New, []byte(key))
hashed.Write([]byte(s))
return string(hashed.Sum(nil))
}
func main() {
AccessKeyId := "8FR8VXACHFFQIT33****"
SecretAccessKey := "PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****"
host := "vssapi.ctyun.cn"
algorithm := "CT-HMAC-SHA256"
service := "vss"
version := "2021-11-25"
timestamp := time.Now().Unix()
// step 1: build canonical request string
httpRequestMethod := "POST"
canonicalURI := "/devices"
canonicalQueryString := ""
canonicalHeaders := "content-type:application/json;charset=utf-8\n" + "host:" + host + "\n" + "timestamp:" + strconv.FormatInt(timestamp, 10) + "\n"
signedHeaders := "content-type;host;timestamp"
payload := "{\"Region\":\"0851001\",\"InNetworkType\":\"public\",\"OutNetworkType\":\"public\",\"Device\":{\"DeviceType\":\"ipc\",\"DeviceVendor\":\"海康\",\"DeviceName\":\"设备名称\",\"DeviceChannelSize\":1,\"InOrgRegion\":11000000,\"InOrgRegionLevel\":1,\"DeviceIndustryCode\":\"54\",\"DeviceNetworkCode\":\"7\"},\"Resource\":{\"Video\":[{\"BillingMode\":\"packages\",\"ResourceId\":\"resource01\"}]},\"VideoDevice\":{\"Gb28181Device\":{\"InVersion\":\"2016\",\"InUserName\":\"test\",\"StreamTransProtocol\":\"tcp\",\"AutoStartStream\":true}}}"
hashedRequestPayload := sha256hex(payload)
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
httpRequestMethod,
canonicalURI,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
hashedRequestPayload)
fmt.Println(canonicalRequest)
// step 2: build string to sign
date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")
credentialScope := fmt.Sprintf("%s/%s", date, service)
hashedCanonicalRequest := sha256hex(canonicalRequest)
string2sign := fmt.Sprintf("%s\n%d\n%s\n%s",
algorithm,
timestamp,
credentialScope,
hashedCanonicalRequest)
fmt.Println(string2sign)
// step 3: sign string
secretDate := hmacsha256(date, "CT"+SecretAccessKey)
SigningKey := hmacsha256(service, secretDate)
signature := hex.EncodeToString([]byte(hmacsha256(string2sign, SigningKey)))
fmt.Println(signature)
// step 4: build authorization
authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
algorithm,
AccessKeyId,
credentialScope,
signedHeaders,
signature)
fmt.Println(authorization)
curl := fmt.Sprintf(`curl -X POST "https://%s"\
-H "Authorization: %s"\
-H "Content-Type: application/json;charset=utf-8"\
-H "Host: %s"\
-H "Version: %s"\
-H "Timestamp: %d"\
-d '%s'`, host+canonicalURI, authorization, host, version, timestamp, payload)
fmt.Println(curl)
}
GET 请求以查询设备为例。
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
"strconv"
)
func sha256hex(s string) string {
b := sha256.Sum256([]byte(s))
return hex.EncodeToString(b[:])
}
func hmacsha256(s, key string) string {
hashed := hmac.New(sha256.New, []byte(key))
hashed.Write([]byte(s))
return string(hashed.Sum(nil))
}
func main() {
AccessKeyId := "8FR8VXACHFFQIT33****"
SecretAccessKey := "PwbZMn5wEqXVrjt3L6QSdxYyOvllrfLPzLcR****"
host := "vssapi.ctyun.cn"
algorithm := "CT-HMAC-SHA256"
service := "vss"
version := "2021-11-25"
timestamp := time.Now().Unix()
// step 1: build canonical request string
httpRequestMethod := "GET"
canonicalURI := "/devices/743780360209498112"
canonicalQueryString := "IncludeDeviceDir=1&IncludeDeviceStats=0"
canonicalHeaders := "host:" + host + "\n" + "timestamp:" + strconv.FormatInt(timestamp, 10) + "\n"
signedHeaders := "host;timestamp"
payload := ""
hashedRequestPayload := sha256hex(payload)
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
httpRequestMethod,
canonicalURI,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
hashedRequestPayload)
fmt.Println(canonicalRequest)
// step 2: build string to sign
date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")
credentialScope := fmt.Sprintf("%s/%s", date, service)
hashedCanonicalRequest := sha256hex(canonicalRequest)
string2sign := fmt.Sprintf("%s\n%d\n%s\n%s",
algorithm,
timestamp,
credentialScope,
hashedCanonicalRequest)
fmt.Println(string2sign)
// step 3: sign string
secretDate := hmacsha256(date, "CT"+SecretAccessKey)
SigningKey := hmacsha256(service, secretDate)
signature := hex.EncodeToString([]byte(hmacsha256(string2sign, SigningKey)))
fmt.Println(signature)
// step 4: build authorization
authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
algorithm,
AccessKeyId,
credentialScope,
signedHeaders,
signature)
fmt.Println(authorization)
curl := fmt.Sprintf(`curl -X GET "https://%s"\
-H "Authorization: %s"\
-H "Host: %s"\
-H "Version: %s"\
-H "Timestamp: %d"\
-d '%s'`, host+canonicalURI+"?"+canonicalQueryString, authorization, host, version, timestamp, payload)
fmt.Println(curl)
}