Request Signing

Last Updated At: 2025-10-16 11:20:40

Note:

  1. This document applies only to the COS XML version.
  2. This document does not apply to HTTP requests for POST objects.

When using COS, you can initiate HTTP anonymous requests or HTTP signed requests to COS through the RESTful API. For signed requests, the COS server verifies the identity of the requester.

  • Anonymous Request: An HTTP request that doesn't carry any identity information or authentication credentials, performed through RESTful API operations.
  • Signed request: An HTTP request is added with a signature when it is sent. After receiving the message, the COS server verifies the identity. If verification is successful, it accepts and executes the request. Otherwise, it returns an error information and discards the request.

COS verifies the identity based on a custom solution of Hash Message Authentication Code (HMAC).

Use Cases for Request Signing

If a COS object needs to be published to the public, you will usually need to set this object to public-read/private-write (everyone can read the object but only the ACL-specified account can write to it). After this, you can use the ACL policy together with the API request signature to authenticate requesters and control the permissions and validity period of operations.

Note:

The API request signing described in this document is already included if you're developing using an SDK. Only when you want to perform secondary development through the original API, follow the steps described in this document.

In the preceding scenario, various security measures can be applied to API requests.

  1. Requester Identity Authentication Verify the requester's identity through their unique ID and key.
  2. Transmitted data tampering prevention: Signs and verifies data to ensure the integrity of the transmitted content.
  3. Signature theft prevention: Sets a validity period for the signature to avoid its theft and reuse.

Preparations

  1. APPID, SecretId, and SecretKey:
    Obtain them on the API Key Management page in the CAM console.
  2. Development language:
    Supported languages include but are not limited to Java, PHP, C#, C++, Node.js, and Python. Determine the HMAC-SHA1, SHA1, and UrlEncode functions based on the development language.
    The HMAC-SHA1 and SHA1 functions use UTF-8 encoded strings as inputs and hexadecimal lowercase strings as outputs. UrlEncode is based on UTF-8 encoding. For printable characters within the ASCII range, the special symbols listed in the following table should also be encoded.
Character Decimal Hexadecimal Character Decimal Hexadecimal
(Space) 32 20 ; 59 3B
! 33 21 < 60 3C
" 34 22 = 61 3D
# 35 23 > 62 3E
$ 36 24 ? 63 3F
% 37 25 @ 64 40
& 38 26 [ 91 5B
' 39 27 \ 92 5C
( 40 28 ] 93 5D
) 41 29 ^ 94 5E
* 42 2A ` 96 60
+ 43 2B { 123 7B
, 44 2C | 124 7C
/ 47 2F } 125 7D
: 58 3A

Signing Process

Generating KeyTime

  1. Obtain the Unix timestamp StartTimestamp corresponding to the current time. The Unix timestamp counts the total number of seconds from 00:00:00 on January 1, 1970 (UTC or GMT time, that is, 08:00:00 on January 1, 1970, Manila time) to the present.
  2. Based on the preceding timestamp and the expected signature validity period, calculate the Unix timestamp EndTimestamp corresponding to the signature expiration time.
  3. Concatenate the signature validity time, in StartTimestamp;EndTimestamp format, that is, KeyTime.
    Example: 1557902800;1557910000

Generating SignKey

Use HMAC-SHA1 with SecretKey as the key and KeyTime as the message to calculate the message digest (hash value), that is, SignKey.
Example: 36bcd76dbb8c9f066472fec403df8a34cab34c77

Generating UrlParamList and HttpParameters

  1. Traverse HTTP request parameters to generate Map (mapping from keys to values) and KeyList (list of keys). Keys are converted to lowercase, and values are encoded using UrlEncode.
    For parameters without values, the values are regarded as null strings. For example, if the request path is /?acl, it is presumed to be /?acl=.
  2. Sort keys in KeyList in lexicographic order.
  3. Encode the keys in Map and KeyList using UrlEncode and convert them to lowercase.
  4. Concatenate each key-value pair in Map based on the order in KeyList, in key1=value1&key2=value2&key3=value3 format, that is, HttpParameters.
  5. Concatenate each item in KeyList based on the order in KeyList, in key1;key2;key3 format, that is, UrlParamList.
    Example:
  • Example 1:
    Request path: /?prefix=example-folder%2F&delimiter=%2F&max-keys=10
    UrlParamList: delimiter;max-keys;prefix
    HttpParameters: delimiter=%2F&max-keys=10&prefix=example-folder%2F

    Note:

    When requests are sent, the request parameters in the request path are also encoded using UrlEncode. Therefore, do not perform UrlEncode multiple times.

  • Example 2:
    Request path: /exampleobject?acl
    UrlParamList: acl
    HttpParameters: acl=

Generating HeaderList and HttpHeaders

Generating HeaderList and HttpHeaders follows the same process as generating UrlParamList and HttpParameters, except that HTTP request parameters are replaced with HTTP request headers. HttpParameters become HttpHeaders, and UrlParamList becomes HeaderList.

Example:
Request header:

Date: Thu, 16 May 2019 03:15:06 GMT
Host: <BucketName-APPID>.<Endpoint>
x-cos-acl: private
x-cos-grant-read: uin="100000000011"

The calculated HeaderList is date;host;x-cos-acl;x-cos-grant-read, and HttpHeaders is date=Thu%2C%2016%20May%202019%2003%3A15%3A06%20GMT&host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn&x-cos-acl=private&x-cos-grant-read=uin%3D%22100000000011%22.

Generating HttpString

Generate HttpString based on the HTTP method, HTTP request path, HttpParameters, and HttpHeaders, in HttpMethod\nUriPathname\nHttpParameters\nHttpHeaders\n format.
HttpMethod is converted to lowercase, such as get or put. UriPathname is the request path, such as / or /exampleobject. \n is a line break. If any string is empty, the beginning and last line breaks should be retained, such as get\n/exampleobject\n\n\n.

Generating StringToSign

Generate StringToSign based on KeyTime and HttpString, in sha1\nKeyTime\nSHA1(HttpString)\n format.
SHA1(HttpString) is the message digest calculated by using SHA1 on HttpString.

Generating Signature

Use HMAC-SHA1 with SignKey as the key and StringToSign as the message to calculate the message digest, that is, Signature.

Generating Signature

Generate the signature based on SecretId, KeyTime, HeaderList, UrlParamList, and Signature, in the following format:

q-sign-algorithm=sha1
&q-ak=SecretId
&q-sign-time=KeyTime
&q-key-time=KeyTime
&q-header-list=HeaderList
&q-url-param-list=UrlParamList
&q-signature=Signature

Note:

Line breaks in the preceding format are for easier reading only and are not included in the actual format.

Using a Signature

Signed HTTP requests sent to COS through the RESTful API can pass the signature in the following ways:

  1. Include the signature in the standard HTTP Authorization header, such as Authorization: q-sign-algorithm=sha1&q-ak=...&q-sign-time=1557989753;1557996953&...&q-signature=....
  2. Use the signature as an HTTP request parameter (note UrlEncode), such as /exampleobject?q-sign-algorithm=sha1&q-ak=...&q-sign-time=1557989753%3B1557996953&...&q-signature=....

    Note:

    In the preceding examples, some specific signature content is omitted in ....

Sample Code

Pseudocode

KeyTime = [Now];[Expires]
SignKey = HMAC-SHA1([SecretKey], KeyTime)
HttpString = [HttpMethod]\n[HttpURI]\n[HttpParameters]\n[HttpHeaders]\n
StringToSign = sha1\nKeyTime\nSHA1(HttpString)\n
Signature = HMAC-SHA1(SignKey, StringToSign)

Example Message Digest Algorithms

The following examples describe how to call HMAC-SHA1 in different languages.

PHP

$sha1HttpString = sha1('ExampleHttpString');

$signKey = hash_hmac('sha1', 'ExampleKeyTime', 'YourSecretKey');

Java

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

String sha1HttpString = DigestUtils.sha1Hex("ExampleHttpString");

String signKey = HmacUtils.hmacSha1Hex("YourSecretKey", "ExampleKeyTime");

Python

import hmac
import hashlib

sha1 = hashlib.sha1()
sha1_http_string = sha1.update('ExampleHttpString'.encode('utf-8')).hexdigest()

sign_key = hmac.new('YourSecretKey'.encode('utf-8'), 'ExampleKeyTime'.encode('utf-8'), hashlib.sha1).hexdigest()

Node.Js

var crypto = require('crypto');

var sha1HttpString = crypto.createHash('sha1').update('ExampleHttpString').digest('hex');
var signKey = crypto.createHmac('sha1', 'YourSecretKey').update('ExampleKeyTime').digest('hex');

Go

import (
    "crypto/hmac"
    "crypto/sha1"
)

h := sha1.New()
h.Write([]byte("ExampleHttpString"))
sha1HttpString := h.Sum(nil)

var hashFunc = sha1.New
h = hmac.New(hashFunc, []byte("YourSecretKey"))
h.Write([]byte("ExampleKeyTime"))
signKey := h.Sum(nil)

Practical Case

Preparations

Log in to the CAM console and go to the API key management page to obtain the APPID, SecretId, and SecretKey. The following table describes an example.

APPID SecretId SecretKey
1250000000 AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx BQYIM75p8x0iWVFSIgqEKwFprpRSVHlz

Uploading an Object

Original Request

PUT /exampleobject(%E8%85%BE%E8%AE%AF%E4%BA%91) HTTP/1.1
Date: Thu, 16 May 2019 06:45:51 GMT
Host: <BucketName-APPID>.<Endpoint>
Content-Type: text/plain
Content-Length: 13
Content-MD5: mQ/fVh815F3k6TAUm8m0eg==
x-cos-acl: private
x-cos-grant-read: uin="100000000011"

ObjectContent

Intermediate Variables

  • KeyTime = 1557989151;1557996351
  • SignKey = eb2519b498b02ac213cb1f3d1a3d27a3b3c9bc5f
  • UrlParamList = (empty string)
  • HttpParameters = (empty string)
  • HeaderList = content-length;content-md5;content-type;date;host;x-cos-acl;x-cos-grant-read
  • HttpHeaders = content-length=13&amp;content-md5=mQ%2FfVh815F3k6TAUm8m0eg%3D%3D&amp;content-type=text%2Fplain&amp;date=Thu%2C%2016%20May%202019%2006%3A45%3A51%20GMT&amp;host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn&amp;x-cos-acl=private&amp;x-cos-grant-read=uin%3D%22100000000011%22
  • HttpString = put\n/exampleobject(Converge Cloud)\n\ncontent-length=13&amp;content-md5=mQ%2FfVh815F3k6TAUm8m0eg%3D%3D&amp;content-type=text%2Fplain&amp;date=Thu%2C%2016%20May%202019%2006%3A45%3A51%20GMT&amp;host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn&amp;x-cos-acl=private&amp;x-cos-grant-read=uin%3D%22100000000011%22\n
  • StringToSign = sha1\n1557989151;1557996351\n8b2751e77f43a0995d6e9eb9477f4b685cca4172\n
  • Signature = 3b8851a11a569213c17ba8fa7dcf2abec6935172
    (empty string) indicates a string of length 0, and \n indicates a line break.

Signed Request

PUT /exampleobject(%E8%85%BE%E8%AE%AF%E4%BA%91) HTTP/1.1
Date: Thu, 16 May 2019 06:45:51 GMT
Host: <BucketName-APPID>.<Endpoint>
Content-Type: text/plain
Content-Length: 13
Content-MD5: mQ/fVh815F3k6TAUm8m0eg==
x-cos-acl: private
x-cos-grant-read: uin="100000000011"
Authorization: q-sign-algorithm=sha1&q-ak=AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&q-sign-time=1557989151;1557996351&q-key-time=1557989151;1557996351&q-header-list=content-length;content-md5;content-type;date;host;x-cos-acl;x-cos-grant-read&q-url-param-list=&q-signature=3b8851a11a569213c17ba8fa7dcf2abec6935172

ObjectContent

Downloading an Object

Original Request

GET /exampleobject(%E8%85%BE%E8%AE%AF%E4%BA%91)?response-content-type=application%2Foctet-stream&response-cache-control=max-age%3D600 HTTP/1.1
Date: Thu, 16 May 2019 06:55:53 GMT
Host: <BucketName-APPID>.<Endpoint>

Intermediate Variables

  • KeyTime = 1557989753;1557996953
  • SignKey = 937914bf490e9e8c189836aad2052e4feeb35eaf
  • UrlParamList = response-cache-control;response-content-type
  • HttpParameters = response-cache-control=max-age%3D600&amp;response-content-type=application%2Foctet-stream
  • HeaderList = date;host
  • HttpHeaders = date=Thu%2C%2016%20May%202019%2006%3A55%3A53%20GMT&amp;host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn
  • HttpString = get\n/exampleobject(Converge Cloud)\nresponse-cache-control=max-age%3D600&amp;response-content-type=application%2Foctet-stream\ndate=Thu%2C%2016%20May%202019%2006%3A55%3A53%20GMT&amp;host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn\n
  • StringToSign = sha1\n1557989753;1557996953\n54ecfe22f59d3514fdc764b87a32d8133ea611e6\n
  • Signature = 01681b8c9d798a678e43b685a9f1bba0f6c0e012
    \n indicates a line break.

Signed Request

GET /exampleobject(%E8%85%BE%E8%AE%AF%E4%BA%91)?response-content-type=application%2Foctet-stream&response-cache-control=max-age%3D600 HTTP/1.1
Date: Thu, 16 May 2019 06:55:53 GMT
Host: <BucketName-APPID>.<Endpoint>
Authorization: q-sign-algorithm=sha1&q-ak=AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&q-sign-time=1557989753;1557996953&q-key-time=1557989753;1557996953&q-header-list=date;host&q-url-param-list=response-cache-control;response-content-type&q-signature=01681b8c9d798a678e43b685a9f1bba0f6c0e012