Signature v3

Last updated: 2019-09-09 21:30:36

TencentCloud API authenticates each access request, i.e. each request needs to include signature information (Signature) in the common request parameters to verify the identity of the requester. The Signature is generated by the security credentials which include SecretId and SecretKey. If you don't have the security credentials yet, please go to the TencentCloud API Key page to apply; otherwise, you cannot call the TencentCloud API.

Applying for Security Credentials

Before using the TencentCloud API for the first time, go to the TencentCloud API Key page to apply for security credentials. Security credentials include SecretId and SecretKey:

  • SecretId is used to identify the API caller.
  • SecretKey is used to encrypt the signature and verify it on the server. *You must keep your security credentials private and avoid disclosure.

You can apply for the security credentials in the following steps:

  1. Log in to Tencent Cloud Console.
  2. Go to the TencentCloud API Key page
  3. On the TencentCloud API Key page, click Create to create a pair of SecretId/SecretKey

Note: A developer account can have up to two pairs of SecretId/SecretKey.

TC3-HMAC-SHA256 Signature Method

Note: For the GET method, only the Content-Type: application/x-www-form-urlencoded protocol format is supported. For the POST method, two protocol formats, Content-Type: application/json and Content-Type: multipart/form-data, are supported. The JSON format is supported by default for all services APIs, and the multipart format is supported only for specific service APIs. In this case, the API cannot be called in JSON format. See the specific service API documentation for more information.

The following uses querying the list of CVM instances in the Guangzhou region as an example to describe the steps of signature splicing. We use only two parameters of the instance list querying API in a GET request, i.e., Limit and Offset.

Assume that the SecretId and SecretKey are: AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE and Gu5t9xGARNpq86cd98joQYCN3EXAMPLE

1. Concatenating the CanonicalRequest String

Concatenate the canonical request string (CanonicalRequest) in the following format:

CanonicalRequest =
    HTTPRequestMethod + '\n' +
    CanonicalURI + '\n' +
    CanonicalQueryString + '\n' +
    CanonicalHeaders + '\n' +
    SignedHeaders + '\n' +
    HashedRequestPayload
  • HTTPRequestMethod: HTTP request method (GET or POST); GET is used in this example.
  • CanonicalURI: URI parameter. Slash (/) is used for API 3.0.
  • CanonicalQueryString: Query string in the URL of the originating HTTP request. It is always an empty string for the POST request, and the string after the question mark (?) in URL for the GET request, such as Limit=10&Offset=0 in this example. Note: CanonicalQueryString must be URL-encoded. CanonicalHeaders: header information for signature, including at least the host and content-type headers. Custom header for signature can also be added to improve the uniqueness and security of the request. Concatenating rules: 1) The key and value headers are converted to lowercase, and the spaces at the start and end are removed. Concatenate ​in the format of key:value\n. 2) Multiple headers are concatenated in the lexicographic order of the header key (lowercase). content-type:application/x-www-form-urlencoded\nhost:cvm.tencentcloudapi.com\n is used in this example.
  • SignedHeaders: Header information for signature calculation. It specifies the headers involved in the signature for the request, which correspond to the header content contained in CanonicalHeaders. The content-type and host headers are required. Concatenating rules: 1. The header key is converted to lowercase; 2. Multiple headers are concatenated in the lexicographical order of the header keys (lowercase) and separated by semicolons (;). content-type;host is used in this example.
  • HashedRequestPayload: Hash value of the request body, which is computed by Lowercase(HexEncode(Hash.SHA256(RequestPayload))). After the SHA256 hash on the entire body payload of the HTTP request is computed, the result is encoded in hexadecimal format, and then the encoded string is converted into lowercase. Note: For a GET request, RequestPayload must be an empty string; for a POST request, RequestPayload is the payload of the HTTP request.

According to the rules above, the CanonicalRequest string obtained in the example is as follows (for clarity, "\n" is removed by starting a new line.):

GET
/
Limit=10&Offset=0
content-type:application/x-www-form-urlencoded
host:cvm.api.tencentyun.com

content-type;host
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

2. Concatenating the String to Sign

The string to sign is concatenated in the following format:

StringToSign =
    Algorithm + \n +
    RequestTimestamp + \n +
    CredentialScope + \n +
    HashedCanonicalRequest
  • Algorithm: Signature algorithm, which is always TC3-HMAC-SHA256 currently.
  • RequestTimestamp: Timestamp of the request, i.e., the value of the X-TC-Timestamp in the request header, such as 1539084154 the above example.
  • CredentialScope: Scope of the credential in the format of Date/service/tc3_request, including the date, requested service, and termination string (tc3_request). Date is a date in UTC time, whose value should match the UTC date converted by the common parameter X-TC-Timestamp. service is the product name, which should match the domain name of the product called, such as cvm. The value in the above example is 2018-10-09/cvm/tc3_request;
  • HashedCanonicalRequest: Hash value of the CanonicalRequest string concatenated in the steps above, which is calculated by Lowercase(HexEncode(Hash.SHA256(CanonicalRequest))).

Note:

  1. Date has to be calculated from the timestamp "X-TC-Timestamp" and the time zone is UTC+0. If you add the system's local time zone information (such as UTC+8), calls can succeed in the daytime and night but will definitely fail at 00:00. For example, if the timestamp is 1551113065 and the time in UTC+8 is 2019-02-26 00:44:25, the UTC+0 date in the calculated Date value should be 2019-02-25 instead of 2019-02-26.
  2. Timestamp must be the current system time, and it should be ensured that the system time and standard time are synced; if the difference is over five minutes, the call will definitely fail. If the time difference exists for a long time, it may cause the requests to definitely fail after running for a period of time (with a signature expiration error returned).

According to the rules above, the string to be signed obtained in the example is as follows (for clarity, "\n" is removed by starting a new line):

TC3-HMAC-SHA256
1539084154
2018-10-09/cvm/tc3_request
91c9c192c14460df6c1ffc69e34e6c5e90708de2a6d282cccf957dbf1aa7f3a7

3. Calculating the Signature

1) Calculate the derived signature key with the following pseudocode:

SecretKey = "Gu5t9xGARNpq86cd98joQYCN3EXAMPLE"
SecretDate = HMAC_SHA256("TC3" + SecretKey, Date)
SecretService = HMAC_SHA256(SecretDate, Service)
SecretSigning = HMAC_SHA256(SecretService, "tc3_request")
  • SecretKey: The original SecretKey;
  • Date: The Date field information in Credential; such as 2018-10-09 in the above example;
  • Service: The Service field information in Credential; such as cvm in the above example;

2) Compute the signature with the following pseudocode:

Signature = HexEncode(HMAC_SHA256(SecretSigning, StringToSign))
  • SecretSigning: The derived signature key obtained by the above computation;
  • StringToSign: The string to be signed obtained by the computation in step 2;

4. Concatenating the Authorization

The Authorization is concatenated in the following format:

Authorization =
    Algorithm + ' ' +
    'Credential=' + SecretId + '/' + CredentialScope + ', ' +
    'SignedHeaders=' + SignedHeaders + ', ' +
    'Signature=' + Signature
  • Algorithm: Signature algorithm, which is always TC3-HMAC-SHA256;
  • SecretId: The SecretId in the key pair;
  • CredentialScope: Scope of the credential (see above);
  • SignedHeaders: Header information for signature computation (see above);
  • Signature: Signature value;

Based on the rules above, the value in the example is:

TC3-HMAC-SHA256 Credential=AKIDEXAMPLE/Date/service/tc3_request, SignedHeaders=content-type;host, Signature=5da7a33f6993f0614b047e5df4582db9e9bf4672ba50567dba16c6ccf174c474

The final complete call information is as follows:

https://cvm.tencentcloudapi.com/?Limit=10&Offset=0

Authorization: TC3-HMAC-SHA256 Credential=AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE/2018-10-09/cvm/tc3_request, SignedHeaders=content-type;host, Signature=5da7a33f6993f0614b047e5df4582db9e9bf4672ba50567dba16c6ccf174c474
Content-Type: application/x-www-form-urlencoded
Host: cvm.tencentcloudapi.com
X-TC-Action: DescribeInstances
X-TC-Version: 2017-03-12
X-TC-Timestamp: 1539084154
X-TC-Region: ap-guangzhou

5. Signature Demo

Java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.codec.digest.DigestUtils;

public class TencentCloudAPITC3Demo {
    private final static String CHARSET = "UTF-8";
    private final static String ENDPOINT = "cvm.tencentcloudapi.com";
    private final static String PATH = "/";
    private final static String SECRET_ID = "AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE";
    private final static String SECRET_KEY = "Gu5t9xGARNpq86cd98joQYCN3EXAMPLE";
    private final static String CT_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
    private final static String CT_JSON = "application/json";
    private final static String CT_FORM_DATA = "multipart/form-data";

    public static byte[] sign256(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(CHARSET));
    }

    public static void main(String[] args) throws Exception {
        String service = "cvm";
        String host = "cvm.tencentcloudapi.com";
        String region = "ap-guangzhou";
        String action = "DescribeInstances";
        String version = "2017-03-12";
        String algorithm = "TC3-HMAC-SHA256";
        String timestamp = "1539084154";
        //String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // Pay attention to the time zone; otherwise, errors may occur
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* Step 1: Concatenate the CanonicalRequest string *************
        String httpRequestMethod = "GET";
        String canonicalUri = "/";
        String canonicalQueryString = "Limit=10&Offset=0";
        String canonicalHeaders = "content-type:application/x-www-form-urlencoded\n" + "host:" + host + "\n";
        String signedHeaders = "content-type;host";
        String hashedRequestPayload = DigestUtils.sha256Hex("");
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println(canonicalRequest);

        // ************* Step 2: Concatenate String to Sign *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = DigestUtils.sha256Hex(canonicalRequest.getBytes(CHARSET));
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println(stringToSign);

        // ************* Step 3: Compute Signature *************
        byte[] secretDate = sign256(("TC3" + SECRET_KEY).getBytes(CHARSET), date);
        byte[] secretService = sign256(secretDate, service);
        byte[] secretSigning = sign256(secretService, "tc3_request");
        String signature = DatatypeConverter.printHexBinary(sign256(secretSigning, stringToSign)).toLowerCase();
        System.out.println(signature);

        // ************* Step 4: Concatenate Authorization Headers *************
        String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + 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("Content-Type", CT_X_WWW_FORM_URLENCODED);
        headers.put("X-TC-Action", action);
        headers.put("X-TC-Timestamp", timestamp);
        headers.put("X-TC-Version", version);
        headers.put("X-TC-Region", region);
    }
}

Python

# -*- coding: utf-8 -*-
import hashlib, hmac, json, os, sys, time
from datetime import datetime

# Key parameters
secret_id = "AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE"
secret_key = "Gu5t9xGARNpq86cd98joQYCN3EXAMPLE"

service = "cvm"
host = "cvm.tencentcloudapi.com"
endpoint = "https://" + host
region = "ap-guangzhou"
action = "DescribeInstances"
version = "2017-03-12"
algorithm = "TC3-HMAC-SHA256"
timestamp = 1539084154
date = datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d")
params = {"Limit": 10, "Offset": 0}

# ************* Step 1: Splice the CanonicalRequest string *************
http_request_method = "GET"
canonical_uri = "/"
canonical_querystring = "Limit=10&Offset=0"
ct = "x-www-form-urlencoded"
payload = ""
if http_request_method == "POST":
    canonical_querystring = ""
    ct = "json"
    payload = json.dumps(params)
canonical_headers = "content-type:application/%s\nhost:%s\n" % (ct, host)
signed_headers = "content-type;host"
hashed_request_payload = hashlib.sha256(payload.encode("utf-8")).hexdigest()
canonical_request = (http_request_method + "\n" +
                     canonical_uri + "\n" +
                     canonical_querystring + "\n" +
                     canonical_headers + "\n" +
                     signed_headers + "\n" +
                     hashed_request_payload)
print(canonical_request)

# ************* Step 2: Concatenate String to Sign *************
credential_scope = date + "/" + service + "/" + "tc3_request"
hashed_canonical_request = hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
string_to_sign = (algorithm + "\n" +
                  str(timestamp) + "\n" +
                  credential_scope + "\n" +
                  hashed_canonical_request)
print(string_to_sign)

# ************* Step 3: Compute Signature *************
# Compute the signature summary function
def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
secret_date = sign(("TC3" + secret_key).encode("utf-8"), date)
secret_service = sign(secret_date, service)
secret_signing = sign(secret_service, "tc3_request")
signature = hmac.new(secret_signing, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
print(signature)

# ************* Step 4: Concatenate Authorization Headers *************
authorization = (algorithm + " " +
                 "Credential=" + secret_id + "/" + credential_scope + ", " +
                 "SignedHeaders=" + signed_headers + ", " +
                 "Signature=" + signature)
print(authorization)

# Add the common parameters to the request header
headers = {
    "Authorization": authorization,
    "Host": host,
    "Content-Type": "application/%s" % ct,
    "X-TC-Action": action,
    "X-TC-Timestamp": str(timestamp),
    "X-TC-Version": version,
    "X-TC-Region": region,
}

Signature Failure

The following error codes for signature failure exist based on the actual conditions. Please cope with the errors accordingly.

Error code Error description
AuthFailure.SignatureExpire Signature expired
AuthFailure.SecretIdNotFound The key does not exist
AuthFailure.SignatureFailure Signature error
AuthFailure.TokenFailure Token error
AuthFailure.InvalidSecretId Invalid key (not TencentCloud API key type)