View a markdown version of this page

Authorization 标头的签名计算:在单个分块中传输有效载荷(AWS 签名版本 4) - Amazon Simple Storage Service

Authorization 标头的签名计算:在单个分块中传输有效载荷(AWS 签名版本 4)

使用 Authorization 标头对请求进行身份验证时,标头值包括签名等其它内容。签名计算因您就传输有效载荷所进行的选择而异(概述)。本节介绍在选择以单个分块传输有效载荷时的签名计算。示例部分(请参阅示例:签名计算)显示了签名计算和生成的 Authorization 标头,您可以将其用作测试套件来验证代码。

重要

在单个分块中传输有效载荷时,您可以选择在签名计算中包含有效载荷哈希值,称为已签名的有效载荷(如果不包括该哈希值,则该有效载荷被视为未签名)。下一节中讨论的签名过程适用于这两者,但请注意以下区别:

  • 签名的有效载荷选项:在构造规范请求时包含有效载荷哈希值(然后,该哈希值将成为 StringToSign 的一部分,如签名计算部分所述)。在向 S3 发送请求时,您还可以指定与 x-amz-content-sha256 标头值相同的值。

  • 未签名的有效载荷选项:您在构造规范请求时包含文本字符串 UNSIGNED-PAYLOAD,并在向 Amazon S3 发送请求时设置与 x-amz-content-sha256 标头值相同的值。

当您将请求发送到 Amazon S3 时,x-amz-content-sha256 标头值会通知 Amazon S3 有效载荷是否已签名。然后,Amazon S3 可以相应地创建签名以进行验证。

在这两种情况下,由于 x-amz-content-sha256 标头值已经是 HashedPayload 的一部分,因此您无需将 x-amz-content-sha256 标头作为规范标头包含在内。

计算签名

要计算签名,首先需要一个待签字符串。然后,使用签名密钥计算待签字符串的 HMAC-SHA256 哈希值。下图演示了该过程,包括您为签名而创建的字符串的各个组成部分

Amazon S3 收到经身份验证的请求时,它将计算签名,然后将其与您在请求中提供的签名进行比较。出于此原因,您必须采用 Amazon S3 使用的相同方法来计算签名。根据适用于签名的统一格式放置请求的过程称为标准化。

AWS4-HMAC-SHA256 签名流程图,显示了规范请求、StringToSign 和签名步骤。

下表介绍了图中显示的函数。您需要为这些函数实现代码。

函数 说明
Lowercase() 将字符串转换为小写。
Hex() base-16 编码的小写形式。
SHA256Hash() 安全哈希算法(SHA)加密哈希函数。
HMAC-SHA256() 使用 SHA256 算法和提供的签名密钥计算 HMAC。这是最终的签名。
Trim() 删除所有前导空格或尾随空格。
UriEncode()

URI 对每个字节进行编码。UriEncode() 必须强制执行以下规则:

  • URI 对除非预留字符之外的所有字节进行编码:“A”-“Z”、“a”-“z”、“0”-“9”、“-”、“.”、“_”和“~”。

  • 空格字符是预留字符,必须编码为“%20”(而不是“+”)。

  • 每个 URI 编码字节由“%”和该字节的两位十六进制值组成。

  • 十六进制值中的字母必须为大写,例如“%1A”。

  • 对除对象键名称之外的所有位置的正斜杠字符“/”进行编码。例如,如果对象键名称是 photos/Jan/sample.jpg,则不会对键名称中的正斜杠进行编码。

重要

由于底层 RFC 中的实现差异和相关歧义,您开发平台提供的标准 UrienCode 函数可能无法正常工作。建议您编写自己的自定义 UrienCode 函数,以确保编码能够正常工作。

以下是 Java 中的 UriEncode() 函数示例。

public static String UriEncode(CharSequence input, boolean encodeSlash) { StringBuilder result = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') { result.append(ch); } else if (ch == '/') { result.append(encodeSlash ? "%2F" : ch); } else { result.append(toHexUTF8(ch)); } } return result.toString(); }

任务 1:创建规范请求

本节提供创建规范请求的概述。

以下是 Amazon S3 用来计算签名的规范请求格式。要使签名匹配,您必须使用以下格式创建规范请求:

<HTTPMethod>\n <CanonicalURI>\n <CanonicalQueryString>\n <CanonicalHeaders>\n <SignedHeaders>\n <HashedPayload>

其中:

  • HTTPMethod 是 HTTP 方法之一,例如 GET、PUT、HEAD 和 DELETE。

  • CanonicalURI 是 URI 的绝对路径组件的 URI 编码版本,即从域名后面的“/”开始,直至字符串结尾处的任何内容;或者如果包含查询字符串参数,则为直至问号字符(“?”)的任何内容。以下示例中的 URI /examplebucket/myphoto.jpg 是绝对路径,并且您无需在绝对路径中对“/”进行编码:

    http://s3.amazonaws.com/examplebucket/myphoto.jpg

    注意

    向 Amazon S3 发出请求时,您没有对 URI 路径进行规范化。例如,您可能有一个存储桶,其中包含一个名为“my-object//example//photo.user”的对象。对路径进行规范化会将请求中的对象名称更改为“my-object/example/photo.user”。这是该对象的不正确路径。

  • CanonicalQueryString 指定 URI 编码的查询字符串参数。您可以单独对名称和值进行 URI 编码。您还必须按键名称的字母顺序对规范查询字符串中的参数进行排序。编码后进行排序。以下 URI 示例中的查询字符串是:prefix=somePrefix&marker=someMarker&max-keys=20

    http://s3.amazonaws.com/examplebucket?prefix=somePrefix&marker=someMarker&max-keys=20

    规范查询字符串如下所示(为便于阅读,此示例中添加了换行符:):

    UriEncode("marker")+"="+UriEncode("someMarker")+"&"+ UriEncode("max-keys")+"="+UriEncode("20") + "&" + UriEncode("prefix")+"="+UriEncode("somePrefix")

    当请求针对子资源时,相应的查询参数值将为空字符串(“”)。例如,以下 URI 标识了 examplebucket 存储桶上的 ACL 子资源:

    http://s3.amazonaws.com/examplebucket?acl

    本例中的 CanonicalQueryString 如下:

    UriEncode("acl") + "=" + ""

    如果 URI 不包含“?”,则请求中没有查询字符串,并且您需要将规范查询字符串设置为空字符串(“”)。此外,仍需包含“\n”。

  • CanonicalHeaders 是请求标头及其值的列表。各个标头名称和值对用换行符(“\n”)分隔。标头名称必须为小写。您必须按字母顺序对标头名称进行排序才能构造字符串,如以下示例所示:

    Lowercase(<HeaderName1>)+":"+Trim(<value>)+"\n" Lowercase(<HeaderName2>)+":"+Trim(<value>)+"\n" ... Lowercase(<HeaderNameN>)+":"+Trim(<value>)+"\n"

    上一部分介绍了本示例中使用的 Lowercase() 和 Trim() 函数。

    CanonicalHeaders 列表必须包含以下内容:

    • HTTP host 标头。

    • 如果请求中存在 Content-MD5 标头,则必须将其添加到 CanonicalHeaders 列表中。

    • 此外,还必须添加计划在请求中包含的所有 x-amz-* 标头。例如,如果您使用临时安全凭证,则请求中必须包含 x-amz-security-token。您必须将此标头添加到 CanonicalHeaders 列表中。

    注意
    • 所有 AWS 签名版本 4 请求都需要 x-amz-content-sha256 标头。它将提供请求负载的哈希。但是,您无需将 x-amz-content-sha256 作为规范标头包含在内,因为 S3 在计算请求中发送的有效载荷哈希值时会自动使用其值。

    • 如果不包含有效负载,则必须提供空字符串的哈希值。

    • 如果您不希望 S3 检查请求的哈希值,则可以改用文本字符串 "UNSIGNED-PAYLOAD"

    以下是示例 CanonicalHeaders 字符串。标头名称为小写且已排序。

    例示例 1
    host:s3.amazonaws.com x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-amz-date:20130708T220855Z
    例示例 2
    host:s3.amazonaws.com x-amz-content-sha256:UNSIGNED-PAYLOAD x-amz-date:20130708T220855Z
    例示例 3
    host:s3.amazonaws.com x-amz-date:20130708T220855Z
    注意

    为了计算授权签名,需要主机标头和所有 x-amz-* 标头(不包括 x-amz-content-sha256)。但是,为了防止篡改数据,您应该考虑在签名计算中包含所有标头。

    x-amz-content-sha256 标头进行签名是可选的,因为 S3 将在计算收到的请求有效载荷哈希值时使用其值。

  • SignedHeaders 是按字母顺序排序、以分号分隔的小写请求标头名称的列表。列表中的请求标头与您在 CanonicalHeaders 字符串中包含的标头相同。在前面的示例中,SignedHeaders 的值如下:

    例示例 1 和 2
    host;x-amz-content-sha256;x-amz-date
    例示例 3
    host;x-amz-date
  • HashedPayload 是请求有效载荷的 SHA256 哈希值的十六进制值。

    Hex(SHA256Hash(<payload>)

    如果请求中不包含有效负载,则按如下方式计算空字符串的哈希值:

    Hex(SHA256Hash(""))

    哈希值将返回以下值:

    e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

    例如,当您使用 PUT 请求上传对象时,您需要在正文中提供对象数据。当您使用 GET 请求检索对象时,您将计算空字符串哈希值。

任务 2:创建待签字符串

本节提供创建待签字符串的概述。有关分步说明,请参阅《AWS 一般参考》中的任务 2:创建待签字符串

待签字符串是以下字符串的结合:

"AWS4-HMAC-SHA256" + "\n" + timeStampISO8601Format + "\n" + <Scope> + "\n" + Hex(SHA256Hash(<CanonicalRequest>))

常量字符串 AWS4-HMAC-SHA256 指定您正在使用的哈希算法 HMAC-SHA256。timeStamp 是采用 ISO 8601 格式的当前 UTC 时间(例如 20130524T000000Z)。

Scope 将生成的签名绑定到特定的日期、AWS 区域和服务。这样,生成的签名将仅在特定区域和特定服务中有效。签名在指定日期后的七天内有效。

date.Format(<YYYYMMDD>) + "/" + <region> + "/" + <service> + "/aws4_request"

对于 Amazon S3,服务字符串为 s3。有关 region 字符串的列表,请参阅《AWS 一般参考》中的区域和端点。此表中的“区域”列提供了有效区域字符串的列表。

以下范围将生成的签名限制为 us-east-1 区域和 Amazon S3。

20130606/us-east-1/s3/aws4_request

注意

Scope 必须使用与用于计算签名密钥的相同日期,如下一节所述。

任务 3:计算签名

在 AWS 签名版本 4 中,您首先创建一个范围仅限于特定区域和服务的签名密钥,而不是使用 AWS 访问密钥对请求进行签名。 有关签名密钥的更多信息,请参阅对请求进行签名简介

DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>") DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>") DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>") SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")

注意

一些使用案例可以处理签名密钥长达 7 天。有关更多信息,请参阅与他人共享对象

有关区域字符串的列表,请参阅《AWS 一般参考》中的区域和端点

使用签名密钥,您可以将 AWS 凭证保存在一个安全的地方。例如,如果您有多个服务器与 Amazon S3 通信,则可以与这些服务器共享签名密钥;您不必在每个服务器上保留秘密访问密钥的副本。签名密钥的有效期最长为七天。因此,每次计算签名密钥时,都需要与服务器共享签名密钥。有关更多信息,请参阅 对请求进行身份验证(AWS 签名版本 4)

最终签名是待签字符串的 HMAC-SHA256 哈希值,使用签名密钥作为密钥。

HMAC-SHA256(SigningKey, StringToSign)

有关创建签名的分步说明,请参阅《AWS 一般参考》中的任务 3:创建签名

示例:签名计算

您可以将本节中的示例用作参考,以检查代码中的签名计算。示例中显示的计算使用以下数据:

  • 访问密钥示例。

    参数
    AWSAccessKeyId AKIAIOSFODNN7EXAMPLE
    AWSSecretAccessKey wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  • 请求时间戳:20130524T000000Z (Fri, 24 May 2013 00:00:00 GMT)。

  • 存储桶名称 examplebucket

  • 假设存储桶位于美国东部(弗吉尼亚州北部)区域。凭证 ScopeSigning Key 计算使用 us-east-1 作为区域说明符。有关其它区域的信息,请参阅《AWS 一般参考》中的区域和端点

  • 您可以使用路径类型请求或虚拟托管类型请求。以下示例演示如何对虚拟托管类型请求进行签名,例如:

    https://examplebucket.s3.amazonaws.com/photos/photo1.jpg

    有关更多信息,请参阅《Amazon Simple Storage Service 用户指南》中的存储桶的虚拟托管

示例:GET 对象

以下示例从 examplebucket 获取对象(test.txt)的前 10 个字节。有关 API 操作的更多信息,请参阅 GetObject

GET /test.txt HTTP/1.1 Host: examplebucket.s3.amazonaws.com Authorization: SignatureToBeCalculated Range: bytes=0-9 x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-amz-date: 20130524T000000Z

由于此 GET 请求不提供任何正文内容,因此 x-amz-content-sha256 值可以是空请求正文的哈希值,也可以是文本字符串 "UNSIGNED-PAYLOAD"。以下步骤显示使用空字符串的哈希值进行签名计算以及构造 Authorization 标头的过程。

  1. StringToSign
    1. CanonicalRequest

      GET /test.txt host:examplebucket.s3.amazonaws.com range:bytes=0-9 x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-amz-date:20130524T000000Z host;range;x-amz-content-sha256;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

      在规范请求字符串中,最后一行是空请求正文的哈希值。因为请求中没有查询参数,所以第三行是空的。

    2. StringToSign

      AWS4-HMAC-SHA256 20130524T000000Z 20130524/us-east-1/s3/aws4_request 7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972

  2. SigningKey

    signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")

  3. Signature

    f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41

  4. Authorization 标头

    生成的 Authorization 标头如下:

    AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;range;x-amz-content-sha256;x-amz-date,Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41

示例:PUT 对象

此示例 PUT 请求在 examplebucket 中创建一个对象 (test$file.text)。示例假定以下各项:

  • 您正在通过添加 x-amz-storage-class 请求标头来请求 REDUCED_REDUNDANCY 作为存储类别。有关存储类别的信息,请参阅《Amazon Simple Storage Service 用户指南》中的存储类别

  • 已上传文件的内容是一个字符串 "Welcome to Amazon S3.",请求中 x-amz-content-sha256 的值基于这个字符串。

有关 API 操作的信息,请参阅 PutObject

PUT test$file.text HTTP/1.1 Host: examplebucket.s3.amazonaws.com Date: Fri, 24 May 2013 00:00:00 GMT Authorization: SignatureToBeCalculated x-amz-date: 20130524T000000Z x-amz-storage-class: REDUCED_REDUNDANCY x-amz-content-sha256: 44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072 <Payload>

以下步骤显示了签名计算。

  1. StringToSign
    1. CanonicalRequest

      PUT /test%24file.text date:Fri, 24 May 2013 00:00:00 GMT host:examplebucket.s3.amazonaws.com x-amz-content-sha256:44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072 x-amz-date:20130524T000000Z x-amz-storage-class:REDUCED_REDUNDANCY date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class 44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072

      在规范请求中,第三行是空的,因为请求中没有查询参数。可以选择对 x-amz-content-sha256 规范标头进行签名,因为其有效载荷哈希值已在请求的底部提供。最后一行是正文的哈希值,它应该与在 HTTP 请求中发送给 S3 的 x-amz-content-sha256 header 值相同。

    2. StringToSign

      AWS4-HMAC-SHA256 20130524T000000Z 20130524/us-east-1/s3/aws4_request 9e0e90d9c76de8fa5b200d8c849cd5b8dc7a3be3951ddb7f6a76b4158342019d

  2. SigningKey

    signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")

  3. Signature

    98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd

  4. Authorization 标头

    生成的 Authorization 标头如下:

    AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class,Signature=98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd

示例:GET 存储桶生命周期

以下 GET 请求检索 examplebucket 的生命周期配置。有关 API 操作的信息,请参阅 GetBucketLifecycleConfiguration

GET ?lifecycle HTTP/1.1 Host: examplebucket.s3.amazonaws.com Authorization: SignatureToBeCalculated x-amz-date: 20130524T000000Z x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

由于请求未提供任何正文内容,因此 x-amz-content-sha256 标头值是空请求正文的哈希值。以下步骤显示了签名计算。

  1. StringToSign
    1. CanonicalRequest

      GET / lifecycle= host:examplebucket.s3.amazonaws.com x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-amz-date:20130524T000000Z host;x-amz-content-sha256;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

      在规范请求中,最后一行是空请求正文的哈希值。

    2. StringToSign

      AWS4-HMAC-SHA256 20130524T000000Z 20130524/us-east-1/s3/aws4_request 9766c798316ff2757b517bc739a67f6213b4ab36dd5da2f94eaebf79c77395ca

  2. SigningKey

    signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")

  3. Signature

    fea454ca298b7da1c68078a5d1bdbfbbe0d65c699e0f91ac7a200a0136783543

  4. Authorization 标头

    生成的 Authorization 标头如下:

    AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=fea454ca298b7da1c68078a5d1bdbfbbe0d65c699e0f91ac7a200a0136783543

示例:Get 存储桶(列出对象)

以下示例从 examplebucket 存储桶中检索对象的列表。有关 API 操作的信息,请参阅 ListObjects

GET ?max-keys=2&prefix=J HTTP/1.1 Host: examplebucket.s3.amazonaws.com Authorization: SignatureToBeCalculated x-amz-date: 20130524T000000Z x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

由于请求未提供正文,因此 x-amz-content-sha256 的值是空请求正文的哈希值。以下步骤显示了签名计算。

  1. StringToSign
    1. CanonicalRequest

      GET / max-keys=2&prefix=J host:examplebucket.s3.amazonaws.com x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-amz-date:20130524T000000Z host;x-amz-content-sha256;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

      在规范请求中,最后一行是空请求正文的哈希值。

    2. StringToSign

      AWS4-HMAC-SHA256 20130524T000000Z 20130524/us-east-1/s3/aws4_request df57d21db20da04d7fa30298dd4488ba3a2b47ca3a489c74750e0f1e7df1b9b7

  2. SigningKey

    signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")

  3. Signature

    34b48302e7b5fa45bde8084f4b7868a86f0a534bc59db6670ed5711ef69dc6f7

  4. Authorization 标头

    生成的 Authorization 标头如下:

    AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=34b48302e7b5fa45bde8084f4b7868a86f0a534bc59db6670ed5711ef69dc6f7