您可以在HTTP请求中增加 Authorization 的Header来包含签名(Signature)信息,表明这个消息已被授权。

SDK 签名实现

OSS SDK已经实现签名,用户使用OSS SDK不需要关注签名问题。如果您想了解具体语言的签名实现,请参考OSS SDK的代码。OSS SDK签名实现的文件如下表:
SDK 签名实现
Java SDK OSSRequestSigner.java
Python SDK auth.py
.Net SDK OssRequestSigner.cs
PHP SDK OssClient.php
C SDK oss_auth.c
JavaScript SDK client.js
Go SDK auth.go
Ruby SDK util.rb
iOS SDK OSSModel.m
Android SDK OSSUtils.java

当您自己实现签名,访问OSS报SignatureDoesNotMatch错误时,请参见自签名计算失败排除错误。

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. 将步骤1中得到的所有HTTP请求头按照名字的字典序进行升序排列。
  4. 删除请求头和内容之间分隔符两端出现的任何空格。例如x-oss-meta-name: TaoBao转换成x-oss-meta-name:TaoBao
  5. 将每一个请求头和内容用\n分隔符分隔拼成最后的CanonicalizedOSSHeaders。
说明
  • CanonicalizedOSSHeaders可以为空,无需添加最后的分隔符\n
  • 如果只有一个CanonicalizedOSSHeaders,例如x-oss-meta-a\n,则需要在最后加上\n
  • 如果有多个CanonicalizedOSSHeaders,例如x-oss-meta-a:a\nx-oss-meta-b:b\nx-oss-meta-c:c\n,则需要在每一个CanonicalizedOSSHeaders之后加上\n

构建CanonicalizedResource的方法

用户发送请求中想访问的OSS目标资源被称为CanonicalizedResource,它的构建方法如下:

  1. 将CanonicalizedResource置为空字符串""
  2. 设置要访问的OSS资源/BucketName/ObjectName。如果仅有BucketName而没有ObjectName,则CanonicalizedResource为”/BucketName/“,如果既没有BucketName也没有ObjectName,则CanonicalizedResource为“/”。
  3. 如果请求的资源包括子资源(SubResource) ,那么将所有的子资源按照字典序,从小到大排列并以&为分隔符生成子资源字符串。在CanonicalizedResource字符串尾添加和子资源字符串。此时的CanonicalizedResource为/BucketName/ObjectName?acl&uploadId=UploadId
    • OSS目前支持的子资源(SubResource)包括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等。
    • 子资源(SubResource)有以下三种类型:

计算签名头规则

  • 签名的字符串必须为UTF-8格式。含有中文字符的签名字符串必须先进行 UTF-8 编码,再与 AccessKeySecret计算最终签名。
  • 签名的方法用RFC 2104中定义的HMAC-SHA1方法,其中Key指的是AccessKeySecret。
  • Content-TypeContent-MD5在请求中不是必须的,但是如果请求需要签名验证,空值的话以换行符 \n 代替。
  • 在所有非HTTP标准定义的header中,只有以x-oss-开头的header,需要加入签名字符串(如下方签名示例中的x-oss-magic则需要加入签名字符串);其他非HTTP标准header将被OSS忽略。
    说明x-oss-开头的header在签名验证前需要符合以下规范:
    • header的名字需要变成小写。
    • header按字典序自小到大排序。
    • 分割header name和value的冒号前后不能有空格。
    • 每个header之后都有一个换行符“\n”,如果没有header,CanonicalizedOSSHeaders则设置为空。

签名示例

请求 签名字符串计算公式 签名字符串
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/nelson

假如AccessKeyId为“44CF959******252F707”,AccessKeySecret为“OtxrzxIsfpFjA7Sw******8Bw21TLhquhboDYROV”,可用以下方法(以Python为例)计算签名(Signature):

import base64
import hmac
import sha
h = hmac.new("OtxrzxIsfpFjA7Sw******8Bw21TLhquhboDYROV",
             "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)计算结果应该为26NBxoKd******Dv6inkoDft/yA=,由于Authorization = “OSS”+ AccessKeyId + “:” + Signature,所以最后Authorization为 “OSS 44CF95900***BF252F707:26NBxoKd******Dv6inkoDft/yA=”,然后加上Authorization头组成最后需要发送的消息:

PUT /nelson HTTP/1.0
Authorization:OSS 44CF95900***BF252F707:26NBxoKd******Dv6inkoDft/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不存在或未激活,返回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-1982”都是非法日期格式。
  • 如果签名验证的时候,头中没有传入Date或者格式不正确,返回403 Forbidden错误。错误码:AccessDenied。
  • 传入请求的时间必须在OSS服务器当前时间之后的15分钟以内,否则返回403 Forbidden。错误码:RequestTimeTooSkewed。
  • 如果AccessKeyId已激活,但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>
         1E446260FF9B****
     </RequestId>
     <HostId>
         oss-cn-hangzhou.aliyuncs.***
     </HostId>
     <SignatureProvided>
         y5H7yzPsA/tP4+0tH1HHvPEwUv8=
     </SignatureProvided>
     <StringToSign>
         GET
    Wed, 11 May 2011 07:59:25 GMT
    /oss-example?acl
     </StringToSign>
     <OSSAccessKeyId>
         AKIAIVAKMSMOY7VO****
     </OSSAccessKeyId>
    </Error>

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='