

# Authorization 标头的签名计算：在多个分块中传输有效载荷（分块上传）（Amazon 签名版本 4）
<a name="sigv4-streaming"></a>

如[概述](sigv4-auth-using-authorization-header.md#sigv4-auth-header-overview)中所述，当使用 `Authorization` 标头对请求进行身份验证时，您可以选择分块上传有效载荷。您可以按固定大小或可变大小的分块发送数据。本节介绍分块上传中的签名计算过程、如何创建分块正文，以及延迟签名的工作原理，即您首先上传分块，然后在后续分块中发送其签名。示例部分（请参阅[示例：PUT 对象](#example-signature-calculations-streaming)）显示了签名计算和生成的 `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。

1. 创建要包含在第一个分块中的种子签名。有关更多信息，请参阅 [计算种子签名](#sigv4-chunked-upload-sig-calculation-chunk0)。

1. 创建第一个分块并对其进行流式传输。有关更多信息，请参阅 [定义分块正文](#sigv4-chunked-body-definition)。

1. 对于每个后续分块，计算在您签名的字符串中包含先前签名的分块签名，构造该分块并发送它。有关更多信息，请参阅 [定义分块正文](#sigv4-chunked-body-definition)。

1. 发送最后一个额外的分块，它与构造中的其它分块相同，但其数据字节为零。有关更多信息，请参阅 [定义分块正文](#sigv4-chunked-body-definition)。

## 计算种子签名
<a name="sigv4-chunked-upload-sig-calculation-chunk0"></a>

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

 

![计算种子签名的过程。](http://docs.amazonaws.cn/AmazonS3/latest/developerguide/images/sigV4-auth-header-chunked-seed-signature.png)


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


| 函数 | 说明 | 
| --- | --- | 
| Lowercase() | 将字符串转换为小写。 | 
| Hex() | base-16 编码的小写形式。 | 
| SHA256Hash() | 安全哈希算法（SHA）加密哈希函数。 | 
| HMAC-SHA256() | 使用 SHA256 算法和提供的签名密钥计算 HMAC。这是最终的签名。 | 
| Trim() | 删除所有前导空格或尾随空格。 | 
| UriEncode() | URI 对每个字节进行编码。UriEncode() 必须强制执行以下规则：+  URI 对除非预留字符之外的所有字节进行编码：“A”-“Z”、“a”-“z”、“0”-“9”、“-”、“.”、“\_”和“\~”。 <br />+ 空格字符是预留字符，必须编码为“%20”（而不是“\+”）。<br />+ 每个 URI 编码字节由“%”和该字节的两位十六进制值组成。<br />+ 十六进制值中的字母必须为大写，例如“%1A”。<br />+ 对除对象键名称之外的所有位置的正斜杠字符“/”进行编码。例如，如果对象键名称是 `photos/Jan/sample.jpg`，则不会对键名称中的正斜杠进行编码。 由于底层 RFC 中的实现差异和相关歧义，您开发平台提供的标准 UrienCode 函数可能无法正常工作。建议您编写自己的自定义 UrienCode 函数，以确保编码能够正常工作。 <br />以下是 Java 中的 UriEncode() 函数示例。<pre>public static String UriEncode(CharSequence input, boolean encodeSlash) {<br />          StringBuilder result = new StringBuilder();<br />          for (int i = 0; i < input.length(); i++) {<br />              char ch = input.charAt(i);<br />              if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') {<br />                  result.append(ch);<br />              } else if (ch == '/') {<br />                  result.append(encodeSlash ? "%2F" : ch);<br />              } else {<br />                  result.append(toHexUTF8(ch));<br />              }<br />          }<br />          return result.toString();<br />      }</pre> | 

有关签名过程的信息，请参阅 [Authorization 标头的签名计算：在单个分块中传输有效载荷（Amazon 签名版本 4）](sig-v4-header-based-auth.md)。过程是相同的，只是创建 `CanonicalRequest` 的过程有不同之处，如下所示：
+ 除了您计划添加的请求标头外，还必须包括以下标头：    
[See the AWS documentation website for more details](http://docs.amazonaws.cn/AmazonS3/latest/developerguide/sigv4-streaming.html)

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

## 定义分块正文
<a name="sigv4-chunked-body-definition"></a>

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

 

```
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}} 对于每个分块，您可以使用以下待签字符串计算签名。对于第一个分块，使用种子签名作为前一个签名。

  

     
![计算种子签名的过程，显示待签字符串的各个组成部分。](http://docs.amazonaws.cn/AmazonS3/latest/developerguide/images/sigV4-auth-header-chunk-signature.png)



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

## 示例：PUT 对象
<a name="example-signature-calculations-streaming"></a>

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

 
+  这些示例中的签名计算使用以下示例安全凭证。    
[See the AWS documentation website for more details](http://docs.amazonaws.cn/AmazonS3/latest/developerguide/sigv4-streaming.html)
+ 所有示例都使用请求时间戳 20130524T000000Z (`Fri, 24 May 2013 00:00:00 GMT`)。
+ 所有示例都使用 `examplebucket` 作为存储桶名称。
+ 假设存储桶位于美国东部（弗吉尼亚州北部）区域，凭证 `Scope` 和 `Signing Key` 计算使用 `us-east-1` 作为区域说明符。有关更多信息，请参阅 [Amazon Web Services 一般参考](https://docs.amazonaws.cn/general/latest/gr/rande.html#s3_region)中的*区域和终端节点*。
+ 您可以使用路径类型请求或虚拟托管类型请求。以下示例使用虚拟托管类型请求，例如：

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

  有关更多信息，请参阅《Amazon Simple Storage Service 用户指南》**中的[存储桶的虚拟托管](https://docs.amazonaws.cn/AmazonS3/latest/userguide/VirtualHosting.html)。

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

 
+ 您正在上传一个 65 KB 的文本文件，文件内容是由字母“a”组成的单字符的字符串。
+ 分块大小为 64 KB。因此，有效载荷分三个分块上传，分别为 64 KB、1 KB，以及包含 0 字节分块数据的最后一个分块。
+ 生成的对象具有键名称 `chunkObject.txt`。
+ 您正在通过添加 `x-amz-storage-class` 请求标头来请求 `REDUCED_REDUNDANCY` 作为存储类别。

有关 API 操作的信息，请参阅 [PutObject](https://docs.amazonaws.cn/AmazonS3/latest/API/API_PutObject.html)。常用请求语法如下：

```
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` 的值相同。

   1. 

**StringToSign**

       

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

       

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

1. 

**SigningKey**

    

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

    

1. 

**种子签名**

    

   ```
   4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9
   ```

    

1. 

**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
   ```

    

1. 

**分块 1：（65536 字节，字母“a”的值为 97）**

   1. 分块待签字符串：

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

       
**注意**  
有关待签字符串中每行的信息，请参阅前面的图表，其中显示了待签字符串的各个组成部分（例如，最后三行是 `previous-signature`、`hash("")` 和 `hash(current-chunk-data)`）。

   1. 分块签名：

      ```
      ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648
      ```

   1. 发送的分块数据：

      ```
      10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648
      <65536-bytes>
      ```

1. 

**分块 2：（1024 字节，字母“a”的值为 97）**

   1. 分块待签字符串：

      ```
      AWS4-HMAC-SHA256-PAYLOAD
      20130524T000000Z
      20130524/us-east-1/s3/aws4_request
      ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648
      e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
      2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a
      ```

   1. 分块签名：

      ```
      0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497
      ```

   1. 发送的分块数据：

      ```
      400;chunk-signature=0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497
      <1024 bytes>
      ```

1. 

**分块 3：（0 字节数据）**

   1. 分块待签字符串：

      ```
      AWS4-HMAC-SHA256-PAYLOAD
      20130524T000000Z
      20130524/us-east-1/s3/aws4_request
      0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497
      e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
      e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
      ```

   1. 分块签名：

      ```
      b6c6ea8a5354eaf15b3cb7646744f4275b71ea724fed81ceb9323e279d449df9
      ```

   1. 发送的分块数据：

      ```
      0;chunk-signature=b6c6ea8a5354eaf15b3cb7646744f4275b71ea724fed81ceb9323e279d449df9
      ```