-
Notifications
You must be signed in to change notification settings - Fork 361
SignV2
- 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:
-
UriEncodeto percentage encode a string -
Lowercaseto convert a string to lower case -
Trimto 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 resAn authorization header in Signature Version 2 has following form
Authorization: OSS2 AccessKeyId:<AccessKeyId>,Signature:<Signature>[,AdditionalHeaders:<AdditionalHeaders>]
where
-
OSS2specifies the signature version -
<AccessKeyId>is the AccessKeyId -
<AdditionalHeaders>are headers included in signature calculation besides those prefixed byx-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>is44CF9590006BF252F707 -
AdditionalHeadersisrange;if-modified-since -
SignatureisYG9mKO3m4S0Jx9Hk6Lq64VchJg/TOTkyCX4DaeeOYxE=
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
-
VERBis one of the HTTP methods, for example GET, PUT, HEAD, DELETE and POST. -
\nis the newline character -
Content-MD5is the value of request's Content-MD5 header, or empty string when Content-MD5 header is not present. -
Content-Typeis the value of request's Content-Type header, or empty string if Content-Type header is not present. -
Dateis the value of requests Data header. -
CanonicalizedOSSHeaderscontains headers prefixed byx-oss-and additional headers -
AdditionalHeadersis a list of normalized additional header names -
CanonicalizedResourceis the OSS resource to visit -
AccessKeySecretis the AccessKeySecret
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
- Convert each HTTP header's name to lower case and trim spaces leading and tailing whitespaces from its value.
- Sort headers by name in alphabetic order.
- Join each header's name and value by colon, then append a newline character.
- Construct the
CanonicalizedOSSHeaderselement 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.
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.
CanonicalizedResource represents the OSS resource targeted by the request. Construct it for a REST request as follows:
- Start with an empty string ("")
- Append UriEncoded
Resource.Resourceis one of/BucketName/ObjectKey,/BucketName,/depending on the resource you are requesting. - 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]
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=
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
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-versionindicates the version of signature algorithm, it must beOSS2 -
x-oss-expiresindicates 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 thanx-oss-expires, a 403 HTTP status will be returned with OSS error codeAccessDenied. -
x-oss-access-key-idindicates AccessKeyId. -
x-oss-signatureis 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
-
CanonicalizedResourceshould includex-oss-signature-version,x-oss-access-key-id,x-oss-expiresandx-oss-additional-headersif any.x-oss-additional-headersindicates additional headers to be included in signature calculation besides headers prefixed byx-oss-. - The value of query parameters should be URL encoded. Especially the value of
x-oss-signatureshould be URL encoded. - The value of
x-oss-signature-versionmust beOSS2. If this parameter is not present, OSS will use Signature Version 1 to verify the request. - The value of
x-oss-expiresis verified before signature verification.
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
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.
The steps to sign a PostObject in Signature Version 2 is as follows:
- set value of form field
x-oss-signature-versiontoOSS2 - set value of form field
x-oss-access-key-idto AccessKeyId. Note in Signature Version 1 AccessKeyId is specified by form fieldOSSAccessKeyId. - set value of form field
x-oss-signatureto the calculated signature. Note in Signature Version 1, signature is specified by form fieldSignature. - 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)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--