创建已签名的 Amazon API 请求
重要
如果您使用 Amazon SDK(请参阅示例代码和库
在支持多个签名版本的区域中,手动签名请求意味着您必须指定使用的签名版本。当您向多区域访问点提供请求时,SDK 和 CLI 会自动切换为使用签名版本 4A,而无需进行其他配置。
您可以使用 Amazon SigV4 签名协议为 Amazon API 请求创建签名的请求。
-
根据请求详细信息创建规范请求。
-
使用 Amazon 凭证计算签名。
-
将此签名作为授权标头添加到请求。
然后 Amazon 复制此过程并验证签名,相应地授予或拒绝访问权限。
要了解如何使用 Amazon SigV4 对 API 请求进行签名,请参阅 请求签名示例。
下表介绍了创建签名请求过程中使用的函数。您需要为这些函数实现代码。如需了解更多信息,请参阅 Amazon 软件开发工具包中的代码示例。
函数 | 描述 |
---|---|
|
将字符串转换为小写。 |
|
base-16 编码的小写形式。 |
|
安全哈希算法(SHA)加密哈希函数。 |
|
使用 SHA256 算法和提供的签名密钥计算 HMAC。这是您使用 SigV4 签名的最终签名。 |
|
使用基于公私密钥加密的非对称签名计算椭圆曲线数字签名算法(ECDSA)签名。 |
|
处于计数器模式的 NIST SP800-108 KDF 使用 NIST SP 800-108r1 |
|
ANSI X9.62 中描述的整数函数的八位字节。 |
|
删除所有前导空格或尾随空格。 |
|
URI 对每个字节进行编码。UriEncode() 必须强制执行以下规则:
重要由于底层 RFC 中的实现差异和相关歧义,您开发平台提供的标准 UrienCode 函数可能无法正常工作。建议您编写自己的自定义 UrienCode 函数,以确保编码能够正常工作。 要查看 Java 中的 UriEncode 函数示例,请参阅 GitHub 网站上的 Java Utilities |
注意
签署请求时,您可以使用 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 请求签名的元素。
创建规范请求的哈希值
使用创建负载的哈希时所使用的相同算法来哈希规范请求。经过哈希处理的规范请求必须以小写十六进制字符串形式表示。
创建待签字符串
使用规范请求和额外信息(例如算法、请求日期、凭证范围和规范请求的哈希)创建待签字符串。
派生签名密钥
使用秘密访问密钥派生用于对请求进行签名的密钥。
计算签名
使用派生的签名密钥作为哈希密钥,对待签字符串执行加密哈希操作。
将签名添加至请求
将计算的签名添加到请求的 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
您可以在区域中使用通配符(*)来指定多个区域。在下面的示例中,标头允许在 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-*
标头为必填项;但是,为了防止数据篡改,您应该考虑在签名计算中包含所有标头。 -
-
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。
-
Service
– 包含服务代码的字符串(例如,ec2
)。 -
在上一步中创建的要签名的字符串。
要派生 SigV4 的签名密钥
-
串联
"AWS4"
和秘密访问密钥。使用密钥和数据调用哈希函数,并将连接的字符串作为密钥,而日期字符串作为数据。DateKey = hash("AWS4" + Key, Date)
-
使用密钥和数据调用哈希函数,并将上一次调用的结果作为密钥,而区域字符串作为数据。
DateRegionKey = hash(kDate, Region)
-
使用密钥和数据调用哈希函数,并将上一次调用的结果作为密钥,而服务字符串作为数据。
服务代码由服务定义。您可以在 Amazon Pricing CLI 中使用 get-products
返回服务的服务代码。 DateRegionServiceKey = hash(kRegion, Service)
-
使用密钥和数据调用哈希函数,并将上一次调用的结果作为密钥,而“aws4_request”作为数据。
SigningKey = hash(kService, "aws4_request")
派生 SigV4a 的签名密钥
要为 SigV4a 创建签名密钥,请使用以下过程从秘密访问密钥派生密钥对。有关此派生实现的示例,请参阅 Amazon 客户端身份验证的 C99 库实现
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 的签名
-
将上一次调用的结果作为密钥,要签名的字符串作为数据来调用哈希函数。使用派生的签名密钥作为此操作的哈希密钥。结果是作为二进制值的签名。
signature = hash(SigningKey,
string-to-sign
) -
将签名从二进制转换为十六进制表示形式,使用小写字符。
要计算 SigV4a 的签名
-
使用数字签名算法(ECDSA P-256),对您在上一步中创建的要签名的字符串进行签名。用于此签名的密钥是从上述秘密访问密钥派生的非对称私有密钥。
signature = base16(
ECDSA-Sign
(k,string-to-sign
)) -
将签名从二进制转换为十六进制表示形式,使用小写字符。
将签名添加至请求
将计算出的签名添加到您的请求中。
例 示例:授权标头
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
例 示例:请求在查询字符串中使用身份验证参数
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