Note:
- This document applies only to the COS XML version.
- 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.
- Requester Identity Authentication Verify the requester's identity through their unique ID and key.
- Transmitted data tampering prevention: Signs and verifies data to ensure the integrity of the transmitted content.
- Signature theft prevention: Sets a validity period for the signature to avoid its theft and reuse.
Preparations
- APPID, SecretId, and SecretKey:
Obtain them on the API Key Management page in the CAM console. - 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
- 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.
- Based on the preceding timestamp and the expected signature validity period, calculate the Unix timestamp EndTimestamp corresponding to the signature expiration time.
- Concatenate the signature validity time, in
StartTimestamp;EndTimestampformat, 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
- 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=. - Sort keys in KeyList in lexicographic order.
- Encode the keys in Map and KeyList using UrlEncode and convert them to lowercase.
- Concatenate each key-value pair in Map based on the order in KeyList, in
key1=value1&key2=value2&key3=value3format, that is, HttpParameters. - Concatenate each item in KeyList based on the order in KeyList, in
key1;key2;key3format, 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%2FNote:
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:
- 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=.... - 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&content-md5=mQ%2FfVh815F3k6TAUm8m0eg%3D%3D&content-type=text%2Fplain&date=Thu%2C%2016%20May%202019%2006%3A45%3A51%20GMT&host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn&x-cos-acl=private&x-cos-grant-read=uin%3D%22100000000011%22 - HttpString =
put\n/exampleobject(Converge Cloud)\n\ncontent-length=13&content-md5=mQ%2FfVh815F3k6TAUm8m0eg%3D%3D&content-type=text%2Fplain&date=Thu%2C%2016%20May%202019%2006%3A45%3A51%20GMT&host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn&x-cos-acl=private&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\nindicates 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&response-content-type=application%2Foctet-stream - HeaderList =
date;host - HttpHeaders =
date=Thu%2C%2016%20May%202019%2006%3A55%3A53%20GMT&host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn - HttpString =
get\n/exampleobject(Converge Cloud)\nresponse-cache-control=max-age%3D600&response-content-type=application%2Foctet-stream\ndate=Thu%2C%2016%20May%202019%2006%3A55%3A53%20GMT&host=examplebucket-1250000000.cos.city.yfm4.fsphere.cn\n - StringToSign =
sha1\n1557989753;1557996953\n54ecfe22f59d3514fdc764b87a32d8133ea611e6\n - Signature =
01681b8c9d798a678e43b685a9f1bba0f6c0e012\nindicates 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