Skip to content
yami edited this page Feb 17, 2017 · 3 revisions

Table Of Contents

OSS Signature Version 2

Compared with the previous signature version, OSS Signature version 2 has the following improvements:

  • In OSS Signature Version 2, it uses SHA256 algorithm instead of SHA1 to sign the request, which has higher security.
  • OSS Signature Version 2 includes all HTTP query parameters in signature calculation.

In following sections, we will describe the details of Signature Version 2. There are three functions that will be used in later sections:

  • UriEncode to percentage encode a string
  • Lowercase to convert a string to lower case
  • Trim to return a copy of the input string with leading and trailing whitespaces removed

Below is a python implementation for UriEncode with input type of str:

def UriEncode(raw_text):
   """raw_text is of type str."""

    res = ''
    for c in raw_text:
        if (c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z')\
            or (c >= '0' and c <= '9') or c in ['_', '-', '~', '.']:
            res += c
        else:
            res += "%{0:02X}".format(ord(c))

    return res

Constructing an Authorization Header

An authorization header in Signature Version 2 has following form

Authorization: OSS2 AccessKeyId:<AccessKeyId>,Signature:<Signature>[,AdditionalHeaders:<AdditionalHeaders>]

where

  • OSS2 specifies the signature version
  • <AccessKeyId> is the AccessKeyId
  • <AdditionalHeaders> are headers included in signature calculation besides those prefixed by x-oss-. It is a list of header names delimited by semi-colon. This field is optional.
  • <Signature> is the calculated signature, which will be verified by OSS.

There should be at least one space after OSS2, and following OSS2 are comma separated key:value list. Value of Authorization header is case sensitive.

A sample authorization header is

authorization: OSS2 AccessKeyId:44CF9590006BF252F707,AdditionalHeaders:range;if-modified-since,Signature:YG9mKO3m4S0Jx9Hk6Lq64VchJg/TOTkyCX4DaeeOYxE=

where

  • <AccessKeyId> is 44CF9590006BF252F707
  • AdditionalHeaders is range;if-modified-since
  • Signature is YG9mKO3m4S0Jx9Hk6Lq64VchJg/TOTkyCX4DaeeOYxE=

Calculating a Signature

Following pseudo code illustrates how to calculate a signature

StringToSign = 
            VERB + "\n"
            + Content-MD5 + "\n" 
            + Content-Type + "\n" 
            + Date + "\n" 
            + CanonicalizedOSSHeaders
            + AdditionalHeaders + "\n"
            + CanonicalizedResource
            
Signature = base64(hmac-sha256(AccessKeySecret, StringToSign))

where

  • VERB is one of the HTTP methods, for example GET, PUT, HEAD, DELETE and POST.
  • \n is the newline character
  • Content-MD5 is the value of request's Content-MD5 header, or empty string when Content-MD5 header is not present.
  • Content-Type is the value of request's Content-Type header, or empty string if Content-Type header is not present.
  • Date is the value of requests Data header.
  • CanonicalizedOSSHeaders contains headers prefixed by x-oss- and additional headers
  • AdditionalHeaders is a list of normalized additional header names
  • CanonicalizedResource is the OSS resource to visit
  • AccessKeySecret is the AccessKeySecret

Constructing CanonicalizedOSSHeaders

To construct the CanonicalizedOSSHeaders part of StringToSign, select all HTTP request headers that start with x-oss- (using a case-insensitive comparison) and headers in AdditionalHeaders, and use the following process

  1. Convert each HTTP header's name to lower case and trim spaces leading and tailing whitespaces from its value.
  2. Sort headers by name in alphabetic order.
  3. Join each header's name and value by colon, then append a newline character.
  4. Construct the CanonicalizedOSSHeaders element by concatenating all headers in this list into a single string.

The resulted CanonicalizedOSSHeaders is like

Lowercase(<HeaderName1>) + ":" + Trim(<HeaderValue1> + "\n"
Lowercase(<HeaderName2>) + ":" + Trim(<HeaderValue2> + "\n"
...
Lowercase(<HeaderNameN>) + ":" + Trim(<HeaderValueN> + "\n"

CanonicalizedOSSHeaders could be an empty string when there are no x-oss- prefixed headers, nor additional headers to sign.

Constructing AdditionalHeaders

AdditionalHeaders is a list of additional header names in lower case, then sorted in alphabetic order, and finally joined by semi-colon. For example, additional header names are Host and Range, AdditionalHeaders should be host;range.

Constructing CanonicalizedResource

CanonicalizedResource represents the OSS resource targeted by the request. Construct it for a REST request as follows:

  1. Start with an empty string ("")
  2. Append UriEncoded Resource. Resource is one of /BucketName/ObjectKey, /BucketName, /depending on the resource you are requesting.
  3. Append the UriEncoded query string parameters and their values. Query string parameters should be sorted by UriEncoded name in alphabetic order. If there are two fields having same name, they should be further sorted by UriEncoded value in alphabetic order. If a parameter's value is empty, = must be omitted.

The constructed CanonicalizedResource looks like

UriEncode(Resource) [?UriEncode(Name1)=UriEncode(Value1)&UriEncode(Name2)&UriEncode(Name3)=UriEncode(Value3]

Example: PutObject

The examples in this and following sections use the (non-working) credentials in the following table.

AccessKeyId AccessKeySecret
44CF9590006BF252F707 OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV

This example uploads an object with key nelson to bucket oss-example, and object ACL is private.

PUT /nelson HTTP/1.1
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
Accept-Encoding: identity
Content-Length: 32
x-oss-object-acl: private
Accept: */*
date: Wed, 15 Feb 2017 09:37:11 GMT
content-type: text/plain
Connection: keep-alive
User-Agent: aliyun-sdk-python/2.2.3(Windows/7/AMD64;3.5.0)
content-md5: FxqG8Ca0qEJPOghSihJ8Ew==
authorization: OSS2 AccessKeyId:44CF9590006BF252F707,Signature:5Am2ewK1tL0gXX7GV6dwybZtj7efOEtc0Mo2FR6CkM8=

The constructed StringToSign is

PUT
FxqG8Ca0qEJPOghSihJ8Ew==
text/plain
Wed, 15 Feb 2017 09:37:11 GMT
x-oss-object-acl:private

%2Foss-example%2Fnelson

Below is the annotated version of above StringToSign

PUT                             # VERB, a.k.a. HTTP method
FxqG8Ca0qEJPOghSihJ8Ew==        # Content-MD5 header value in the request
text/plain                      # Content-Type value in the request
Wed, 15 Feb 2017 09:37:11 GMT   # Date value in the request
x-oss-object-acl:private        # an 'x-oss-' prefixed header, header name should be lower cased
                                # no additional headers
%2Foss-example%2Fnelson         # CanonicalizedResource

With StringToSign obtained above, we can calculate a signature. For example, in Python we may

import base64
import hmac
import hashlib
h = hmac.new("OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV",
             "PUT\nFxqG8Ca0qEJPOghSihJ8Ew==\ntext/plain\nWed, 15 Feb 2017 09:37:11 GMT\nx-oss-object-acl:private\n\n%2Foss-example%2Fnelson", hashlib.sha256)

signature = base64.b64encode(h.digest())

The value of Signature should be 5Am2ewK1tL0gXX7GV6dwybZtj7efOEtc0Mo2FR6CkM8=

Example: GetObject with Additional Headers

This example downloads first 8 bytes of object nelson in bucket oss-example with if-modified-since condition. Note that range and if-modified-since are included into signature calculation as additional headers.

GET /nelson HTTP/1.1
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
Accept-Encoding: identity
User-Agent: aliyun-sdk-python/2.2.3(Windows/7/AMD64;3.5.0)
Connection: keep-alive
range: bytes=0-7
date: Thu, 16 Feb 2017 02:09:39 GMT
Accept: */*
authorization: OSS2 AccessKeyId:44CF9590006BF252F707,AdditionalHeaders:range;if-modified-since,Signature:YG9mKO3m4S0Jx9Hk6Lq64VchJg/TOTkyCX4DaeeOYxE=
if-modified-since: Thu, 16 Feb 2017 02:10:39 GMT

StringToSign should be

GET


Thu, 16 Feb 2017 02:09:39 GMT
if-modified-since:Thu, 16 Feb 2017 02:10:39 GMT
range:bytes=0-7
if-modified-since;range
%2Foss-example%2Fnelson

Below is the annotated version of above StringToSign

GET                                             # VERB
                                                # empty Content-MD5 header value
                                                # empty Content-Type header value
Thu, 16 Feb 2017 02:09:39 GMT                   # Date
if-modified-since:Thu, 16 Feb 2017 02:10:39 GMT # CanonicalizedOSSHeaders: additional header name, value pairs
range:bytes=0-7
if-modified-since;range                         # AdditionalHeaders: additional header names
%2Foss-example%2Fnelson                         # resource

Constructing a Presigned URL

In addition to sign a request in HTTP header, authorization information can also be entirely expressed in a URL. Such URL is called a presigned URL, and it could be given to third party users, so that they can access a privileged resource via this URL. However, supplying authorization information in both HTTP header and URL is not allowed.

Example presigned URL looks like

http://oss-example.oss-cn-hangzhou.aliyuncs.com/nelson?x-oss-expires=1487152431&x-oss-signature=ps%2F%2BMLhd1WKkVi%2FQlOiliJsTaBMBk93f6UYVscDNHCQ%3D&x-oss-access-key-id=44CF9590006BF252F707&x-oss-signature-version=OSS2

A Signature Version 2 presigned URL must contain following components

  • x-oss-signature-version indicates the version of signature algorithm, it must be OSS2
  • x-oss-expires indicates the timeout time of the URL. It is a UNIX timestamp, which is the number of seconds that have elapsed since 00:00:00 UTC, Jan. 1, 1970. If the time when OSS server receives a request accessing this URL is later than x-oss-expires, a 403 HTTP status will be returned with OSS error code AccessDenied.
  • x-oss-access-key-id indicates AccessKeyId.
  • x-oss-signature is the calculated signature.

Additionally a presigned URL may contain x-oss-additional-headers to indicate additional headers to be included in signature calculation. The format of x-oss-additional-headers is a semi-colon separated list of header names.

The algorithm to calculate signature is similar to the one that constructs an authorization header. The main difference is to use Expires, i.e. the value of x-oss-expires to replace Date:

StringToSign = 
            VERB + "\n” 
            + Content-MD5 + "\n" 
            + Content-Type + "\n" 
            + Expires + "\n" 
            + CanonicalizedOSSHeaders
            + AdditionalHeaders + "\n"
            + CanonicalizedResource

Signature = base64(hmac-sha256(AccessKeySecret, StringToSign))

Other differences are

  • CanonicalizedResource should include x-oss-signature-version, x-oss-access-key-id, x-oss-expires and x-oss-additional-headers if any. x-oss-additional-headers indicates additional headers to be included in signature calculation besides headers prefixed by x-oss-.
  • The value of query parameters should be URL encoded. Especially the value of x-oss-signature should be URL encoded.
  • The value of x-oss-signature-version must be OSS2. If this parameter is not present, OSS will use Signature Version 1 to verify the request.
  • The value of x-oss-expires is verified before signature verification.

Example: Generating a Private Download URL

Same AccessKeyId and AccessKeySecret in above examples are used in follow examples. A presigned URL will be constructed, with which a user can download the object before UNIX time 1487152431, i.e. Wed, 15 Feb 2017 09:53:51 GMT. StringToSign is

GET


1487152431

%2Foss-example%2Fnelson?x-oss-access-key-id=44CF9590006BF252F707&x-oss-expires=1487152431&x-oss-signature-version=OSS2

Below is the annotated version of the above StringToSign

GET             # VERB, a.k.a. HTTP method
                # Content-MD5 header value is empty
                # Content-Type header value is empty
1487152431      # Expiration time in UNIX timestamp format
                # no additional headers.
%2Foss-example%2Fnelson?x-oss-access-key-id=44CF9590006BF252F707&x-oss-expires=1487152431&x-oss-signature-version=OSS2

Example: Generating a Private Download URL with Extra Query Parameters

In Signature Version 2, query string parameters that are not meaningful to OSS are included in signature calculation too. Below is an example for this. The resulted URL is (add extra new lines for readability)

http://oss-example.oss-cn-hangzhou.aliyuncs.com/nelson?
x-oss-signature-version=OSS2&
x-oss-signature=wsARTPqvZdbdPjYpZfDZ%2FjisUaacYq7gGOdB3f1BgTE%3D&
extra-query=1&
x-oss-access-key-id=44CF9590006BF252F707&
x-oss-expires=1487211619

StringToSign is

GET


1487211619

%2Foss-example%2Fnelson?extra-query=1&x-oss-access-key-id=44CF9590006BF252F707&x-oss-expires=1487211619&x-oss-signature-version=OSS2

Note that extra-query is not meaningful to OSS, but it is included in StringToSign.

Constructing a Signature for PostObject Request

The steps to sign a PostObject in Signature Version 2 is as follows:

  • set value of form field x-oss-signature-version to OSS2
  • set value of form field x-oss-access-key-id to AccessKeyId. Note in Signature Version 1 AccessKeyId is specified by form field OSSAccessKeyId.
  • set value of form field x-oss-signature to the calculated signature. Note in Signature Version 1, signature is specified by form field Signature.
  • Use AccessKeySecret to sign Base64 encoded policy with SHA256 hash algorithm. Note that Base64 encoded policy is also the value of form field policy.

The sign algorithm could be illustrated by following Python2 code

import hmac
import hashlib
import base64

digest = hmac.new(access_key_secret, encoded_policy, hashlib.sha256).digest()
signature = base64.b64encode(digest)

Example: PostObject

This example uploads an object with key object-from-post.txt

POST / HTTP/1.1
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
Content-Length: 887
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.9.1
Connection: keep-alive
Content-Type: multipart/form-data; boundary=arbitraryboundaryvalue

--arbitraryboundaryvalue
Content-Disposition: form-data; name="policy"

eyAiZXhwaXJhdGlvbiI6ICIyMDE3LTAyLTE2VDEzOjAxOjU5LjAwMFoiLCJjb25kaXRpb25zIjogW1sic3RhcnRzLXdpdGgiLCAiJGtleSIsICIiXV19
--arbitraryboundaryvalue
Content-Disposition: form-data; name="x-oss-access-key-id"

44CF9590006BF252F707
--arbitraryboundaryvalue
Content-Disposition: form-data; name="x-oss-signature"

g5N6HBLwr0AGIH4wYHz2k7EieGCklb1I/oNp5mXc3oc=
--arbitraryboundaryvalue
Content-Disposition: form-data; name="key"

object-from-post.txt
--arbitraryboundaryvalue
Content-Disposition: form-data; name="x-oss-signature-version"

OSS2
--arbitraryboundaryvalue
Content-Disposition: form-data; name="file"; filename="object-from-post.txt"

file content for post object request
--arbitraryboundaryvalue
Content-Disposition: form-data; name="submit"

Upload to OSS
--arbitraryboundaryvalue--