使用对象存储服务(棱束链对象存储) 时,可通过 RESTful API 对 棱束链对象存储 发起 HTTP 匿名请求或 HTTP 签名请求,对于签名请求,棱束链对象存储服务器端将会对请求发起者进行身份验证。
棱束链对象存储提供V4签名方式。棱束链对象存储 V4签名兼容 AWS Signature Version 4,签名方法参见Authenticating Requests - AWS Signature Version 4。
注意:本文描述的是HTTP RESTful API 请求签名方式,如果您使用 SDK 进行开发,SDK中会包含签名计算方法,您无需单独计算签名。仅在您希望通过原始 API 进行开发时,需要根据本文所描述步骤进行签名计算。
创建V4签名的步骤简述如下,后面将详述各个步骤的操作方法。
用户可以通过以下几种方式来发送签名信息:
Authorization请求头示例如下(增加换行是为了方便阅读,实际为空字符串):
Authorization: AWS4-HMAC-SHA256
Credential=AKLTYS28X1sBSXCMRPMxU77t7A/20211129/SHANGHAI/s3/aws4_request,
SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date, Signature=6d9df5717ebb7cdeae7d08d817b80d6a90ca32320b07e46f30c64136eade9d48
组成字段说明如下:
字段名称 | 描述 |
---|---|
AWS4-HMAC-SHA256 |
用于签名的算法,固定值。 |
Credential | 用户的AccessKeyId和范围信息,范围信息包括请求日期、区域、服务、终止字符串aws4_request,格式如下: |
SignedHeaders | 已签名请求头的列表。该列表只需包含请求头名字,用分号分隔,必须全部小写,并按字符顺序对其进行排序,示例如下:host;range;x-amz-date |
Signature | 计算出的256位签名信息,以64个小写十六进制字符串形式表示。 |
有两种签名计算方式:
1)签名负载方式 - 用户可以选择计算整个负载(即请求体)的checksum,并将其包含在签名计算中。这种方式提高了安全性,但用户需要读取两次负载或将其缓冲在内存中。我们建议用户使用包含负载checksum的签名方式,以增强安全性。
2)无签名负载方式 - 在签名计算中不包括负载的checksum。
上述两种方式,都必须携带x-amz-content-sha256请求头,如果选择签名负载方式,请将x-amz-content-sha256请求头的值设置为负载的checksum值,否则将值设置为文本字符串 UNSIGNED-PAYLOAD。
下表描述了图中显示的方法,用户需要为这些函数实现代码。
功能 | 描述 |
---|---|
Lowercase() | 将字符串转换为小写。 |
Hex() | 小写16进制编码。 |
SHA256Hash() | 安全散列算法(SHA)加密散列函数。 |
HMAC-SHA256() | 使用签名密钥,根据SHA256算法计算出的签名值。 |
Trim() | 删除任何前导或尾随空格。 |
UriEncode() | URI编码每个字节。UriEncode()必须强制执行以下规则: URI编码除了下面字符之外的每个字节:‘A’ - ‘Z’,‘a’ - ‘z’,‘0’ - ‘9’,’ - ‘,’.’,’_‘和’〜’。 空格字符是保留字符,必须编码为“%20”(而不是“+”)。 每个URI编码字节由’%‘和两位十六进制值组成。 十六进制值中的字母必须为大写,例如“%1A”。 除了对象名之外,对正斜杠字符’/'进行编码。 例如,如果对象名称为photos/Jan/sample.jpg,则不对名称中的正斜杠进行编码。 重要建议用户编写自己的自定义UriEncode函数,以确保您的编码可以正常工作。 |
注意:以下代码是Java中的示例UriEncode()函数。
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();
}
将请求的内容(包括请求方法、URI、请求头等)组织为标准规范格式。伪代码如下:
CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Hash(RequestPayload))
CanonicalQueryString是URI编码后的查询字符串参数。用户需要单独对参数名称和值进行URI编码。并需要按参数名称的字母顺序,对参数进行排序,排序在编码后进行。具有重复名称的参数应按值进行排序。例如,以大写字母 F 开头的参数名称排在以小写字母 b 开头的参数名称之前。
以下URI示例中的查询字符串是 prefix=somePrefix&marker=someMarker&max-keys=20:
http://examplebucket.s3-us-east-1.ossfiles.com/?prefix=somePrefix&marker=someMarker&max-keys=20
CanonicalQueryString的构造方式如下(为了便于阅读,添加了换行符):
UriEncode("marker")+"="+UriEncode("someMarker")+"&"+
UriEncode("max-keys")+"="+UriEncode("20") + "&" +
UriEncode("prefix")+"="+UriEncode("somePrefix")
当请求的目标是子资源时,相应的查询参数的值设置为空字符串(“”)。例如,下面的请求用于设置bucket的ACL权限:
http://examplebucket.s3-us-east-1.ossfiles.com/?acl
在这种情况下,CanonicalQueryString为:
UriEncode("acl") + "=" + ""
如果URI中不包含“?”,则请求中不存在查询字符串,此时将CanonicalQueryString设置为空字符串(“”),但仍需要包含“\ n”。
CanonicalHeaders是请求头的列表。请求头名称转成小写,请求头名称和值之间用冒号(“:”)分隔,各行之间用换行符(“\ n”)分隔,并对请求头名称按字母顺序进行排序,示例如下:
Lowercase(<HeaderName1>)+":"+Trim(<value>)+"\n"
Lowercase(<HeaderName2>)+":"+Trim(<value>)+"\n"
...
Lowercase(<HeaderNameN>)+":"+Trim(<value>)+"\n"
CanonicalHeaders列表必须包括以下内容:
除了上述列出的必须增加的头,只计算SignedHeaders中包含的头。注意,Authorization不参与签名内容计算。
以下是CanonicalHeaders 的示例,请求头名称为小写并已排序。
host:bucket-name.s3-us-east-1.ossfiles.com
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20211130T070023Z
为防止用户请求头被篡改,将参与签名运算的请求头加入到SignedHeaders中。SignedHeaders是按字母顺序排序的,以分号分隔的小写请求头名称列表。列表中的请求头与用户在CanonicalHeaders字符串中包含的请求头相同。
格式为:
Lowercase(<HeaderName1>)+";"+Lowercase(<HeaderName2>)+";"+...+Lowercase(<HeaderNameN>)
例如,对于前面的示例,SignedHeaders的值为:
host;x-amz-content-sha256;x-amz-date
注意:时间戳(x-amz-date或Date请求头)15分钟内有效,没有权限的用户可通过截获已签名的请求,并篡改SignedHeaders中没有包含的部分来伪造请求,所以为确保您的数据安全,建议您签名所有请求头和请求体。
HashedPayload是请求体的SHA256哈希的十六进制值。
如果请求中没有请求体,则计算空字符串的哈希值,如下所示:
Hex(SHA256Hash(""))
哈希返回以下值:
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
例如,当用户使用PUT请求上传对象时,用户可以在请求体中提供对象数据。使用GET请求检索对象时,没有请求体,所以计算空字符串的哈希。
x-amz-content-sha256 请求头是必须的,取值范围如下:
值 | 描述 |
---|---|
HexEncode(Sha256Hash(RequestPayload)) | 请求体经过哈希处理的以小写十六进制字符串表示形式 |
UNSIGNED-PAYLOAD | 忽略请求正文的哈希校验 |
STREAMING-AWS4-HMAC-SHA256-PAYLOAD | chunk传输校验,暂不支持 |
计算方法为:
HashedPayload = Lowercase(HexEncode(SHA256Hash(requestPayload)))
使用 SHA256 哈希函数对请求体创建哈希值。V4签名不需要您使用特定字符编码来对负载中的文本进行编码。
如果负载为空,则使用空字符串作为哈希函数的输入。
待签名的字符串格式如下:
"AWS4-HMAC-SHA256" + "\n" + <RequestDateTime> + "\n" + <CredentialScope> + "\n" + Hex(SHA256Hash(<CanonicalRequest>))
date.Format(<YYYYMMDD>) + "/" + <region> + "/" + <service> + "/amz4_request"
使用 secretKey 作为初始哈希操作的密钥,对请求日期、区域和服务执行一系列加密哈希操作(HMAC 操作),从而生成签名密钥。伪代码如下:
secret = your Secret Key
kDate = HMAC("AWS4" + secret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
SigningKey = HMAC(kService, "amz4_request")
最终签名是使用签名密钥作为密钥,对待签名字符串计算得到HMAC-SHA256哈希值。伪代码如下:
HMAC-SHA256(SigningKey, StringToSign)
请注意,哈希过程中所使用的日期的格式为 YYYYMMDD(例如,20150830),不包括时间。
确保以正确的顺序为您要使用的编程语言指定 HMAC 参数。在此示例中,密钥是第1个参数,数据 (消息) 是第2个参数,但您使用的函数可能以不同顺序指定密钥和数据。
使用摘要 (二进制格式) 来派生密钥。大多数语言都有用来计算二进制格式哈希(通常称为摘要)或十六进制编码哈希(称为十六进制摘要)的函数。派生密钥需要使用二进制格式摘要。
示例:
HMAC(HMAC(HMAC(HMAC("AWS4" + secret,"20211129"),"BEIJING"),"s3"),"amz4_request")
在计算签名后,将其添加到请求的 HTTP 请求头Authorization中,伪代码如下:
Authorization: <algorithm> Credential=<ak>/<credential_scope>, SignedHeaders=<SignedHeaders>, Signature=<signature>
以下是使用V4签名的示例。示例中使用的访问密钥如下:
参数 | 值 |
---|---|
AccessKeyId |
2421a691b4ed625de19f6f92677b6459 |
SecretAccessKey |
447655646fc5c2118cb75b97e4275cd96739ae70408108541b0f0124fcd4d0d2 |
Bucket名称:examplebucket。访问的域名是s3-us-east-1.ossfiles.com, region是UsEast1
从存储桶 examplebucket 中获取对象 1.txt 的前5个字节。请求如下:
GET /1.txt HTTP/1.1
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Authorization: SignatureToBeCalculated
x-amz-date: 20230116T141422Z
Range: bytes=0-4
Host: examplebucket.s3-us-east-1.ossfiles.com
由于此GET请求不提供任何请求体内容,因此该 x-amz-content-sha256请求头的值是空请求体的哈希值。以下步骤显示Authorization请求头的计算的方法。
1)StringToSign
a.创建规范请求
GET
/1.txt
host:examplebucket.s3-us-east-1.ossfiles.com
range:bytes=0-4
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20230116T141422Z
host;range;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
其中,最后一行是空请求体的hash值。第三行是空,因为此请求不包含请求参数。
b.待签名字符串
AWS4-HMAC-SHA256
20230116T141424Z
20230116/us-east-1/s3/aws4_request
f6f66b6d576a178ddcc47a4f93d5412c2e219a80d97c5dfa955b8da3690f2bdb
2)生成签名密钥
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20230116"),"us-east-1"),"s3"),"amz4_request")
3)计算后的签名
cf07cb6f2907cacf37bfc25c323b84358030ad7795e5c3234c3a962396d9d7a0
4)Authorization请求头
AWS4-HMAC-SHA256 Credential=2421a691b4ed625de19f6f92677b6459/20230116/us-east-1/s3/aws4_request, SignedHeaders=host;range;x-amz-content-sha256;x-amz-date, Signature=cf07cb6f2907cacf37bfc25c323b84358030ad7795e5c3234c3a962396d9d7a0
在存储桶中上传对象1.txt。
PUT /1.txt HTTP/1.1
x-amz-content-sha256: 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
Authorization: SignatureToBeCalculated
x-amz-date: 20230116T141741Z
Host: examplebucket.s3-us-east-1.ossfiles.com
Content-Length: 12
hello world!
以下步骤显示Authorization请求头的计算的方法。
1)StringToSign
a.创建规范请求
PUT
/1.txt
host:examplebucket.s3-us-east-1.ossfiles.com
x-amz-content-sha256:7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
x-amz-date:20230116T141741Z
host;x-amz-content-sha256;x-amz-date
7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
其中,第三行是空,因为此请求不包含请求参数。最后一行是请求体的hash值,它应该与x-amz-content-sha256 请求头的值相同。
b.待签名字符串
AWS4-HMAC-SHA256
20230116T141741Z
20230116/us-east-1/s3/aws4_request
7b648585d66f4928886ba9c54f3a4d68345992dd3d6e747935263ec927251ec8
2)生成签名密钥
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20230116"),"us-east-1"),"s3"),"amz4_request")
3)计算后的签名
89886432ea6e3bec95274692b3768d488f584452b73eab7cc228e6868d2a9f6e
4)Authorization请求头
Authorization: AWS4-HMAC-SHA256 Credential=2421a691b4ed625de19f6f92677b6459/20230116/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=89886432ea6e3bec95274692b3768d488f584452b73eab7cc228e6868d2a9f6e
列出存储空间 examplebucket 中的对象,prefix设置为“1”,最多返回2个对象。请求如下:
GET /?max-keys=2&prefix=1 HTTP/1.1
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855Authorization: SignatureToBeCalculated
x-amz-date: 20230116T142142Z
Host: examplebucket.s3-us-east-1.ossfiles.com
以下步骤显示Authorization请求头的计算的方法。
1)StringToSign
a.创建规范请求
GET
/
max-keys=2&prefix=1
host:examplebucket.s3-us-east-1.ossfiles.com
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20230116T142142Z
host;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
其中,最后一行是空请求体的hash值。
b.待签名字符串
AWS4-HMAC-SHA256
20230116T142142Z
20230116/us-east-1/s3/aws4_request
2c6319ff6dade2e857cb2c895927750aa35a6ad26b8c7707df29f8f438253162
2)生成签名密钥
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20230116"),"us-east-1"),"s3"),"amz4_request")
3)计算后的签名
2762a82163af18deca383b51c3d16657409ffe4966841999b66fa47db93cd535
4)Authorization请求头
Authorization: AWS4-HMAC-SHA256 Credential=2421a691b4ed625de19f6f92677b6459/20230116/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=2762a82163af18deca383b51c3d16657409ffe4966841999b66fa47db93cd535
用户可以使用查询字符串参数来发送签名信息,示例如下。
https://examplebucket.s3-us-east-1.ossfiles.com/test.txt
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=<your-access-key-id>/<yyyymmdd>/<region>/s3/amz4_request
&X-Amz-Date=20230116T142403Z
&X-Amz-Expires=900
&X-Amz-Signature=<signature-value>
在示例URL中,请注意以下事项:
&X-Amz-Credential=<your-access-key-id>%2F20230116%2F<region>%2Fs3%2Faws4_request
各参数含义说明如下。
参数名称 | 描述 |
---|---|
X-Amz-Algorithm | 签名算法:AWS4-HMAC-SHA256 |
X-Amz-Credential | 用户的accessKeyId和范围信息,范围信息包括请求日期、区域、服务、终止字符串aws4_request,格式如下: |
X-Amz-Date | 日期和时间格式必须遵循ISO 8601标准,并且必须使用“yyyyMMddTHHmmssZ”格式进行格式化。例如,如果日期和时间是“08/01/2018 15:32:41.982-700”,则必须首先将其转换为UTC(协调世界时),然后转换为“20180801T083241Z”。请求的时间戳不能大于(服务端时间戳+15分钟) |
X-Amz-Expires | 过期时间,单位秒。时间范围为1-604800(7天)。例如,86400(表示24小时)。请求时间戳+过期时间应大于服务端时间戳。 |
X-Amz-SignedHeaders | 列出用于计算签名的标头。签名计算中需要以下标头: HTTP Host请求头。 x-amz-*请求头。 |
X-Amz-Signature | 签名结果 |
签名过程如下图所示。
使用参数的签名过程与使用请求头的签名过程类似,说明如下:
通过创建签名URL的方式,与其他人共享 examplebucket 中 1.txt 对象,过期时间设置为 900秒。以GET请求为例:
GET
https://examplebucket.s3-us-east-1.ossfiles.com/1.txt
?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=2421a691b4ed625de19f6f92677b6459%2F20230116%2Fus-east-1%2Fs3%2Faws4_request
&X-Amz-Date=20230116T142752Z
&X-Amz-Expires=900
&X-Amz-SignedHeaders=host
&X-Amz-Signature=<signature-value>
以下步骤首先说明如何计算签名和构建预签名URL。示例中使用的访问密钥如下:
参数 | 值 |
---|---|
AccessKeyId |
2421a691b4ed625de19f6f92677b6459 |
SecretAccessKey |
447655646fc5c2118cb75b97e4275cd96739ae70408108541b0f0124fcd4d0d2 |
1)StringToSign
a. 创建规范请求(以GET请求为例)
GET
/1.txt
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=2421a691b4ed625de19f6f92677b6459%2F20230116%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230116T142752Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host
host:examplebucket.s3-us-east-1.ossfiles.com
host
UNSIGNED-PAYLOAD
b. 待签名字符串
AWS4-HMAC-SHA256
20230116T142752Z
20230116/us-east-1/s3/aws4_request
a87a9df03cd15c20a019bbe878aa5ae6b72440dfeaafc8c31135a8240254141f
2)生成签名密钥
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20211130"),"us-east-1"),"s3"),"amz4_request")
3)计算后的签名
d5438a5549fe0bad6dfb26cc75cfb0911da30d503f46ca9c4fea43997c928ec6
4)预签名URL
https://examplebucket.s3-us-east-1.ossfiles.com/1.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=2421a691b4ed625de19f6f92677b6459%2F20230116%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230116T142752Z&X-Amz-Expires=900&X-Amz-Signature=d5438a5549fe0bad6dfb26cc75cfb0911da30d503f46ca9c4fea43997c928ec6&X-Amz-SignedHeaders=host
您在凭证范围中使用的日期必须与您的请求的日期匹配。您可以用多种方法将日期包括在请求中。您可以使用 date 请求头或 x-amz-date请求头,或者将 x-amz-date作为查询参数包含在内。
时间戳必须采用 UTC 表示,并具有以下 ISO 8601 格式:YYYYMMDD’T’HHMMSS’Z’。例如,20150830T123600Z 是有效时间戳。请勿在时间戳中包含毫秒。
棱束链对象存储先检查时间戳的x-amz-date请求头或参数,如果无法找到 x-amz-date,则将寻找 date 请求头。 检查八位数字字符串形式的凭证范围,表示请求的年 (YYYY)、月 (MM) 和日 (DD)。例如,如果 x-amz-date 标头值为 20111015T080000Z,并且凭证范围的日期部分为 20111015,则允许身份验证过程继续执行。如果日期不匹配,则拒绝请求,即使时间戳距离凭证范围中的日期仅有数秒之差也是如此。例如,将拒绝其x-amz-date请求头值为 20151014T235959Z 且凭证范围包括日期 20151015 的请求。