在Header中包含V1签名

在OSS中,使用HTTP请求的Authorization Header来携带签名信息是进行身份验证的最常见方法。除了使用POST签名和URL签名之外,所有的OSS操作都需要通过Authorization Header来进行身份验证。本文介绍如何使用V1签名算法来在Header中包含签名。

重要

OSS支持更安全的V4签名算法,建议您使用V4签名。更多信息,请参见V4签名

SDK签名实现

OSS SDK已自动实现V1签名,您使用OSS SDK时无需关注签名问题。如果您想了解具体语言的签名实现,请参考OSS SDK的代码。OSS SDK签名实现的文件请参见下表。

SDK

签名实现

Java

OSSV1Signer.java

PHP

SignerV1.php

Node.js

client.js

Browser.js

Python

auth.py

.Net

OssRequestSigner.cs

Android

OSSUtils.java

Go

v1.go

iOS

OSSModel.m

C++

SignerV1.cc

C

oss_auth.c

Ruby

util.rb

Authorization字段计算的方法

计算方法

Authorization = "OSS " + AccessKeyId + ":" + Signature
Signature = base64(hmac-sha1(AccessKeySecret,
            VERB + "\n"
            + Content-MD5 + "\n" 
            + Content-Type + "\n" 
            + Date + "\n" 
            + CanonicalizedOSSHeaders
            + CanonicalizedResource))

参数说明

参数

类型

是否必选

示例值

说明

AccessKeyId

字符串

LTAI********************

填写当前账号的访问密钥AccessKey,包括AccessKey ID和AccessKey Secret。

AccessKeySecret

字符串

Q0Ye**************************

x-oss-security-token

字符串

CAIS********************************

STS安全令牌。仅当使用STS构造Header签名时,才需要设置此参数。关于如何获取Security-Token的具体操作,请参见AssumeRole - 获取扮演角色的临时身份凭证

VERB

枚举值

PUT

HTTP请求的Method,包含PUT、GET、POST、HEAD、DELETE、OPTIONS等。

\n

字符串

\n

换行符。

Content-MD5

字符串

eB5eJF1ptWaXm4bijSPyxw==

请求内容数据的MD5值,对消息内容(不包括头部)计算MD5值获得128比特位数字,对该数字进行base64编码得出。更多信息,请参见RFC2616 Content-MD5

该请求头可用于消息合法性的检查(消息内容是否与发送时一致),也可以为空。

关于Content-MD5的计算方法,请参见Content-MD5的计算方法

Content-Type

字符串

application/octet-stream

请求内容的类型,也可以为空。

说明

如果生成签名时没有设置Content-Type,则后续使用签名上传文件时也无需设置该参数。

Date

字符串

Sun, 22 Nov 2015 08:16:38 GMT

此次操作的时间,Date必须为GMT格式,且不能为空。该值取自请求头的Date字段或者x-oss-date字段。当这两个字段同时存在时,以x-oss-date为准。

重要

如果请求中的Date时间和OSS服务器的当前时间差15分钟以上,OSS服务器将拒绝该请求,并返回HTTP 403错误。

CanonicalizedOSSHeaders

字符串

x-oss-meta-a:a\nx-oss-meta-b:b\nx-oss-meta-c:c\n

x-oss-为前缀的HTTP Header的字典序排列,可以为空。

  • 如果设置CanonicalizedOSSHeaders为空,则无需在最后添加分隔符\n

  • 如果只有一个CanonicalizedOSSHeaders,则需要在最后添加分隔符\n,例如x-oss-meta-a\n

  • 如果有多个CanonicalizedOSSHeaders,则需要在每一个CanonicalizedOSSHeaders之后添加分隔符\n,例如x-oss-meta-a:a\nx-oss-meta-b:b\nx-oss-meta-c:c\n

关于CanonicalizedOSSHeaders的构建方法,请参见构建CanonicalizedOSSHeaders的方法

CanonicalizedResource

字符串

examplebucket

您希望访问的OSS资源,不能为空。

关于CanonicalizedResource的构建方法,请参见构建CanonicalizedResource的方法

签名示例

  • 示例1(包含所有参数)

    请求

    签名字符串计算公式

    签名字符串

    PUT /nelson HTTP/1.0 Content-MD5: eB5eJF1ptWaXm4bijSPyxw== Content-Type: text/html Date: Wed, 28 Dec 2022 10:27:41 GMT Host: examplebucket.oss-cn-hangzhou.aliyuncs.com x-oss-meta-author: alice x-oss-meta-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 Wed, 28 Dec 2022 10:27:41 GMT\n x-oss-meta-magic:abracadabra\nx-oss-meta-author:alice\n/examplebucket/nelson

    假设AccessKey ID为LTAI********************,AccessKey Secret为Q0Ye**************************,此处以Python为例介绍计算签名(Signature)的方法。

    import hmac
    import hashlib
    import base64
    
    h = hmac.new("Q0Ye**************************".encode('utf-8'),
                 "PUT\nODBGOERFMDMzQTczRUY3NUE3NzA5QzdFNUYzMDQxNEM\ntext/html\nWed, 28 Dec 2022 10:27:41 GMT\nx-oss-meta-magic:abracadabra\nx-oss-meta-author:alice\n/oss-example/nelson".encode('utf-8'), hashlib.sha1)
    signature =  base64.encodebytes(h.digest())
    print(signature)

    签名(Signature)计算结果为J9Nl************************,结合Authorization头的结构组成,需要发送的消息体如下。

    PUT /nelson HTTP/1.0
    Authorization:OSS LTAI********************:J9Nl************************
    Content-Md5: eB5eJF1ptWaXm4bijSPyxw==
    Content-Type: text/html
    Date: Wed, 28 Dec 2022 10:27:41 GMT
    Host: oss-example.oss-cn-hangzhou.aliyuncs.com
    x-oss-meta-author: alice
    x-oss-meta-magic: abracadabra
  • 示例2(不包含可选参数Content-MD5以及Content-Type)

    请求

    签名字符串计算公式

    签名字符串

    PUT /nelson HTTP/1.0 Date: Wed, 28 Dec 2022 09:56:32 GMT Host: examplebucket.oss-cn-hangzhou.aliyuncs.com x-oss-meta-author: alice x-oss-meta-magic: abracadabra

    Signature = base64(hmac-sha1(AccessKeySecret,VERB + “\n” + “\n”+ “\n” + Date + “\n” + CanonicalizedOSSHeaders+ CanonicalizedResource))

    PUT\n\n\nWed, 28 Dec 2022 09:56:32 GMT\n x-oss-meta-magic:abracadabra\nx-oss-meta-author:alice\n/examplebucket/nelson

    假设AccessKey ID为LTAI********************,AccessKey Secret为KZo1**************************,此处以Python为例介绍计算签名(Signature)的方法。

    import hmac
    import hashlib
    import base64
    
    h = hmac.new("KZo1**************************".encode('utf-8'),
                 "PUT\n\n\nWed, 28 Dec 2022 09:56:32 GMT\nx-oss-meta-magic:abracadabra\nx-oss-meta-author:alice\n/oss-example/nelson".encode('utf-8'), hashlib.sha1)
    signature =  base64.encodebytes(h.digest())
    print(signature)

    签名(Signature)计算结果为Mhb1************************,结合Authorization头的结构组成,需要发送的消息体如下。

    PUT /nelson HTTP/1.0
    Authorization:OSS LTAI********************:Mhb1************************
    Date: Wed, 28 Dec 2022 09:56:32 GMT
    Host: oss-example.oss-cn-hangzhou.aliyuncs.com
    x-oss-meta-author: alice
    x-oss-meta-magic: abracadabra

相关说明

  • 如果传入的AccessKey ID不存在或未激活,则返回403 Forbidden错误,错误码为InvalidAccessKeyId;如果AccessKey ID已激活,但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
    /examplebucket?acl
     </StringToSign>
     <OSSAccessKeyId>
         AKIAIVAKMSMOY7VO****
     </OSSAccessKeyId>
    </Error>
  • 如果用户请求头中Authorization值的格式不对,则返回400 Bad Request错误,错误码为InvalidArgument。

  • OSS所有的请求都必须使用HTTP 1.1协议规定的GMT时间格式。其中,日期的格式为:

    date1 = 2DIGIT SP month SP 4DIGIT; day month year (e.g., 02 Jun 1982)
    说明

    上述日期格式中day所占位数为2DIGIT,因此Jun 22 Jun 1982以及2-Jun-1982均为非法日期格式。

    • 如果签名验证时,Authorization头中没有传入Date,或者传入的格式不正确,则返回403 Forbidden错误,错误码为AccessDenied。

    • 传入请求的时间必须在OSS服务器当前时间之后的15分钟以内,否则返回403 Forbidden错误,错误码为RequestTimeTooSkewed。

构建CanonicalizedOSSHeaders的方法

所有以x-oss-为前缀的HTTP Header被称为CanonicalizedOSSHeaders,构建方法如下:

  1. 将所有以x-oss-为前缀的HTTP请求头的名称转换为小写的形式,例如X-OSS-Meta-Name: TaoBao转换为x-oss-meta-name: TaoBao

  2. 如果以从STS服务获取的临时访问凭证发送请求时,您还需要将获得的security-token值以x-oss-security-token:security-token的形式加入到签名字符串中。

    说明

    关于搭建STS服务的具体操作,请参见使用STS临时访问凭证访问OSS。您可以通过调用STS服务的AssumeRole接口或者使用各语言STS SDK来获取临时访问凭证。临时访问凭证包括临时访问密钥(AccessKey ID和AccessKey Secret)和安全令牌(SecurityToken)。

  3. 将获取的所有HTTP请求头按照名称的字典序进行升序排列。

  4. 删除请求头和内容之间分隔符两端出现的任何空格。例如x-oss-meta-name: TaoBao转换为x-oss-meta-name:TaoBao

  5. 将每一个请求头和内容使用分隔符\n分隔拼成CanonicalizedOSSHeaders。

构建CanonicalizedResource的方法

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

  • 如果既有BucketName也有ObjectName,则CanonicalizedResource格式为/BucketName/ObjectName

  • 如果仅有BucketName而没有ObjectName,则CanonicalizedResource格式为/BucketName/

  • 如果既没有BucketName也没有ObjectName,则CanonicalizedResource为正斜线(/)。

  • 如果请求的资源包括子资源(SubResource),则所有的子资源需按照字典序升序排列,并以&为分隔符生成子资源字符串。在CanonicalizedResource字符串尾添加?和子资源字符串。此时的CanonicalizedResource为/BucketName/ObjectName?acl&uploadId=UploadId

    OSS支持以下四种类型的子资源:

    • 资源标识,例如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、callback、callback-var等。更多信息,请参见关于Bucket的操作关于Object的操作

      重要

      资源标识严格区分大小写。

    • 指定返回Header字段,例如response-content-type、response-content-language、response-expires、response-cache-control、response-content-disposition、response-content-encoding等。更多信息,请参见GetObject

    • 图片处理操作方式,例如x-oss-process。更多信息,请参见图片处理

    • x-oss-ac-*开头的访问控制字段,例如x-oss-ac-source-ip、x-oss-ac-subnet-mask、x-oss-ac-vpc-id、x-oss-ac-forward-allow。更多信息,请参见签名版本1

      说明

      使用包含x-oss-ac-source-ip参数的CanonicalizedResource生成签名后,请从需要发送的消息query参数中移除x-oss-ac-source-ip,以保护IP地址信息安全。

计算签名头规则

  • 签名的字符串必须为UTF-8格式。含有中文字符的签名字符串必须先进行UTF-8编码,再与AccessKeySecret计算最终签名。

  • 签名的方法用RFC 2104中定义的HMAC-SHA1方法,其中Key指的是AccessKey Secret。

  • Content-TypeContent-MD5在请求中不是必须的,如果请求需要签名验证,空值请以换行符\n代替。

  • 在所有非HTTP标准定义的Header中,只有以x-oss-开头的Header需要加入签名字符串(例如签名示例中的x-oss-meta-magic则需要加入签名字符串);其他非HTTP标准Header将被OSS忽略。

    说明

    x-oss-开头的Header在签名验证前需要符合以下规范:

    • Header的名称需要转为小写。

    • Header按字典序升序排序。

    • 分割Header的name和value之间的冒号前后不能有空格。

    • 每个Header之后都要有一个换行符“\n”,如果没有Header,则设置CanonicalizedOSSHeaders为空。

Content-MD5的计算方法

以消息内容“0123456789”为例,以下详细说明正确及错误计算该字符串的Content-MD5的方法。

  • 正确计算示例

    1. 先计算MD5加密的二进制数组(128位)。

    2. 对该二进制数组进行base64编码(而不是对32位字符串编码)。

    以Python为例:

    >>> import base64,hashlib
    >>> hash = hashlib.md5()
    >>> hash.update("0123456789")   #在Python 3中此处需要改为hash.update(b"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='