AWS V4签名

2022-09-26 16:33:47

本节介绍使用AWS签名版本4算法的请求身份验证。

注意

如果使用AWS软件开发工具包发送请求,则无需阅读本部分,因为SDK客户端使用您提供的访问密钥验证请求。只有在自定义客户端中实施AWS签名版本4算法时,您才需要阅读本部分。目前棱束链对象存储只支持v4签名策略。

签名方法

您可以使用以下方法之一来添加身份签名信息:

HTTP头部签名 - 使用HTTP签名头部是认证棱束链对象存储请求的最常用方法。

HTTP预签名 - 您可以使用查询字符串在URL中添加签名信息。因为请求签名是URL的一部分,所以这种类型的URL通常被称为预先签名的URL。您可以使用预先分配的网址在HTML中嵌入可点击的链接,该链接最长可以有效十五天。

HTTP头部签名

以下是基于HTTP头部签名方式的请求的Authorization标头值的示例。 为了便于阅读,在此示例中添加换行符:

Authorization: AWS4-HMAC-SHA256 
Credential=AKIAIOSFODNN7EXAMPLE/20161208/US/s3/aws4_request, 
SignedHeaders=host;range;x-amz-date, 
Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024

注意

AWS4-HMAC-SHA256和Credential之间存在一个空格

Credential,SignedHeaders和Signature由逗号分隔。

下表描述了上例中的各字段的说明:
名称 描述
AWS4-HMAC-SHA256 用于计算签名的算法。 当您使用AWS签名版本4进行身份验证时,必须提供此值。该字符串指定AWS签名版本4(AWS4)和签名算法(HMAC-SHA256)。
Credential 您的访问密钥和地域信息,包括用于计算签名的日期,区域等信息。此字符串具有以下形式: *<your-access-key>*/*<date>*/*<aws-region>*/***s3***/aws4_request Where: <date> value is specified using YYYYMMDD format.<aws-region> 主要用于后端记录用户的地域信息,目前未限定具体值
SignedHeaders 以分号分隔的请求标头列表,用于计算Signature。 该列表仅包括头名称,并且头名称必须为小写。 例如:host;range;x-amz-date
Signature 它表示为64个小写十六进制字符的256位签名. 例如:fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024

签名计算因您选择传输请求有效内容的方法而异。

S3支持以下选项:

传输内容校验:您可以选择计算整个有效负载校验和,并将其包括在签名计算中。 这提供了增加的安全性,但你需要读取你的有效载荷两次或缓冲在内存中。例如,为了上传文件,您需要首先读取文件以计算用于签名计算的有效负载哈希,并在创建请求时再次传输。 对于较小的有效载荷。

如何计算签名

如图所示签名的过程包括如下几步:

1.规范化请求

2.生成待签名字符串

3.签名

img

下表描述了图中显示的功能。 您需要实现这些功能的代码。

函数 描述
Lowercase() 将字符转换为小写
Hex() 十六进制编码
SHA256Hash() 安全散列算法(SHA)加密散列函数。
HMAC-SHA256() 通过使用提供了签名密钥的SHA256算法来计算HMAC。 这是最终的签名。
Trim() 删除任何前导或尾随空格。
UriEncode() 对每个字节进行URI编码。 UriEncode()必须强制执行以下规则:对非保留字符外的每个字符进行编码,非保留字符包括:‘A’ - ‘Z’,‘a’ - ‘z’,‘0’ - ‘9’,’ - ‘,’。‘,’_‘和’〜’。空格字符是保留字符,必须编码为“%20”(而不是“+”)。每个URI编码字节由“%”和字节的两位十六进制值组成。十六进制值中的字母必须为大写,例如“%1A”。在除了对象键名称以外的任何地方编码正斜杠字符’/'。 例如,如果对象键名称为photos/Jan/sample.jpg,则键名称中的正斜杠不会被编码。 注意由于RFC中的实现和相关歧义的差异,您的开发平台提供的标准UriEncode函数可能无法工作。 我们建议您编写自己的自定义UriEncode函数,以确保您的编码将工作。

规范化请求

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

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

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

HTTPMethod 是其中一种HTTP方法,例如GET,PUT,HEAD和DELETE。

CanonicalURI 是URI的绝对路径组件的URI编码版本 - 以域名后面的“/”开头,直到字符串的末尾或以问号字符(‘?’)开头的所有内容 查询字符串参数。 以下示例中的URI /examplebucket/myphoto.jpg是绝对路径,并且不在绝对路径中对“/”进行编码:

http://s3-us-east-1.ossfiles.com/examplebucket/myphoto.jpg

CanonicalQueryString指定URI编码的查询字符串参数。 您分别对URI和URI进行编码。 您还必须按键名称按字母顺序对规范查询字符串中的参数进行排序。 编码后进行排序。 以下URI示例中的查询字符串为:

prefix=somePrefix&marker=someMarker:
http://s3-us-east-1.ossfiles.com/examplebucket?prefix=somePrefix&marker=someMarker

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

URI-encode("marker")+"="+URI-encode("someMarker")+"&"+
URI-encode("prefix")+"="+URI-encode(“somePrefix")

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

http://s3-us-east-1.ossfiles.com/examplebucket?acl 

在这种情况下,CanonicalQueryString如下:

URI-encode("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 header。

您计划要包含在请求中的任何x-amz- *标头也必须添加。

下面是一个CanonicalHeaders字符串示例。 标题名称以小写字母排序。

host:s3-us-east-1.ossfiles.com
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20130708T220855Z

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

host;x-amz-content-sha256;x-amz-date

HashedPayload是请求有效内容的SHA256散列的十六进制值。

Hex(SHA256Hash(<payload>)

如果请求中没有有效内容,则计算空字符串的哈希值,如下所示:

Hex(SHA256Hash(“”))

该哈希值返回以下值:

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  

例如,当您通过使用PUT请求上传对象时,您在主体中提供对象数据。 当您通过使用GET请求检索对象时,将计算空字符串散列。

生成待签名字符串

本节提供了创建要签名的字符串的概述。

要签名的字符串是以下字符串的合并:

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

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

Scope 包含特定日期,A区域和服务。 因此,您的结果签名将仅在特定区域和特定服务中工作。 签名在指定日期后的七天内有效。

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

region用于指定存储的机房区域,目前该指定没有特殊要求

serverice 字段值为s3

如下实例:

20130606/northchina-1/s3/aws4_request

计算签名

在AWS签名版本4中,您不是直接使用ACCESS KEY来签署请求,而是首先基于ACCESS KEY创建一个范围限于特定区域和服务的签名密钥。

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天。 因此,每次计算签名密钥时,您都需要与服务器共享签名密钥。

最终签名是要签名的字符串的HMAC-SHA256散列,使用签名密钥作为密钥。

HMAC-SHA256(SigningKey, StringToSign)

示例:签名计算

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

示例账号信息

Parameter Value
AWSAccessKeyId AKIAIOSFODNN7EXAMPLE
AWSSecretAccessKey wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

请求的 timestamp of 20130524T000000Z (Fri, 24 May 2013 00:00:00 GMT).

桶名examplebucket.

假设bucket所在的region是美国东部

您可以使用路径样式或桶的子域名样式。 以下示例显示如何签名路径样式请求,例如:

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

示例:获取对象

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

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值是空请求正文的哈希值。 以下步骤显示了签名计算和授权头的构造。

生成待签名字符串

规范化请求

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  

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

生成待签名字符串

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

生成签名秘钥

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

签名

f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41

生成认证头部

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

预签名

您可以使用查询字符串参数提供身份验证信息。 当您想在URL中完全表达请求时,使用查询参数来验证请求很有用。 此方法也称为预指定URL。

预签名URL的用例场景是您可以授予对棱束对象存储资源的临时访问权限。 例如,您可以在网站上嵌入预先分配的URL,或者在命令行客户端(如Curl)中使用它来下载对象。

以下是presign示例。

https://s3.amazonaws.com/examplebucket/test.txt
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=<your-access-key-id>/20130721/us-east-1/s3/aws4_request
&X-Amz-Date=20130721T201207Z
&X-Amz-Expires=86400
&X-Amz-SignedHeaders=host
&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 标识AWS签名的版本以及用于计算签名的算法。对于AWS签名版本4,您将此参数值设置为AWS4-HMAC-SHA256。
X-Amz-Credential 除了您的访问密钥ID之外,此参数还提供签名有效的范围(AWS区域和服务)。 此值必须与在签名计算中使用的范围相匹配,如以下部分所述。 此参数值的一般形式如下:*<**your-access-key-id**>*/*<**date**>*/*<**AWS-region**>*/*<**AWS-service**>*/aws4_request
X-Amz-Date 日期和时间格式必须遵循ISO 8601标准,并且必须使用“yyyyMMddTHHmmssZ”格式进行格式化。 例如,如果日期和时间为“08/01/2016 15:32:41.982-700”,则必须首先将其转换为UTC(协调世界时),然后提交为“20160801T083241Z”。
X-Amz-Expires 提供生成的预先分配的URL有效的时间段(以秒为单位)。 例如,86400(24小时)。 此值为整数。 您可以设置的最小值为1,最大值为1296000(十五天)。预先分配的网址最长有效期为15天。
X-Amz-SignedHeaders 列出用于计算签名的头。 签名计算中需要以下标头: HTTP host header。 您计划要包含在请求中的任何x-amz- *标头也必须添加。
X-Amz-Signature 提供签名以验证您的请求。 此签名必须与棱束链对象存储计算的签名相匹配; 否则,棱束链对象存储会拒绝该请求。 例如, 733255ef022bec3f2a8701cd61d4b371f3f28c9f193a1f02279211d48d5193d7

签名过程

请参阅授权头的签名计算。 该过程通常是相同的,除了创建CanonicalRequest不同如下:

  • 您不在规范请求中包括有效负载哈希,因为在创建预分配的URL时,您不知道有效负载内容,因为该URL用于上传任意有效负载。 相反,您使用常量字符串UNSIGNED-PAYLOAD。
  • 规范查询字符串必须包括上表中除X-Amz-Signature之外的所有查询参数。
  • 规范报头必须包括HTTP主机头。 如果计划包括任何x-amz- *头,还必须添加这些头以进行签名计算。 您可以选择添加计划在请求中包含的所有其他标头。 为了增加安全性,您应该尽可能多地标记标头。

示例:签名计算

假设你的examplebucket桶中有一个对象test.txt。 您想要通过创建预先分配的URL与其他人共享此对象,持续24小时(86400秒)。

https://s3.amazonaws.com/examplebucket/test.txt
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request
&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host
&X-Amz-Signature=<signature-value>

以下步骤首先说明签名计算,然后构造预签名的URL。

您可以使用此示例作为测试用例来验证代码计算的签名; 但是,必须使用相同的存储桶名称,对象键,时间戳和以下示例凭据:

示例账号信息

Parameter Value
AWSAccessKeyId AKIAIOSFODNN7EXAMPLE
AWSSecretAccessKey ****************

生成待签名字符串

规范化请求

GET
/test.txt
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host
host:examplebucket.s3.amazonaws.com

host
UNSIGNED-PAYLOAD

生成待签名字符串

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

生成签名秘钥

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

签名

aeeed9bbccd4d02ee5c0109b86d86835f995330da4c265957d157751f604d404

现在,您有了构建预签名URL的所有信息。 此示例的结果网址如下所示(您可以使用此网址比较您的预先分配的网址):

https://examplebucket.s3.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=aeeed9bbccd4d02ee5c0109b86d86835f995330da4c265957d157751f604d404