View a markdown version of this page

Authorization 标头的签名计算:在多个分块中传输有效载荷(分块上传)(Amazon 签名版本 4) - Amazon Simple Storage Service
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

概述中所述,当使用 Authorization 标头对请求进行身份验证时,您可以选择分块上传有效载荷。您可以按固定大小或可变大小的分块发送数据。本节介绍分块上传中的签名计算过程、如何创建分块正文,以及延迟签名的工作原理,即您首先上传分块,然后在后续分块中发送其签名。示例部分(请参阅示例:PUT 对象)显示了签名计算和生成的 Authorization 标头,您可以将其用作测试套件来验证代码。

注意

在一系列分块中传输数据时,必须执行以下操作之一:

  • 使用 Content-Length HTTP 标头显式指定内容的总长度(以字节为单位的对象长度加上每个分块中的元数据)。为此,在开始请求之前,您必须预先计算有效载荷的总长度,包括您在每个分块中发送的元数据。

  • 指定 Transfer-Encoding HTTP 标头。如果您包含 Transfer-Encoding 标头并指定除 identity 之外的任何值,则必须忽略 Content-Length 标头。

对于所有请求,您都必须包含 x-amz-decoded-content-length 标头,同时指定对象的大小(以字节为单位)。

每个分块签名计算都包括前一个分块的签名。首先,您仅使用标头来创建种子签名。在第一个分块的签名计算中使用种子签名。对于每个后续分块,您都要创建一个分块签名,其中包含前一个分块的签名。这样,分块签名链接在一起;也就是说,分块 n 的签名是函数 F(chunk n, signature(chunk n-1))。链接可确保您按正确的顺序发送分块。

要执行分块上传,请执行以下操作:

  1. 确定有效载荷分块大小。编写代码时需要此信息。

    分块大小必须至少为 8 KB。为了获得更好的性能,我们建议分块大小至少为 64 KB。此分块大小适用于除最后一个分块之外的所有分块。您发送的最后一个分块可以小于 8 KB。如果有效载荷较小而适合放入一个分块中,则它可以小于 8 KB。

  2. 创建要包含在第一个分块中的种子签名。有关更多信息,请参阅 计算种子签名

  3. 创建第一个分块并对其进行流式传输。有关更多信息,请参阅 定义分块正文

  4. 对于每个后续分块,计算在您签名的字符串中包含先前签名的分块签名,构造该分块并发送它。有关更多信息,请参阅 定义分块正文

  5. 发送最后一个额外的分块,它与构造中的其它分块相同,但其数据字节为零。有关更多信息,请参阅 定义分块正文

计算种子签名

下图说明了计算种子签名的过程。

计算种子签名的过程。

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

函数 说明
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(); }

有关签名过程的信息,请参阅 Authorization 标头的签名计算:在单个分块中传输有效载荷(Amazon 签名版本 4)。过程是相同的,只是创建 CanonicalRequest 的过程有不同之处,如下所示:

  • 除了您计划添加的请求标头外,还必须包括以下标头:

    标题 说明
    x-amz-content-sha256

    所有 Amazon 签名版本 4 请求都需要此标头。将值设置为 STREAMING-AWS4-HMAC-SHA256-PAYLOAD,以表示签名仅涵盖标头且没有有效载荷。

    Content-Encoding

    将值设置为 aws-chunked

    Amazon S3 支持多个内容编码值。当使用签名版本 4 流式传输 API 时,您可以指定自定义内容编码。

    例如:

    Content-Encoding : aws-chunked,gzip

    Amazon S3 存储生成的对象,content-encoding 标头中不带 aws-chunked 值。如果 aws-chunked 是您在 content-encoding 标头中传递的唯一值,则 S3 会认为 content-encoding 标头为空,在您检索对象时不会返回此标头。

    x-amz-decoded-content-length 将值设置为要分块的数据的长度(以字节为单位),而不计入任何元数据。例如,如果您要上传一个 4 GB 的文件,请将该值设置为 4294967296。这是要上传的对象的原始大小(您要存储在 Amazon S3 中的数据)。
    Content-Length

    将该值设置为所传输的 HTTP 正文的实际大小,其中包括数据的长度(为 x-amz-decoded-content-length 设置的值)以及分块元数据。每个分块都有元数据,例如前一个分块的签名。下一节将讨论分块计算。如果您包含 Transfer-Encoding 标头并指定除 identity 之外的任何值,则不得包含 Content-Length 标头。

您发送带有种子签名的第一个分块。您必须按下一节所述构造该分块。

定义分块正文

所有分块都包含一些元数据。每个分块都必须符合以下结构:

string(IntHexBase(chunk-size)) + ";chunk-signature=" + signature + \r\n + chunk-data + \r\n

其中:

  • IntHexBase() 是您编写的用于将整数分块大小转换为十六进制的函数。例如,如果分块大小为 65536,则十六进制字符串为“10000”。

  • chunk-size 是分块数据的大小(以字节为单位),不含元数据。例如,如果您要上传一个 65 KB 的对象,并且使用的分块大小为 64 KB,则将数据分成三个分块上传:第一个为 64 KB,第二个为 1 KB,最后一个分块为 0 字节。

  • signature 对于每个分块,您可以使用以下待签字符串计算签名。对于第一个分块,使用种子签名作为前一个签名。

    计算种子签名的过程,显示待签字符串的各个组成部分。

您发送的最后一个分块数据的大小为 0,尽管分块正文仍包含元数据(包括前一个分块的签名)。

示例:PUT 对象

您可以将本节中的示例用作参考,以检查代码中的签名计算。在查看示例之前,请注意以下事项:

  • 这些示例中的签名计算使用以下示例安全凭证。

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

  • 所有示例都使用 examplebucket 作为存储桶名称。

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

  • 您可以使用路径类型请求或虚拟托管类型请求。以下示例使用虚拟托管类型请求,例如:

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

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

以下示例发送 PUT 请求以上传对象。签名计算假设如下:

  • 您正在上传一个 65 KB 的文本文件,文件内容是由字母“a”组成的单字符的字符串。

  • 分块大小为 64 KB。因此,有效载荷分三个分块上传,分别为 64 KB、1 KB,以及包含 0 字节分块数据的最后一个分块。

  • 生成的对象具有键名称 chunkObject.txt

  • 您正在通过添加 x-amz-storage-class 请求标头来请求 REDUCED_REDUNDANCY 作为存储类别。

有关 API 操作的信息,请参阅 PutObject。常用请求语法如下:

PUT /examplebucket/chunkObject.txt HTTP/1.1 Host: s3.amazonaws.com x-amz-date: 20130524T000000Z x-amz-storage-class: REDUCED_REDUNDANCY Authorization: SignatureToBeCalculated x-amz-content-sha256: STREAMING-AWS4-HMAC-SHA256-PAYLOAD Content-Encoding: aws-chunked x-amz-decoded-content-length: 66560 Content-Length: 66824 <Payload>

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

  1. 种子签名:创建待签字符串
    1. CanonicalRequest

      PUT /examplebucket/chunkObject.txt content-encoding:aws-chunked content-length:66824 host:s3.amazonaws.com x-amz-content-sha256:STREAMING-AWS4-HMAC-SHA256-PAYLOAD x-amz-date:20130524T000000Z x-amz-decoded-content-length:66560 x-amz-storage-class:REDUCED_REDUNDANCY content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-storage-class STREAMING-AWS4-HMAC-SHA256-PAYLOAD

      在规范请求中,第三行是空的,因为请求中没有查询参数。最后一行是作为经过哈希处理的有效载荷值提供的常量字符串,应与 x-amz-content-sha256 header 的值相同。

    2. StringToSign

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

      注意

      有关待签字符串中每行的信息,请参阅解释种子签名计算的图表。

  2. SigningKey

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

  3. 种子签名

    4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9

  4. Authorization 标头

    生成的 Authorization 标头如下:

    AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-storage-class,Signature=4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9

  5. 分块 1:(65536 字节,字母“a”的值为 97)
    1. 分块待签字符串:

      AWS4-HMAC-SHA256-PAYLOAD 20130524T000000Z 20130524/us-east-1/s3/aws4_request 4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 bf718b6f653bebc184e1479f1935b8da974d701b893afcf49e701f3e2f9f9c5a

      注意

      有关待签字符串中每行的信息,请参阅前面的图表,其中显示了待签字符串的各个组成部分(例如,最后三行是 previous-signaturehash("")hash(current-chunk-data))。

    2. 分块签名:

      ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648
    3. 发送的分块数据:

      10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648 <65536-bytes>
  6. 分块 2:(1024 字节,字母“a”的值为 97)
    1. 分块待签字符串:

      AWS4-HMAC-SHA256-PAYLOAD 20130524T000000Z 20130524/us-east-1/s3/aws4_request ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a
    2. 分块签名:

      0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497
    3. 发送的分块数据:

      400;chunk-signature=0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497 <1024 bytes>
  7. 分块 3:(0 字节数据)
    1. 分块待签字符串:

      AWS4-HMAC-SHA256-PAYLOAD 20130524T000000Z 20130524/us-east-1/s3/aws4_request 0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    2. 分块签名:

      b6c6ea8a5354eaf15b3cb7646744f4275b71ea724fed81ceb9323e279d449df9
    3. 发送的分块数据:

      0;chunk-signature=b6c6ea8a5354eaf15b3cb7646744f4275b71ea724fed81ceb9323e279d449df9