用戶可以在HTTP請求中增加 Authorization 的Header來包含簽名(Signature)資訊,表明這個消息已被授權。

Authorization欄位計算的方法

Authorization = "OSS " + AccessKeyId + ":" + Signature
Signature = base64(hmac-sha1(AccessKeySecret,
            VERB + "\n"
            + Content-MD5 + "\n" 
            + Content-Type + "\n" 
            + Date + "\n" 
            + CanonicalizedOSSHeaders
            + CanonicalizedResource))
  • AccessKeySecret 表示簽名所需的密鑰
  • VERB表示HTTP 要求的Method,主要有PUT,GET,POST,HEAD,DELETE等
  • \n 表示分行符號
  • Content-MD5 表示請求內容數據的MD5值,對消息內容(不包括頭部)計算MD5值獲得128位元位元字,對該數字進行base64編碼而得到。該要求標頭可用於消息合法性的檢查(消息內容是否與發送時一致),如”eB5eJF1ptWaXm4bijSPyxw==”,也可以為空。詳情參看RFC2616 Content-MD5
  • Content-Type 表示請求內容的類型,如”application/octet-stream”,也可以為空
  • Date 表示此次操作的時間,且必須為GMT格式,如”Sun, 22 Nov 2015 08:16:38 GMT”
  • CanonicalizedOSSHeaders 表示以 x-oss- 為首碼的http header的字典序排列
  • CanonicalizedResource 表示用戶想要訪問的OSS資源

其中,Date和CanonicalizedResource不能為空;如果請求中的Date時間和OSS伺服器的時間差15分鐘以上,OSS伺服器將拒絕該服務,並返回HTTP 403錯誤。

構建CanonicalizedOSSHeaders的方法

所有以 x-oss- 為首碼的HTTP Header被稱為CanonicalizedOSSHeaders。它的構建方法如下:

  1. 將所有以 x-oss- 為首碼的HTTP要求標頭的名字轉換成 小寫 。如X-OSS-Meta-Name: TaoBao轉換成x-oss-meta-name: TaoBao
  2. 如果請求是以STS獲得的AccessKeyId和AccessKeySecret發送時,還需要將獲得的security-token值,以 x-oss-security-token:security-token 的形式加入到簽名字元串中。
  3. 將上一步得到的所有HTTP要求標頭按照名字的字典序進行升序排列。
  4. 刪除要求標頭和內容之間分隔符號兩端出現的任何空格。如x-oss-meta-name: TaoBao轉換成:x-oss-meta-name:TaoBao
  5. 將每一個頭和內容用 \n 分隔符號分隔拼成最後的CanonicalizedOSSHeaders。
说明
  • CanonicalizedOSSHeaders可以為空,無需添加最後的 \n
  • 如果只有一個,則如 x-oss-meta-a\n,注意最後的\n
  • 如果有多個,則如 x-oss-meta-a:a\nx-oss-meta-b:b\nx-oss-meta-c:c\n, 注意最後的\n

構建CanonicalizedResource的方法

用戶發送請求中想訪問的OSS目標資源被稱為CanonicalizedResource。它的構建方法如下:

  1. 將CanonicalizedResource置成Null 字元串 ""
  2. 放入要訪問的OSS資源 /BucketName/ObjectName無ObjectName則CanonicalizedResource為”/BucketName/“,如果同時也沒有BucketName則為“/”)
  3. 如果請求的資源套件括子資源(SubResource) ,那麼將所有的子資源按照字典序,從小到大排列並以 & 為分隔符號生成子資源字元串。在CanonicalizedResource字元串尾添加 和子資源字元串。此時的CanonicalizedResource如:/BucketName/ObjectName?acl&uploadId=UploadId
  4. 如果用戶請求在指定了查詢字元串(QueryString,也叫Http Request Parameters),那麼將這些查詢字元串及其請求值按照 字典序,從小到大排列,以 & 為分隔符號,按參數添加到CanonicalizedResource中。此時的CanonicalizedResource如:/BucketName/ObjectName?acl&response-content-type=ContentType&uploadId=UploadId
说明
  • OSS目前支援的子資源(sub-resource)包括:acl,uploads,location,cors,logging,website,referer,lifecycle,delete,append,tagging,objectMeta,uploadId,partNumber,security-token,position,img,style,styleName,replication,replicationProgress,replicationLocation,cname,bucketInfo,comp,qos,live,status,vod,startTime,endTime,symlink,x-oss-process,response-content-type,response-content-language,response-expires,response-cache-control,response-content-disposition,response-content-encoding等
  • 子資源(sub-resource)有三種類型:
    • 資源標識,如子資源中的acl,append,uploadId,symlink等,詳見關於Bucket的操作關於Object的操作
    • 指定返回Header欄位,如 response-***,詳見GetObjectRequest Parameters
    • 檔案(Object)處理方式,如 x-oss-process,用於檔案的處理方式,如圖片處理

計算簽名頭規則

  • 簽名的字元串必須為 UTF-8 格式。含有中文字元的簽名字元串必須先進行 UTF-8 編碼,再與 AccessKeySecret計算最終簽名。
  • 簽名的方法用RFC 2104中定義的HMAC-SHA1方法,其中Key為 AccessKeySecret` 。
  • Content-TypeContent-MD5 在請求中不是必須的,但是如果請求需要簽名驗證,空值的話以分行符號 \n 代替。
  • 在所有非HTTP標準定義的header中,只有以 x-oss- 開頭的header,需要加入簽名字元串;其他非HTTP標準header將被OSS忽略(如上例中的x-oss-magic是需要加入簽名字元串的)。
  • x-oss- 開頭的header在簽名驗證前需要符合以下規範:
    • header的名字需要變成小寫。
    • header按字典序自小到大排序。
    • 分割header name和value的冒號前後不能有空格。
    • 每個Header之後都有一個分行符號“\n”,如果沒有Header,CanonicalizedOSSHeaders就設定為空。

簽名樣本

假如AccessKeyId是”44CF9590006BF252F707”,AccessKeySecret是”OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV”

請求 簽名字元串計算公式 簽名字元串
PUT /nelson HTTP/1.0 Content-MD5: eB5eJF1ptWaXm4bijSPyxw== Content-Type: text/html Date: Thu, 17 Nov 2005 18:49:58 GMT Host: oss-example.oss-cn-hangzhou.aliyuncs.com X-OSS-Meta-Author: foo@bar.com X-OSS-Magic: abracadabra Signature = base64(hmac-sha1(AccessKeySecret,VERB + “\n” + Content-MD5 + “\n”+ Content-Type + “\n” + Date + “\n” + CanonicalizedOSSHeaders+ CanonicalizedResource)) “PUT\n eB5eJF1ptWaXm4bijSPyxw==\n text/html\n Thu, 17 Nov 2005 18:49:58 GMT\n x-oss-magic:abracadabra\nx-oss-meta-author:foo@bar.com\n/oss-example/nels

可用以下方法計算簽名(Signature):

python範例程式碼:

import base64
import hmac
import sha
h = hmac.new("OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV",
             "PUT\nODBGOERFMDMzQTczRUY3NUE3NzA5QzdFNUYzMDQxNEM=\ntext/html\nThu, 17 Nov 2005 18:49:58 GMT\nx-oss-magic:abracadabra\nx-oss-meta-author:foo@bar.com\n/oss-example/nelson", sha)
Signature = base64.b64encode(h.digest())
print("Signature: %s" % Signature)

簽名(Signature)計算結果應該為 26NBxoKdsyly4EDv6inkoDft/yA=,因為Authorization = “OSS “ + AccessKeyId + “:” + Signature所以最後Authorization為 “OSS 44CF9590006BF252F707:26NBxoKdsyly4EDv6inkoDft/yA=”然後加上Authorization頭來組成最後需要發送的消息:

PUT /nelson HTTP/1.0
Authorization:OSS 44CF9590006BF252F707:26NBxoKdsyly4EDv6inkoDft/yA=
Content-Md5: eB5eJF1ptWaXm4bijSPyxw==
Content-Type: text/html
Date: Thu, 17 Nov 2005 18:49:58 GMT
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
X-OSS-Meta-Author: foo@bar.com
X-OSS-Magic: abracadabra

細節分析

  • 如果傳入的AccessKeyId不存在或inactive,返回403 Forbidden。錯誤碼:InvalidAccessKeyId。
  • 若用戶要求標頭中Authorization值的格式不對,返回400 Bad Request。錯誤碼:InvalidArgument。
  • OSS所有的請求都必須使用HTTP 1.1協議規定的GMT時間格式。其中,日期的格式為:
    date1 = 2DIGIT SP month SP 4DIGIT; day month year (e.g., 02 Jun
            1982)
    上述日期格式中,“天”所佔位元都是“2 DIGIT”。因此,“Jun 2”、“2 Jun 1982”和“2-Jun-82”都是非法日期格式。
  • 如果簽名驗證的時候,頭中沒有傳入Date或者格式不正確,返回403 Forbidden錯誤。錯誤碼:AccessDenied。
  • 傳入請求的時間必須在OSS伺服器目前時間之後的15分鐘以內,否則返回403 Forbidden。錯誤碼:RequestTimeTooSkewed。
  • 如果AccessKeyId是active的,但OSS判斷用戶的請求發生簽名錯誤,則返回403 Forbidden,並在返回給用戶的response中告訴用戶正確的用於驗證加密的簽名字元串。用戶可以根據OSS的response來檢查自己的簽名字元串是否正確。返回樣本:
    <?xml version="1.0" ?>
    <Error>
     <Code>
         SignatureDoesNotMatch
     </Code>
     <Message>
         The request signature we calculated does not match the signature you provided. Check your key and signing method.
     </Message>
     <StringToSignBytes>
         47 45 54 0a 0a 0a 57 65 64 2c 20 31 31 20 4d 61 79 20 32 30 31 31 20 30 37 3a 35 39 3a 32 35 20 47 4d 54 0a 2f 75 73 72 65 61 6c 74 65 73 74 3f 61 63 6c
     </StringToSignBytes>
     <RequestId>
         1E446260FF9B10C2
     </RequestId>
     <HostId>
         oss-cn-hangzhou.aliyuncs.com
     </HostId>
     <SignatureProvided>
         y5H7yzPsA/tP4+0tH1HHvPEwUv8=
     </SignatureProvided>
     <StringToSign>
         GET
    Wed, 11 May 2011 07:59:25 GMT
    /oss-example?acl
     </StringToSign>
     <OSSAccessKeyId>
         AKIAIVAKMSMOY7VOMRWQ
     </OSSAccessKeyId>
    </Error>
说明

常見問題

Content-MD5的計算方法

Content-MD5的計算
以消息內容為"123456789"來說,計算這個字元串的Content-MD5
正確的計算方式:
標準中定義的演算法簡單點說就是:
1. 先計算MD5加密的位元組(128位)。
2. 再對這個二進位進行base64編碼(而不是對32位字元串編碼)。 
以Python為例子:
正確計算的代碼為:
>>> import base64,hashlib
>>> hash = hashlib.md5()
>>> hash.update("0123456789")
>>> base64.b64encode(hash.digest())
'eB5eJF1ptWaXm4bijSPyxw=='
需要注意
正確的是:hash.digest(),計算出進位數組(128位)
>>> hash.digest()
'x\x1e^$]i\xb5f\x97\x9b\x86\xe2\x8d#\xf2\xc7'
常見錯誤是直接對計算出的32位字元串編碼進行base64編碼。
例如,錯誤的是:hash.hexdigest(),計算得到可見的32位字元串編碼
>>> hash.hexdigest()
'781e5e245d69b566979b86e28d23f2c7'
錯誤的MD5值進行base64編碼後的結果:
>>> base64.b64encode(hash.hexdigest())
'NzgxZTVlMjQ1ZDY5YjU2Njk3OWI4NmUyOGQyM2YyYzc='