Signature v3

Last updated: 2019-08-15 23:35:18

Tencent Cloud API authenticates each access request, i.e. each request needs to add authentication information (Signature) to the common parameters that the API can verify the identity of the requester.

The Signature is generated by the security credentials (SecretId and SecretKey) and is required for making API calls. You may apply your security credentials here Cloud API Key.

Applying for Security Credentials

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

  • SecretId: for identifying the API requester.
  • SecretKey: for encrypting the string to sign that can be verified at the endpoint.

  • Do not disclose your security credential information to a third party

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

  1. Log in to Tencent Cloud Management Center Console.
  2. Go to the Cloud API Key page
  3. On the Cloud API Key page, click [New] to create a pair of SecretId/SecretKey

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

TC3-HMAC-SHA256 Algorithm

Note: For the GET method, the only supported protocol is Content-Type: application/x-www-form-urlencoded.

When the method is POST, two kinds of protocols are supported: Content-Type: application/json, Content-Type: multipart/form-data. By default, all APIs support JSON, while only some specific APIs support multipart, to which the requests cannot be sent in JSON format. For details, see the specific API documentation.

Example: The following example describes how to create a signature and add it to an HTTP request to query the list of CVM instances in the Guangzhou region.

Use two parameters (Limit and Offset) for querying a list of CVM instances with the Get HTTP method. SecretId: AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE; SecretKey: Gu5t9xGARNpq86cd98joQYCN3EXAMPLE.

1. Creating a Canonical Request

The Canonical Request is formatted as below:

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: Canonical URI parameter. Always use a forward slash.
  • CanonicalQueryString: Add the canonical query string to HTTP request URL. A POST request uses an empty string. For a GET request, add the canonical query string followed by a question mark (?) in the URL. In this example: Limit=10&Offset=0. Note: Canonical Query Strings must be URL encoded.
  • CanonicalHeaders: Add the canonical headers that contain a list of all the HTTP headers that you are including with the signed request, and must include at least two headers: host and content-type. You can also customize your HTTP headers to improve security. Construct a canonical headers list. 1) convert all header names (keys) and values to lowercase and remove leading spaces and trailing spaces, and append a new line such as key:value\n. 2) if there are multiple headers, build the canonical headers list by sorting the lowercase header names ( keys) in lexicographical order. Example: content-type:application/x-www-form-urlencoded\nhost:cvm.tencentcloudapi.com\n
  • SignedHeaders: IAdd the signed headers, a list of headers that you included in the canonical headers to tell Tencent Cloud API which headers in the request are part of the signing process. Content-type and host are required headers.

Construct a signed headers list: convert all header names (keys) to lowercase, if there is more than one header, sort the header names (keys) in lexicographical order, and use a semicolon to separate them. Example: content-type;host

  • HashedRequestPayload: Create a hashed value from the payload in the request body with a hash function, for example: Lowercase(HexEncode(Hash.SHA256(RequestPayload))). The hashed payload must be represented as a lowercase hexadecimal string. Note: In a GET request, the payload is empty; in a POST request, RequestPlayload is the payload in the body of the HTTP request. Following the steps above, you create a canonical request as shown below (newline characters (' \n') are removed in this example. Instead, we add new lines for a clearer layout).
GET
/
Limit=10&Offset=0
content-type:application/x-www-form-urlencoded
host:cvm.api.tencentyun.com

content-type;host
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

2. Creating a String to Sign

Construct a string to sign in the following format:

StringToSign =
    Algorithm + \n +
    RequestTimestamp + \n +
    CredentialScope + \n +
    HashedCanonicalRequest
  • Algorithm: authentication algorithm. TC3-HMAC-SHA256 (fixed);
  • RequestTimestamp: Timestamp of the request, i.e., the value of the X-TC-Timestamp in the request header; 1539084154 as shown in the example above;
  • 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. As shown in the example request above, the value is 2018-10-09/cvm/tc3_request;
  • HashedCanonicalRequest: Hash value of the CanonicalRequest string created earlier, calculated as 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 a clear display, the \n line break is replaced by adding a new line):

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

3. Calculating the Signature

1) Derive singing key, as shown in 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; 2018-10-09 as shown in the example above;
  • Service: The Service field information in Credential; cvm as shown in the example above;

2) Derive singing key, as shown in the following pseudocode:

Signature = HexEncode(HMAC_SHA256(SecretSigning, StringToSign))
  • SecretSigning: The derived singing key;
  • StringToSign: The string to sign calculated in step 2;

4. Passing Signing Information In the Authorization Header

The Authorization header is constructed in the following format:

Authorization =
    Algorithm + ' ' +
    'Credential=' + SecretId + '/' + CredentialScope + ', ' +
    'SignedHeaders=' + SignedHeaders + ', '
    'Signature=' + Signature
  • Algorithm: Authentication algorithm: C3-HMAC-SHA256 (fixed);
  • SecretId: The SecretId in the key pair;
  • CredentialScope: Scope of the credential (see above);
  • SignedHeaders: headers in the request are part of the signing process (see above);
  • Signature: Calculated Signature ;

Following the steps above, the resulting values for this example are:

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

The following example shows a finished authorization header:

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. Pseudocodes for Creating Signature

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: Splice 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: Splice the string to be signed *************
        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: Calculate the 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: Splice the Authorization *************
        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: Splice the string to be signed *************
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: Calculate the signature *************
# Calculate 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: Splice the Authorization *************
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,
}

Failed to Sign the Request

The following error codes indicate unsuccessful request signing processes.

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