

# 适用于 API 请求的 Amazon 签名版本 4
Amazon 签名版本 4

**重要**  
如果您使用 Amazon SDK（请参阅[示例代码和库](https://www.amazonaws.cn/developer/)）或 Amazon Command Line Interface（Amazon CLI）工具向 Amazon 发送 API 请求，则可以跳过签名过程，因为 SDK 和 CLI 客户端会使用您提供的访问密钥来验证您的请求。除非您有充分的理由不这样做，否则我们建议您始终使用 SDK 或 CLI。  
在支持多个签名版本的区域中，手动签名请求意味着您必须指定要使用的签名版本。当您向多区域访问点提供请求时，SDK 和 CLI 会自动切换为使用签名版本 4A，而无需进行其他配置。

您在请求中发送的身份验证信息必须包含签名。Amazon签名版本 4（SigV4）是将身份验证信息添加到 Amazon API 请求的 Amazon 签名协议。

您无法使用秘密访问密钥对 API 请求进行签名。相反，您可以使用 Sigv4 签名过程。签名请求涉及：

1. 根据请求详细信息创建规范请求。

1. 使用 Amazon 凭证计算签名。

1. 将此签名作为授权标头添加到请求。

然后 Amazon 复制此过程并验证签名，相应地授予或拒绝访问权限。

Symmetric SigV4 要求您在特定日期派生一个密钥，该密钥的作用域限定为单个 Amazon 区域的单个 Amazon 服务。这让各区域的密钥和计算得出的签名均会有所不同，也就是说，您必须知道签名要发送到哪个区域。

非对称签名版本 4（SigV4a）是支持使用新算法进行签名的扩展版本，可以生成可在多个 Amazon 区域验证的单个签名。借助 SigV4a，您可以签署多个区域的请求，在多个区域之间实现无缝路由和失效转移。在使用 Amazon SDK 或 Amazon CLI 来调用需要多区域签名的功能时，签名类型会自动改为使用 SigV4a。有关详细信息，请参阅[Amazon SigV4a 的工作原理](#how-sigv4a-works)。

## Amazon SigV4 工作原理


以下步骤介绍了使用 SigV4 计算签名的一般过程：

1. **待签字符串**取决于请求类型。例如，使用 HTTP 授权标头或查询参数进行身份验证时，可以使用请求元素组合来创建待签字符串。对于 HTTP POST 请求，请求中的 `POST` 策略是您签名的字符串。

1. **签名密钥**是一系列计算，每个步骤的结果都输入到下一个步骤中。最后一步是签名密钥。

1. Amazon 服务收到经身份验证请求时，其会使用请求中包含的身份验证信息重新创建**签名**。如果签名匹配，则服务将处理该请求。否则，服务将拒绝该请求。

有关更多信息，请参阅 [Amazon API 请求签名的元素](reference_sigv-signing-elements.md)。

## Amazon SigV4a 的工作原理


SigV4a 使用基于公私密钥加密的非对称签名。SigV4a 的作用域凭证派生过程与 SigV4 类似，唯一差异在于 SigV4a 使用相同密钥对所有请求进行签名，而无需根据日期、服务和区域派生不同的签名密钥。[椭圆曲线数字签名算法](https://csrc.nist.gov/glossary/term/ecdsa)（ECDSA）密钥对可由现有的 Amazon 秘密访问密钥派生而来。

此系统使用非对称加密来验证多区域签名，因此 Amazon 只需要存储您的公有密钥即可。公有密钥不具私密性，不能用于签署请求。多区域 API 请求必须使用非对称签名，例如 Amazon S3 多区域接入点。

以下步骤介绍了使用 SigV4a 计算签名的一般过程：

1. **待签字符串**取决于请求类型。例如，使用 HTTP 授权标头或查询参数进行身份验证时，可以使用请求元素组合来创建待签字符串。对于 HTTP POST 请求，请求中的 `POST` 策略是您签名的字符串。

1. **签名密钥**通过一系列计算从 Amazon 秘密访问密钥派生而来，每个步骤的结果都输入到下一个步骤中。最后一步将生成密钥对。

1. 当 Amazon 服务收到使用 SigV4a 签名的请求时，Amazon 仅使用密钥对的公共部分来验证签名。如果签名有效，则请求将经过身份验证，再由服务加以处理。系统将拒绝签名无效的请求。

有关用于多区域 API 请求的 SigV4a 的更多信息，请参阅 GitHub 上的 [sigv4a-signing-examples](https://github.com/aws-samples/sigv4a-signing-examples) 项目。

## 何时签署请求


编写自定义代码来将 API 请求发送给 Amazon 时，必须包含用于签署请求的代码。您可能需要编写自定义代码，原因如下：
+ 您正在使用的编程语言没有对应的 Amazon 开发工具包。
+ 您需要完全控制将请求发送给 Amazon 的方式。

API 请求使用 Amazon SigV4 验证访问权限，而 Amazon SDK 和 Amazon CLI 使用您提供的访问密钥对您的请求进行身份验证。有关使用 Amazon SDK 和 Amazon CLI 进行身份验证的更多信息，请参阅 [其他资源](#reference_aws-signing-resources)。

## 为什么签署请求


签名过程通过以下方式帮助保护请求：
+ **验证请求者的身份**

  经过身份验证的请求需要您使用访问密钥（访问密钥 ID、秘密访问密钥）创建的签名。如果您使用的是临时安全凭证，则签名计算还需要安全令牌。有关更多信息，请参阅 [Amazon 安全凭证以编程方式访问](security-creds-programmatic-access.md)。
+ **保护传输中的数据**

  为了防止传输时请求被篡改，一些请求元素将用于计算请求的哈希（摘要），得到的哈希值将包括在请求中。在 Amazon Web Services 服务 收到请求时，它将使用相同信息计算哈希，并将其与您的请求中包括的哈希值进行匹配。如果值不匹配，Amazon 将拒绝请求。
+ **防止潜在的反演攻击**

  在大多数情况下，请求必须在请求中的时间戳的 5 分钟内到达 Amazon。否则，Amazon 将拒绝该请求。

Amazon Sigv4 可以在 HTTP 授权标头中表示，也可以作为 URL 中的查询字符串表示。有关更多信息，请参阅 [身份验证方法](reference_sigv-authentication-methods.md)。

## 其他资源

+ 有关不同服务的 Sigv4 签名过程的更多信息，请参阅 [请求签名示例](reference_sigv-examples.md)。
+ 要为 Amazon CLI 的编程访问配置凭证，请参阅《Amazon Command Line Interface User Guide》**中的 [Authentication and access credentials](https://docs.amazonaws.cn/cli/latest/userguide/cli-chap-authentication.html)。
+ Amazon SDK 包括 GitHub 上用于签署 Amazon API 请求的源代码。有关代码示例，请参阅[Amazon 示例存储库中的示例项目](reference_sigv-examples.md#signature-v4-examples-sdk)。
  + 适用于 .NET 的 Amazon SDK – [AWS4Signer.cs](https://github.com/aws/aws-sdk-net/blob/master/sdk/src/Core/Amazon.Runtime/Internal/Auth/AWS4Signer.cs)
  + 适用于 C\$1\$1 的 Amazon SDK – [AWSAuthV4Signer.cpp](https://github.com/aws/aws-sdk-cpp/blob/main/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp)
  + 适用于 Go 的 Amazon SDK – [sigv4.go](https://github.com/aws/smithy-go/blob/a4c9efcda6aa54c75d1a130d1320a2709eebf51d/aws-http-auth/sigv4/sigv4.go)
  + 适用于 Java 的 Amazon SDK – [BaseAws4Signer.java](https://github.com/aws/aws-sdk-java-v2/blob/master/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java)
  + 适用于 JavaScript 的 Amazon SDK – [signature-v4](https://github.com/smithy-lang/smithy-typescript/tree/main/packages/signature-v4)
  + 适用于 PHP 的 Amazon SDK – [SignatureV4.php](https://github.com/aws/aws-sdk-php/blob/master/src/Signature/SignatureV4.php)
  + Amazon SDK for Python (Boto) – [signers.py](https://github.com/boto/botocore/blob/develop/botocore/signers.py)
  + 适用于 Ruby 的 Amazon SDK – [signer.rb](https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sigv4/lib/aws-sigv4/signer.rb)

# Amazon API 请求签名的元素
SigV4 请求元素

**重要**  
除非您使用 Amazon SDK 或 CLI，否则您必须编写代码来计算在请求中提供身份验证信息的签名。Amazon 签名版本 4 中的签名计算可能是一项复杂的任务，我们建议您尽可能使用 Amazon SDK 或 CLI。

每个使用 Signature Version 4 签名的 HTTP/HTTPS 请求都必须包含这些元素。

**Topics**
+ [

## 终端节点规范
](#endpoint-specification)
+ [

## Action
](#action)
+ [

## 操作参数
](#parameters)
+ [

## 日期
](#date)
+ [

## 身份验证信息
](#authentication)

## 终端节点规范


指定您要向其发送请求的端点的 DNS 名称。此名称通常包含服务代码和区域。例如，`us-east-1` 区域的 Amazon DynamoDB 端点为 `dynamodb.us-east-1.amazonaws.com`。

对于 HTTP/1.1 请求，您必须使用 `Host` 标头。对于 HTTP/2 请求，您可以使用 `:authority` 标头或 `Host` 标头。仅使用 `:authority` 标头以符合 HTTP/2 规范。并非所有服务都支持 HTTP/2 请求。

## Action


为服务指定 API 操作。例如，DynamoDB `CreateTable` 操作或 Amazon EC2 `DescribeInstances` 操作。

有关每项服务支持的操作，请参阅 [服务授权参考](https://docs.amazonaws.cn//service-authorization/latest/reference/reference.html)。

## 操作参数


指定请求中指定的操作的参数。每个 Amazon API 操作都有一组必备参数和可选参数。API 版本通常是必需参数。

有关 API 操作支持的参数，请参阅服务的 API 参考。

## 日期


指定请求的日期和时间。在请求中包括日期和时间有助于防止第三方拦截您的请求并稍后重新提交。您在凭证范围中指定的日期必须与您请求的日期匹配。

时间戳必须采用 UTC 表示，并具有以下 ISO 8601 格式：*YYYYMMDD*T*HHMMSS*Z。例如 `20220830T123600Z`。请勿在时间戳中包含毫秒。

您可以使用 `date` 标头或 `x-amz-date` 标头，或将 `x-amz-date` 作为查询参数包含在内。如果无法找到 `x-amz-date` 标头，则需要查找 `date` 标头。

## 身份验证信息


您发送的每个请求都必须包含以下信息。Amazon 使用这些信息来确保请求的有效性和真实性。
+ 算法 – 您在签名过程中使用的算法。
  + SigV4 – 使用 `AWS4-HMAC-SHA256` 对 `HMAC-SHA256` 哈希算法指定 Signature Version 4。
  + SigV4a – 使用 `AWS4-ECDSA-P256-SHA256` 指定 `ECDSA-P256-SHA-256` 哈希算法。
+ 凭证 – 通过将您的访问密钥 ID 和凭证范围组件串联起来而形成的字符串。
  + SigV4 – 凭证范围包括访问密钥 ID、*YYYYMMDD* 格式的日期、区域代码、服务代码和 `aws4_request` 终止字符串，用斜杠（/）分隔。区域代码、服务代码和终止字符串必须使用小写字符。

    ```
    AKIAIOSFODNN7EXAMPLE/YYYYMMDD/region/service/aws4_request
    ```
  + SigV4a – 凭证范围包括 YYYYMMDD 格式的日期、服务名称和 `aws4_request` 终止字符串，用斜杠（/）分隔。请注意，凭证范围不包括区域，因为该区域包含在单独的标头 `X-Amz-Region-Set` 中。

    ```
    AKIAIOSFODNN7EXAMPLE/YYYYMMDD/service/aws4_request
    ```
+ 已签名标头 – 签名中要包含的 HTTP 标头，用分号（;）分隔。例如 `host;x-amz-date`。

  对于 SigV4a，您必须包含一个区域集标头，该标头指定请求将在哪一组区域中生效。标头 `X-Amz-Region-Set` 被指定为逗号分隔值的列表。下面的示例显示一个区域标头，该标头允许在 us-east-1 和 us-west-1 区域中进行请求。

  ```
  X-Amz-Region-Set=us-east-1,us-west-1
  ```

  您可以在区域中使用通配符（\$1）来指定多个区域。在下面的示例中，标头允许在 us-west-1 和 us-west-2 中进行请求。

  ```
  X-Amz-Region-Set=us-west-*
  ```
+ 签名 – 代表计算得到的签名的十六进制编码字符串。您必须使用您在 `Algorithm` 参数中指定的算法来计算签名。

有关更多信息，请参阅 [身份验证方法](reference_sigv-authentication-methods.md)。

# 身份验证方法


**重要**  
除非您使用 Amazon SDK 或 CLI，否则您必须编写代码来计算在请求中提供身份验证信息的签名。Amazon 签名版本 4 中的签名计算可能是一项复杂的任务，我们建议您尽可能使用 Amazon SDK 或 CLI。

您可以使用以下方法之一快速传递身份验证信息：

## HTTP 授权标头


HTTP `Authorization` 标头是验证请求的最常用方法。所有 REST API 操作（使用 `POST` 请求的基于浏览器的上传除外）都需要此标头。

以下示例显示了 SigV4 和 SigV4a 的 `Authorization` 标头值。为便于阅读，此示例中添加了换行符。在您的代码中，该标头必须是连续的字符串。算法和凭证之间没有逗号，但是，必须使用逗号分隔其他元素。

**Example SigV4**  

```
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, 
SignedHeaders=host;range;x-amz-date, 
Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
```

**Example SigV4a**  

```
Authorization: AWS4-ECDSA-P256-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20130524/s3/aws4_request, 
SignedHeaders=host;range;x-amz-date;x-amz-region-set,
Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
```

下表介绍了上述示例中授权标头值的各个组成部分：


| 组件 | 说明 | 
| --- | --- | 
|  授权  | 用于计算签名的算法。 [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-authentication-methods.html)  | 
|  凭证  |  您的访问密钥 ID 和范围信息。 [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) <date> 值使用 YYYYMMDD 格式指定。当发送请求到 Amazon S3 时，<aws-service> 值为 S3。  | 
|  SignedHeaders  |   用于计算签名的请求标头的分号分隔列表。该列表仅包含标头名称，并且标头名称必须为小写。例如：`host;range;x-amz-date` 对于 SigV4a，您必须包含一个区域集标头，该标头指定请求将在哪一组区域中生效。标头 X-Amz-Region-Set 被指定为逗号分隔值的列表。  | 
|  签名  |  256 位签名以 64 个小写十六进制字符表示。例如：`fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024` 请注意，签名计算因所选择的传输有效负载的选项而异。  | 

## 查询字符串参数


您可以使用查询字符串在 URL 中完全表达请求。在这种情况下，您可以使用查询参数来提供请求信息，包括身份验证信息。由于请求签名是 URL 的一部分，因此这类 URL 通常称为预签名 URL。您可以使用预签名 URL 在 HTML 中嵌入可单击链接，该链接的有效期最长 7 天。有关更多信息，请参阅《Amazon S3 API Reference**》中的 [Authenticating Requests: Using Query Parameters (Amazon Signature Version 4)](https://docs.amazonaws.cn/AmazonS3/latest/API/sigv4-query-string-auth.html)。

以下示例显示了 SigV4 和 SigV4a 的预签名 URL。为便于阅读，此示例中添加了换行符：

**Example SigV4**  

```
https://s3.amazonaws.com/amzn-s3-demo-bucket/test.txt ?
X-Amz-Algorithm=AWS4-HMAC-SHA256 &
X-Amz-Credential=<your-access-key-id>/20130721/<region>/s3/aws4_request &
X-Amz-Date=20130721T201207Z &
X-Amz-Expires=86400 &
X-Amz-SignedHeaders=host &X-Amz-Signature=<signature-value>
```

**Example SigV4a**  

```
http://s3.amazonaws.com/amzn-s3-demo-bucket/test.txt ?
X-Amz-Algorithm=AWS4-ECDSA-P256-SHA256 &
X-Amz-Credential=<your-access-key-id>/20240721/s3/aws4_request &
X-amz-Region-Set=<regionset> &
X-Amz-Date=20240721T201207Z &
X-Amz-Expires=86400 &
X-Amz-SignedHeaders=host;x-amz-region-set &
X-Amz-Signature=<signature-value>
```

**注意**  
URL 中的 `X-Amz-Credential` 值显示“/”字符只是为了方便阅读。实际上，它应该编码为 %2F。例如：  
`&X-Amz-Credential=<your-access-key-id>%2F20130721%2Fus-east-1%2Fs3%2Faws4_request`

下表介绍了 URL 中提供身份验证信息的查询参数。


| 查询字符串参数名称 | 说明 | 
| --- | --- | 
|  X-Amz-Algorithm  |  Amazon 签名的版本和用于计算签名的算法。 [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-authentication-methods.html)  | 
|  X-Amz-Credential  |  除了访问密钥 ID 外，该参数还提供签名有效的范围。该值必须与您在签名计算中使用的范围匹配（后续部分将对此进行说明）。 [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) 有关 Amazon 区域字符串的列表，请参阅《Amazon General Reference**》中的 [Regional Endpoints](https://docs.amazonaws.cn//general/latest/gr/rande.html#regional-endpoints)。  | 
|  X-Amz-Region-Set  |  请求将在其中生效的区域集。标头 x-amz-region-set 被指定为逗号分隔值的列表。  | 
|  X-Amz-Date  |  日期和时间格式必须遵循 ISO 8601 标准，并且必须按照 `yyyyMMddTHHmmssZ` 格式进行格式化。例如，如果日期和时间是“08/01/2016 15:32:41.982-700”，则必须先将其转换为 UTC（协调世界时），然后以“20160801T223241Z”形式提交。  | 
|  X-Amz-Expires  |  提供生成的预签名 URL 的有效时间段（以秒为单位）。例如，86400（24 小时）。该值是一个整数。您可以设置的最小值为 1，最大值为 604800（七天）。预签名 URL 的有效期长达七天，因为您在签名计算中使用的签名密钥的有效期最长为七天。  | 
|  X-Amz-SignedHeaders  |  列出用于计算签名的标头。签名计算中需要以下标头： [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) 为了提高安全性，您应该签署计划在请求中包含的所有请求标头。  | 
|  X-Amz-Signature  |  提供签名以验证您的请求。该签名必须与服务计算的签名相匹配；否则服务会拒绝该请求。例如，`733255ef022bec3f2a8701cd61d4b371f3f28c9f193a1f02279211d48d5193d7` 以下部分将介绍签名计算：  | 
|  X-Amz-Security-Token  |  如果使用来自 STS 服务的凭证，则为可选凭证参数。  | 

# 创建已签名的 Amazon API 请求
创建已签名的请求

**重要**  
如果您使用 Amazon SDK（请参阅[示例代码和库](https://www.amazonaws.cn/developer/)）或 Amazon Command Line Interface（Amazon CLI）工具向 Amazon 发送 API 请求，则可以跳过本部分，因为 SDK 和 CLI 客户端会使用您提供的访问密钥来验证您的请求。除非您有充分的理由不这样做，否则我们建议您始终使用 SDK 或 CLI。  
在支持多个签名版本的区域中，手动签名请求意味着您必须指定使用的签名版本。当您向多区域访问点提供请求时，SDK 和 CLI 会自动切换为使用签名版本 4A，而无需进行其他配置。

您可以使用 Amazon SigV4 签名协议为 Amazon API 请求创建签名的请求。

1. 根据请求详细信息创建规范请求。

1. 使用 Amazon 凭证计算签名。

1. 将此签名作为授权标头添加到请求。

然后 Amazon 复制此过程并验证签名，相应地授予或拒绝访问权限。

要了解如何使用 Amazon SigV4 对 API 请求进行签名，请参阅 [请求签名示例](reference_sigv-examples.md)。

下表介绍了创建签名请求过程中使用的函数。您需要为这些函数实现代码。如需了解更多信息，请参阅 [Amazon 软件开发工具包中的代码示例](reference_sigv.md#reference_aws-signing-resources)。


| 函数 | 说明 | 
| --- | --- | 
|  `Lowercase()`  |  将字符串转换为小写。  | 
|  `Hex()`  |  base-16 编码的小写形式。  | 
|  `SHA256Hash()`  |  安全哈希算法（SHA）加密哈希函数。  | 
|  `HMAC-SHA256()`  |  使用 SHA256 算法和提供的签名密钥计算 HMAC。这是您使用 SigV4 签名的最终签名。  | 
|  `ECDSA-Sign`  |  使用基于公私密钥加密的非对称签名计算椭圆曲线数字签名算法（ECDSA）签名。  | 
|  `KDF(K, Label, Context, L)`  |  处于计数器模式的 NIST SP800-108 KDF 使用 [NIST SP 800-108r1](https://doi.org/10.6028/NIST.SP.800-108r1-upd1) 中定义的 PRF 函数 HMAC-SHA256。  | 
|  `Oct2Int(byte[ ])`  |  ANSI X9.62 中描述的整数函数的八位字节。  | 
|  `Trim()`  |  删除所有前导空格或尾随空格。  | 
|  `UriEncode()`  |  URI 对每个字节进行编码。UriEncode() 必须强制执行以下规则： [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-create-signed-request.html)  由于底层 RFC 中的实现差异和相关歧义，您开发平台提供的标准 UrienCode 函数可能无法正常工作。建议您编写自己的自定义 UrienCode 函数，以确保编码能够正常工作。  要查看 Java 中的 UriEncode 函数示例，请参阅 GitHub 网站上的 [Java Utilities](https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/util/SdkHttpUtils.java#L66)。  | 

**注意**  
签署请求时，您可以使用 Amazon SigV4 或 Amazon SigV4a。两者之间的关键区别取决于签名的计算方式。使用 SigV4a 时，区域集包含在待签字符串中，但不是凭证派生步骤的一部分。

## 使用临时安全凭证签名请求


您可使用 Amazon Security Token Service（Amazon STS）提供的临时安全凭证来签署请求，而不是使用长期凭证。

使用临时安全凭证时，必须将 `X-Amz-Security-Token` 添加至授权标头或将其包含在查询字符串中以保存会话令牌。某些服务会要求您将 `X-Amz-Security-Token` 添加至规范请求。其他服务仅会要求您在计算出签名后在末尾添加 `X-Amz-Security-Token`。有关具体要求，请查看每个 Amazon Web Services 服务的文档。

## 签名步骤摘要


**创建规范请求**  
将请求的内容（主机、操作、标头等）组织为标准规范格式。规范请求是用于创建待签字符串的输入之一。有关创建规范请求的详细信息，请参阅[Amazon API 请求签名的元素](reference_sigv-signing-elements.md)。

**创建规范请求的哈希值**  
使用创建负载的哈希时所使用的相同算法来哈希规范请求。经过哈希处理的规范请求必须以小写十六进制字符串形式表示。

**创建待签字符串**  
使用规范请求和额外信息（例如算法、请求日期、凭证范围和规范请求的哈希）创建待签字符串。

**派生签名密钥**  
使用秘密访问密钥派生用于对请求进行签名的密钥。

**计算签名**  
使用派生的签名密钥作为哈希密钥，对待签字符串执行加密哈希操作。

**将签名添加至请求**  
将计算的签名添加到请求的 HTTP 标头或查询字符串中。

## 创建规范请求


要创建规范请求，请串联由换行符分隔的以下字符串。这有助于确保您计算出的签名能够与 Amazon 计算出的签名相匹配。

```
<HTTPMethod>\n
<CanonicalURI>\n
<CanonicalQueryString>\n
<CanonicalHeaders>\n
<SignedHeaders>\n
<HashedPayload>
```
+ *HTTPMethod* – HTTP 方法，例如 `GET`、`PUT`、`HEAD` 和 `DELETE`。
+ *CanonicalUri*：绝对路径组件 URI 的 URI 编码版本，以域名后面的 `/` 开头，直至字符串结尾处，或者如果包含查询字符串参数，则直至问号字符（`?`）。如果绝对路径为空，则使用正斜杠字符（`/`）。以下示例中的 URI `/amzn-s3-demo-bucket/myphoto.jpg` 是绝对路径，并且您无需在绝对路径中对 `/` 进行编码：

  ```
  http://s3.amazonaws.com/amzn-s3-demo-bucket/myphoto.jpg
  ```
+ *CanonicalQueryString* – URI 编码的查询字符串参数。您可以单独对每个名称和值进行 URI 编码。您还必须按键名称的字母顺序对规范查询字符串中的参数进行排序。编码后进行排序。以下 URI 示例中的查询字符串是：

  ```
  http://s3.amazonaws.com/amzn-s3-demo-bucket?prefix=somePrefix&marker=someMarker&max-keys=2
  ```

  规范查询字符串如下所示（为便于阅读，此示例中添加了换行符：）：

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

  当请求针对子资源时，相应的查询参数值将为空字符串（`""`）。例如，以下 URI 标识了 `amzn-s3-demo-bucket` 存储桶上的 `ACL` 子资源：

   

  ```
  http://s3.amazonaws.com/amzn-s3-demo-bucket?acl
  ```

  在这种情况下，CanonicalQueryString 将为：

   

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

  如果 URI 不包含 `?`，则请求中没有查询字符串，并且您需要将规范查询字符串设置为空字符串（`""`）。您仍然需要包含换行符 (`"\n"`)。
+ *CanonicalHeaders*：请求标头及其值的列表。各个标头名称和值对用换行符（`"\n"`）分隔。以下是 CanonicalHeader 的示例：

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

  CanonicalHeaders 列表必须包含以下内容：
  + HTTP `host` 标头。
  + 如果请求中存在 `Content-Type` 标头，则必须将其添加到 *CanonicalHeaders* 列表中。
  + 此外，还必须添加计划在请求中包含的所有 `x-amz-*` 标头。例如，如果您使用临时安全凭证，则请求中必须包含 `x-amz-security-token`。您必须将此标头添加到 *CanonicalHeaders* 列表中。
  + 对于 SigV4a，您必须包含一个区域集标头，该标头指定请求将在哪一组区域中生效。标头 `X-Amz-Region-Set` 被指定为逗号分隔值的列表。下面的示例显示一个区域标头，该标头允许在 us-east-1 和 us-west-1 区域中进行请求。

    `X-Amz-Region-Set=us-east-1,us-west-1 `

    您可以在区域中使用通配符（\$1）来指定多个区域。在下面的示例中，标头允许在 us-west-1 和 us-west-2 中进行请求。

    `X-Amz-Region-Set=us-west-*`
**注意**  
`x-amz-content-sha256` 标题是 Amazon S3 Amazon 请求所必需的。它将提供请求负载的哈希。如果不包含有效负载，则必须提供空字符串的哈希值。

  每个标头名称必须：
  + 使用小写字符。
  + 按字母顺序显示。
  + 后跟冒号（`:`）。

  对于值，您必须：
  + 去除任何前导空格或尾随空格。
  + 将连续空格转换为单个空格。
  + 使用逗号分隔多值标头的值。
  + 签名中必须包含 host 标头（HTTP/1.1）或 :authority 标头（HTTP/2）以及任何 `x-amz-*` 标头。签名中也可以包含其他标准标头，例如 content-type。

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

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

   

  ```
  host:s3.amazonaws.com
  x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  x-amz-date:20130708T220855Z
  ```

   
**注意**  
为了计算授权签名，只有主机以及任何 `x-amz-*` 标头为必填项；但是，为了防止数据篡改，您应该考虑在签名计算中包含其他标头。  
请勿包含会在复杂系统传输中频繁更改的逐跳标头。这包括所有由代理、负载均衡器和分布式系统中的节点改变的易失性传输标头，包括 `connection`、`x-amzn-trace-id`、`user-agent`、`keep-alive`、`transfer-encoding`、`TE`、`trailer`、`upgrade`、`proxy-authorization` 和 `proxy-authenticate`。
+ *SignedHeaders*：按字母顺序排序、以分号分隔的小写请求标头名称列表。列表中的请求标头与您在 `CanonicalHeaders` 字符串中包含的标头相同。对于前面的示例，*SignedHeaders* 的值如下：

  ```
  host;x-amz-content-sha256;x-amz-date
  ```
+ *HashedPayload* – 使用 HTTP 请求正文中的负载作为哈希函数的输入创建的字符串。此字符串使用小写十六进制字符。

  ```
  Hex(SHA256Hash(<payload>>))
  ```

  如果请求中不包含有效负载，则计算空字符串的哈希值，例如，当使用 `GET` 请求检索对象时，有效负载中没有任何内容。

  ```
  Hex(SHA256Hash(""))
  ```
**注意**  
对于 Amazon S3，请在构造规范请求时包含文字字符串 `UNSIGNED-PAYLOAD`，并在发送请求时设置与 `x-amz-content-sha256` 标头值相同的值。  
`Hex(SHA256Hash("UNSIGNED-PAYLOAD"))`

## 创建规范请求的哈希值


使用创建负载的哈希时所使用的相同算法来创建规范请求的哈希（摘要）。经过哈希处理的规范请求必须以小写十六进制字符串形式表示。

## 创建待签字符串


要创建待签字符串，请串联以下由换行符分隔的以下字符串。请勿使用换行符作为此字符串的结尾。

```
Algorithm \n
RequestDateTime \n
CredentialScope  \n
HashedCanonicalRequest
```
+ *Algorithm* – 用于创建规范请求的哈希的算法。
  + SigV4 – 使用 `AWS4-HMAC-SHA256` 指定 `HMAC-SHA256` 哈希算法。
  + SigV4a – 使用 `AWS4-ECDSA-P256-SHA256` 指定 `ECDSA-P256-SHA-256` 哈希算法。
+ *RequestDateTime* – 在凭证范围内使用的日期和时间。该值是采用 ISO 8601 格式的当前 UTC 时间（例如 `20130524T000000Z`）。
+ *CredentialScope*：凭证范围，将生成的签名限制在指定的区域和服务范围内。
  + SigV4 – 凭证包括访问密钥 ID、`YYYYMMDD` 格式的日期、区域代码、服务代码和 `aws4_request` 终止字符串，用斜杠（/）分隔。区域代码、服务代码和终止字符串必须使用小写字符。字符串具有以下格式：`YYYYMMDD/region/service/aws4_request`。
  + SigV4a – 凭证包括 `YYYYMMDD` 格式的日期、服务名称和 `aws4_request` 终止字符串，用斜杠（/）分隔。请注意，凭证范围不包括区域，因为该区域包含在单独的标头 `X-Amz-Region-Set` 中。字符串具有以下格式：`YYYYMMDD/service/aws4_request`。
+ *HashedCanonicalRequest*：上一步中计算出的规范请求的哈希。

以下是要签名的字符串的示例。

```
"<Algorithm>" + "\n" +
timeStampISO8601Format + "\n" +
<Scope> + "\n" +
Hex(<Algorithm>(<CanonicalRequest>))
```

## 派生签名密钥


要派生签名密钥，请选择以下过程之一来计算 SigV4 或 SigV4a 的签名密钥。

### 派生 SigV4 的签名密钥


要派生 SigV4 的签名密钥，请使用 Amazon 秘密访问密钥作为初始哈希操作的密钥，对请求日期、区域和服务执行一系列加密哈希操作（HMAC）。

对于每个步骤，使用所需的密匙和数据调用哈希函数。每次调用哈希函数的结果都会变成下一次调用哈希函数的输入。

以下示例说明了如何派生本过程下一部分中使用的 `SigningKey`，并说明了输入的串联和哈希顺序。`HMAC-SHA256` 是用于对数据进行哈希处理的哈希功能，如下所示。

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

**必填项**
+ `Key` – 包含您的秘密访问密钥的字符串。
+ `Date` – 包含在凭证范围中使用的日期的字符串，格式为 *YYYYMMDD*。
+ `Region` – 包含区域代码的字符串（例如，`us-east-1`）。

  有关区域字符串的列表，请参阅 *Amazon Web Services 一般参考* 中的 [Regional Endpoints](https://docs.amazonaws.cn//general/latest/gr/rande.html#regional-endpoints)。
+ `Service` – 包含服务代码的字符串（例如，`ec2`）。
+ 在上一步中创建的要签名的字符串。

**要派生 SigV4 的签名密钥**

1. 串联 `"AWS4"` 和秘密访问密钥。使用密钥和数据调用哈希函数，并将连接的字符串作为密钥，而日期字符串作为数据。

   ```
   DateKey = hash("AWS4" + Key, Date)
   ```

1. 使用密钥和数据调用哈希函数，并将上一次调用的结果作为密钥，而区域字符串作为数据。

   ```
   DateRegionKey = hash(kDate, Region)
   ```

1. 使用密钥和数据调用哈希函数，并将上一次调用的结果作为密钥，而服务字符串作为数据。

   服务代码由服务定义。您可以在 *Amazon Pricing CLI* 中使用 [get-products](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/pricing/get-products.html) 返回服务的服务代码。

   ```
   DateRegionServiceKey = hash(kRegion, Service)
   ```

1. 使用密钥和数据调用哈希函数，并将上一次调用的结果作为密钥，而“aws4\$1request”作为数据。

   ```
   SigningKey = hash(kService, "aws4_request")
   ```

### 派生 SigV4a 的签名密钥


要为 SigV4a 创建签名密钥，请使用以下过程从秘密访问密钥派生密钥对。有关此派生实现的示例，请参阅 [Amazon 客户端身份验证的 C99 库实现](https://github.com/awslabs/aws-c-auth/blob/e8360a65e0f3337d4ac827945e00c3b55a641a5f/source/key_derivation.c#L291.) 

```
n = [NIST P-256 elliptic curve group order]
G = [NIST P-256 elliptic curve base point]
label = "AWS4-ECDSA-P256-SHA256"

akid = [Amazon access key ID as a UTF8 string]
sk = [Amazon secret access Key as a UTF8 Base64 string]

input_key = "AWS4A" || sk
count = 1
while (counter != 255) {
  context = akid || counter // note: counter is one byte
  key = KDF(input_key, label, context, 256)
  c = Oct2Int(key)
  if (c > n - 2) {
    counter++
  } else {
    k = c + 1   // private key
    Q = k * G   // public key
  }
}

if (c < 255) {
  return [k, Q]
} else {
  return FAILURE
}
```

## 计算签名


派生签名密钥后，计算要添加到请求中的签名。此过程因您使用的签名版本而不同。

**要计算 SigV4 的签名**

1. 将上一次调用的结果作为密钥，**要签名的字符串**作为数据来调用哈希函数。使用派生的签名密钥作为此操作的哈希密钥。结果是作为二进制值的签名。

   ```
   signature = hash(SigningKey, string-to-sign)
   ```

1. 将签名从二进制转换为十六进制表示形式，使用小写字符。

**要计算 SigV4a 的签名**

1. 使用数字签名算法（ECDSA P-256），对您在上一步中创建的**要签名的字符串**进行签名。用于此签名的密钥是从上述秘密访问密钥派生的非对称私有密钥。

   ```
   signature = base16(ECDSA-Sign(k, string-to-sign))
   ```

1. 将签名从二进制转换为十六进制表示形式，使用小写字符。

## 将签名添加至请求


将计算出的签名添加到您的请求中。

**Example 示例：授权标头**  
**SigV4**  
以下示例显示了使用 Amazon SigV4 的 `DescribeInstances` 操作的 `Authorization` 标头。为便于阅读，此示例已使用换行符编排过格式。在您的代码中，这必须是连续的字符串。算法和 `Credential` 之间没有逗号。但是，必须使用逗号分隔其他元素。

```
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request,
SignedHeaders=host;x-amz-date,
Signature=calculated-signature
```

**SigV4a**  
以下示例显示了使用 Amazon SigV4a 的 `CreateBucket` 操作的授权标头。为便于阅读，此示例已使用换行符编排过格式。在您的代码中，这必须是连续的字符串。算法和凭证之间没有逗号。但是，必须使用逗号分隔其他元素。

```
Authorization: AWS4-ECDSA-P256-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20220830/s3/aws4_request,
SignedHeaders=host;x-amz-date;x-amz-region-set,
Signature=calculated-signature
```

**Example 示例：请求在查询字符串中使用身份验证参数**  
**SigV4**  
以下示例显示了对包含身份验证信息的使用 Amazon SigV4 的 `DescribeInstances` 操作的查询。为便于阅读，此示例已使用换行符编排过格式，而非 URL 编码。在您的代码中，查询字符串必须是采用 URL 编码的连续字符串。

```
https://ec2.amazonaws.com/?
Action=DescribeInstances&
Version=2016-11-15&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request&
X-Amz-Date=20220830T123600Z&
X-Amz-SignedHeaders=host;x-amz-date&
X-Amz-Signature=calculated-signature
```

**SigV4a**  
以下示例显示了对包含身份验证信息的使用 Amazon SigV4a 的 `CreateBucket` 操作的查询。为便于阅读，此示例已使用换行符编排过格式，而非 URL 编码。在您的代码中，查询字符串必须是采用 URL 编码的连续字符串。

```
https://ec2.amazonaws.com/?
Action=CreateBucket&
Version=2016-11-15&
X-Amz-Algorithm=AWS4-ECDSA-P256-SHA256&
X-Amz-Credential=AKIAIOSFODNN7EXAMPLE/20220830/s3/aws4_request&
X-Amz-Region-Set=us-west-1&
X-Amz-Date=20220830T123600Z&
X-Amz-SignedHeaders=host;x-amz-date;x-amz-region-set&
X-Amz-Signature=calculated-signature
```

# 请求签名示例


Amazon 签名请求的以下示例向您介绍如何使用 SigV4 对在没有 Amazon SDK 或 Amazon 命令行工具的情况下发送的请求进行签名。

## 使用 HTTP POST 进行基于浏览器的 Amazon S3 上传


 [对请求进行身份验证：基于浏览器的上传](https://docs.amazonaws.cn/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html)介绍 Amazon S3 在收到请求时用来计算签名的签名和相关信息。

[示例：使用 HTTP POST 进行基于浏览器的上传（使用 Amazon 签名版本 4）](https://docs.amazonaws.cn/AmazonS3/latest/API/sigv4-post-example.html)提供更多信息，其中包含示例 POST 策略和可用于上传文件的表单。示例策略和虚拟凭证向您介绍工作流程以及生成的签名和策略哈希。

## VPC Lattice 经过身份验证的请求


 [签名版本 4（SigV4）经过身份验证的请求示例](https://docs.amazonaws.cn/vpc-lattice/latest/ug/sigv4-authenticated-requests.html)提供 Python 和 Java 示例，显示了如何在使用和不使用自定义拦截器的情况下执行请求签名。

## 对 Amazon Translate 使用签名版本 4


 [元宇宙中的实时翻译](https://www.amazonaws.cn/blogs/spatial/live-translations-in-the-metaverse/)展示了如何构建能够生成近乎实时的翻译解决方案的应用程序。这种语音到语音翻译器解决方案在事件流编码中使用 Amazon SigV4 来生成实时转录。

## 对 Neptune 使用签名版本 4


[示例：搭配使用 Python 和签名版本 4 签名连接到 Neptune](https://docs.amazonaws.cn/neptune/latest/userguide/iam-auth-connecting-python.html) 介绍如何使用 Python 向 Neptune 发出签名请求。此示例包括使用访问密钥或临时凭证的变体。

## 向 Amazon Glacier 签署 HTTP 请求


[流式处理 API 的签名计算示例](https://docs.amazonaws.cn/amazonglacier/latest/dev/amazon-glacier-signing-requests.html)介绍为上传档案（发布档案）创建签名的详细信息，上传档案（发布档案）是 Amazon Glacier 中的两个流式处理 API 之一。

## 向 Amazon SWF 发出 HTTP 请求


[向 Amazon SWF 发出 HTTP 请求](https://docs.amazonaws.cn/amazonswf/latest/developerguide/UsingJSON-swf.html#HTTPHeader)显示了向 Amazon SWF 发出的 JSON 请求的标题内容。

## Amazon OpenSearch Service 中流式处理 API 的签名计算


[使用适用于 PHP 的 Amazon SDK 版本 3 对 Amazon OpenSearch Service 搜索请求签名](https://docs.amazonaws.cn/sdk-for-php/v3/developer-guide/service_es-data-plane.html)包括如何向 Amazon OpenSearch Service 发送已签名的 HTTP 请求的示例。

## Amazon 示例存储库中的示例项目


以下示例项目显示了如何签署请求，以便使用 Python、Node.js、Java、C\$1、Go 和 Rust 等常见语言向 Amazon 服务发出 Rest API 请求。

### 签名版本 4a 项目


[sigv4-signing-examples](https://github.com/aws-samples/sigv4-signing-examples) 项目提供了如何使用 SigV4 签署请求，以便使用 Python、Node.js、Java、C\$1、Go 和 Rust 等常用语言向 Amazon Web Services 服务 发出 Rest API 请求的示例。

[sigv4a-signing-examples](https://github.com/aws-samples/sigv4a-signing-examples) 项目提供了签署多区域 API 请求的示例，例如 [Amazon S3 中的多区域访问点](https://docs.amazonaws.cn/AmazonS3/latest/userguide/MultiRegionAccessPoints.html)。

### 发布到 Amazon IoT Core


[使用 HTTPS 协议发布到 Amazon IoT Core 的 Python 代码](https://github.com/aws-samples/aws-iot-core-python-node-sigv4-https)提供了如何使用 HTTPS 协议和 Amazon SigV4 身份验证向 Amazon IoT Core 发布信息的指导。它有两个参考实施，一个在 Python 中，另一个在 NodeJS 中。

[使用 HTTPS 协议发布到 Amazon IoT Core 的 .Net Framework 应用程序](https://github.com/aws-samples/aws-iot-core-http-sigv4-dotnet-app)提供了如何使用 HTTPS 协议和 Amazon SigV4 身份验证向 Amazon IoT Core 发布信息的指导。该项目还包括一个 .NET Core 等效实施。

# 排查 Amazon API 请求的签名版本 4 签名问题
排查 SigV4 问题

**重要**  
除非您使用 Amazon SDK 或 CLI，否则您必须编写代码来计算在请求中提供身份验证信息的签名。SigV4 签名计算可能十分复杂，我们建议您尽可能使用 Amazon SDK 或 CLI。

在开发可创建已签名请求的代码时，您可能会从 Amazon Web Services 服务收到 HTTP 403 `SignatureDoesNotMatch` 错误。此类错误表示您对 Amazon 发出的 HTTP 请求中的签名值与 Amazon Web Services 服务 计算出的签名不一致。当权限不允许调用者发出请求时，系统会返回 HTTP 401 `Unauthorized` 错误。

出现以下情况时，API 请求可能会返回错误：
+ API 请求未签名，并且 API 请求使用的是 IAM 身份验证。
+ 用于签署请求的 IAM 证书不正确或无权调用该 API。
+ 已签名 API 请求的签名与 Amazon 服务计算出的签名不一致。
+ API 请求标头不正确。

**注意**  
请首先将签名协议从 Amazon 签名版本 2（SigV2）更新为 Amazon 签名版本 4（SigV4），然后再探索其他错误解决方案。Amazon S3 等服务和区域不再支持 Sigv2 签名。

**Topics**
+ [

## 凭证错误
](#signature-v4-troubleshooting-credential)
+ [

## 规范请求和签名字符串错误
](#signature-v4-troubleshooting-canonical-errors)
+ [

## 凭证范围错误
](#signature-v4-troubleshooting-credential-scope)
+ [

## 密钥签名错误
](#signature-v4-troubleshooting-key-signing)

## 凭证错误


确保 API 请求是使用 Sigv4 签署的。如果 API 请求未签名，则可能会收到以下错误消息：`Missing Authentication Token`。[添加缺失的签名](https://docs.amazonaws.cn/IAM/latest/UserGuide/create-signed-request.html#add-signature-to-request) 并重新发送请求。

确认访问密钥和私有密钥的身份验证凭证准确无误。如果访问密钥不正确，则可能会收到以下错误消息：`Unauthorized`。确保用于签署请求的实体有权提出请求。有关更多信息，请参阅 [排查访问被拒绝错误消息](troubleshoot_access-denied.md)。

## 规范请求和签名字符串错误


如果您在 [创建规范请求的哈希值](reference_sigv-create-signed-request.md#create-canonical-request-hash) 或 [创建待签字符串](reference_sigv-create-signed-request.md#create-string-to-sign) 中计算的规范请求有误，则服务执行的签名验证步骤将会失败，并显示以下错误消息：

```
The request signature we calculated does not match the signature you provided
```

Amazon 服务收到已签名的请求后，将会重新计算签名。如果两者的值存在差异，则签名不一致。将已签名请求的规范请求和字符串与错误消息中的值进行比较。如果两者有任何差异，请修改签名过程。

**注意**  
您还可以验证您没有通过修改标头或请求的代理发送请求。

**Example 规范请求示例**  

```
GET                                                      -------- HTTP method
/                                                        -------- Path. For API stage endpoint, it should be /{stage-name}/{resource-path}
                                                         -------- Query string key-value pair. Leave it blank if the request doesn't have a query string.
content-type:application/json                            -------- Header key-value pair. One header per line.
host:0123456789.execute-api.us-east-1.amazonaws.com      -------- Host and x-amz-date are required headers for all signed requests.                       
x-amz-date:20220806T024003Z                              

content-type;host;x-amz-date                             -------- A list of signed headers
d167e99c53f15b0c105101d468ae35a3dc9187839ca081095e340f3649a04501        -------- Hash of the payload
```

要验证密钥是否与访问密钥 ID 匹配，您可以使用已知的有效实施对其进行测试。例如，使用 Amazon SDK 或 Amazon CLI 向 Amazon 发出请求。

### API 请求标头


当授权标头为空，凭证密钥或签名缺失或不正确，标头不是以算法名称开头，或者键值对不包含等号时，您会收到以下错误之一：
+ 授权标头不能为空。
+ 授权标头需要“Credential”参数。
+ 授权标头需要“Signature”参数。
+ 签名在授权标头中包含无效的 key=value 对（缺少等号）。

请确保您在 [计算签名](reference_sigv-create-signed-request.md#calculate-signature) 中添加的 SigV4 授权标头包含正确的凭证密钥，同时包含使用 HTTP 日期或 `x-amz-date` 标头的请求日期。

如果您收到 IncompleteSignatureException 错误且签名的构造正确，则可以通过计算客户端请求中授权标头的 SHA-256 哈希值和 B64 编码来验证授权标头在传输到 Amazon Web Services 服务 的过程中未被修改。

1. 获取您在请求中发送的[授权标头](https://docs.amazonaws.cn/IAM/latest/UserGuide/reference_sigv-authentication-methods.html)。您的授权标头与以下示例类似：

   ```
   Authorization: AWS4-HMAC-SHA256 
   Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, 
   SignedHeaders=host;range;x-amz-date,
   Signature=example-generated-signature
   ```

1. 计算授权标头的 SHA-256 哈希值。

   ```
   hashSHA256(rawAuthorizationHeader) = hashedAuthorizationHeader
   ```

1. 将经过哈希处理的授权标头编码为 Base64 格式。

   ```
   base64(hashedAuthorizationHeader) = encodedHashedAuthorizationHeader
   ```

1. 将刚才计算的经过哈希处理和编码的字符串与您在错误消息中收到的字符串进行比较。您的错误消息应类似于以下示例：

   ```
   com.amazon.coral.service#IncompleteSignatureException: 
   The signature contains an in-valid key=value pair (missing equal-sign) 
   in Authorization header (hashed with SHA-256 and encoded with Base64): 
   '9c574f83b4b950926da4a99c2b43418b3db8d97d571b5e18dd0e4f3c3ed1ed2c'.
   ```
+ 如果两个哈希值不同，则授权标头的某些部分在传输过程中更改。此更改可能是由于您的网络或客户端处理程序附加签名的标头或以某种方式更改授权标头。
+ 如果两个哈希值匹配，则您在请求中发送的授权标头与 Amazon 收到的内容相匹配。查看您收到的错误消息，确定问题是否是凭证或签名不正确的结果。本页面的其他部分将介绍这些错误。

## 凭证范围错误


您在 [创建待签字符串](reference_sigv-create-signed-request.md#create-string-to-sign) 中创建的凭证范围可将签名限定为特定的日期、区域和服务。此字符串具有以下格式：

```
YYYYMMDD/region/service/aws4_request
```

**注意**  
如果您使用的是 Sigv4a，则该区域未包括在凭证范围内。

**日期**  
如果凭证范围未指定与 x-amz-date 标头相同的日期，则签名验证步骤将失败，并显示以下错误消息：

```
Date in Credential scope does not match YYYYMMDD from ISO-8601 version of date from HTTP
```

如果请求指定了未来时间，则签名验证步骤将失败，并显示以下错误消息：

```
Signature not yet current: date is still later than date
```

如果请求已过期，则签名验证步骤将失败，并显示以下错误消息：

```
Signature expired: date is now earlier than date
```

**区域**  
如果凭证范围未指定与请求相同的区域，则签名验证步骤将失败，并显示以下错误消息：

```
Credential should be scoped to a valid Region, not region-code
```

**服务**  
如果凭证范围未指定与 host 标头相同的服务，则签名验证步骤将失败，并显示以下错误消息：

```
Credential should be scoped to correct service: 'service'
```

**终止字符串**  
如果凭证范围没有以 aws4\$1request 结尾，则签名验证步骤将失败，并显示以下错误消息：

```
Credential should be scoped with a valid terminator: 'aws4_request'
```

## 密钥签名错误


由于不正确地派生签名密钥或使用密码术而导致的错误更难排查。验证规范字符串和待签字符串正确无误后，还可以检查是否存在以下某个问题：
+ 秘密访问密钥与您指定的访问密钥 ID 不匹配。
+ 您的密钥派生代码存在问题。

要验证密钥是否与访问密钥 ID 匹配，您可以使用已知的有效实施对其进行测试。例如，使用 Amazon SDK 或 Amazon CLI 向 Amazon 发出请求。有关示例，请参阅[请求签名示例](reference_sigv-examples.md)。