AWSS3V4签名实现(nodejs)
虽然亚马逊提供了sdk,使⽤SDK会⾃动帮你计算签名。如果你能使⽤SDK 请⼀定使⽤SDK。
但是⽬标平台为嵌⼊式平台(⾮Linux)只⽀持C接⼝,很多库移植成本过⾼,为了使⽤S3服务,我们应该⾸选restfulAPI 这种⽅案。这就涉及到了⾃⼰实现AWS V4签名的问题。
1 AWS v4签名
与Amazon S3的每次交互都是经过⾝份验证。本节介绍使⽤AWS Signature Version 4算法的请求认证。S3⽬前只⽀持V4验证。
1.1认证⽅法:
英语电子邮件格式
HTTP授权标头 - 使⽤HTTP Authorization标头是验证Amazon S3请求的最常⽤⽅法。所有Amazon S3 REST操作(使⽤POST请求的基于浏览器的上传除外)都需要此标头。本⽂使⽤的是这种⽅式。且为单个区块传输⽅式。
1.2 签名环境
在单个区块中传输有效负载(AWS签名版本4)
当在单个块中传输有效载荷时,可以选择将有效载荷散列包含在签名计算中,称为带符号有效载荷(如果不包含它,有效载荷将被视为⽆符号)。即是否计算待上传⽂件hash值。
我们选择的是第⼆种:⽆符号有效负载选项 - UNSIGNED-PAYLOAD在构建规范请求时包含⽂字字符串,并x-amz-content-sha256在向S3发送请求时将相同的值设置为 标题值。不计算负载hash值。即x-amz-content-sha256 设置为UNSIGNED-PAYLOAD。
签名环境总结:
1 ⽤于s3
2 单个区块上传⽂件
3 不计算有效负载hash
1.3签名理论
要计算签名,您⾸先需要⼀个字符串来签名。然后HMAC-SHA256使⽤签名密钥计算要签名的字符串的散列。下图说明了该过程,包括您为签名创建的字符串的各个组件
当Amazon S3收到验证请求时,它会计算签名,然后将其与您在请求中提供的签名进⾏⽐较。因此,您必须使⽤Amazon S3使⽤的相同⽅法来计算签名。以协议形式签署请求的过程称为规范化。
即你的操作必须完全符合标准,因为AWS 也会按照标准计算⼀次,相同就成功。
签名流程图:
所需要的基础⽅法:
2 实际操作
我们可以从图中看出Authorization的得到有3部分组成,且是顺序获取 前⼀个是后⼀个的前置条件。
1 Canonical Request
2 StringToSign
3 Signature
下⾯将分别介绍
初为人师
本⽂验证⽤了些全局变量:
2.1 Canonitcal Requet Canonitcal Requet 有下⾯六个部分组成
var accessKeyId = ' ';
var cretAccessKey= ' ';
var region='us-east-1';
枇杷怎么读
var rviceName='s3';
var algorithm = 'AWS4-HMAC-SHA256';
var v4Identifier = 'aws4_request';
<HTTPMethod>\n
<CanonicalURI>\n
<CanonicalQueryString>\n
<CanonicalHeaders>\n
<SignedHeaders>\n
<HashedPayload>
2.1.1HTTPMethod
HTTPMethod 是HTTP⽅法之⼀,例如GET,PUT,HEAD和DELETE。
2.1.2 CanonicalURI
2.1.3 CanonicalQueryString
CanonicalQueryString指定URI编码的查询字符串参数。您可以单独对URI进⾏编码名称和值。即url的query参数。⽐较复杂,详情可参
见⽂档。
因为我们是S3操作不带query参数,按⽂档说明:
如果URI不包含’?’,则请求中没有查询字符串,并且将规范查询字符串设置为空字符串(“”)。您仍然需要包含“\ n”。
2.1.4 CanonicalHeaders
CanonicalHeaders是请求标题及其值的列表。单独的标题名称和值对由换⾏符(“\ n”)分隔。标题名称必须⼩写。您必须按字母顺序
排列标题名称以构建字符串华游蛇
Lowerca(<HeaderName1>)+":"+Trim(<value>)+"\n"
Lowerca(<HeaderName2>)+":"+Trim(<value>)+"\n"
...
Lowerca(<HeaderNameN>)+":"+Trim(<value>)+"\n"
sample:
'content-md5:ZwmeZ+R9fiKB4KNgjnCUbw==\nhost:xxxxxx.\nx-amz-content-sha256:UNSIGNED-PAYLOAD\nx-amz-date:20180313T05实现code:
function canonicalHeaderValues(values) {
place(/\s+/g, ' ').replace(/^\s+|\s+$/g, '');
}
function canonicalHeaders(request) {
var headers = [];
for(var i in request.header)
{
headers.push([i, request.header[i]]);
}
headers.sort(function (a, b) {
return a[0].toLowerCa() < b[0].toLowerCa() ? -1 : 1;
});
var parts = [];
语文教学总结
for(var i in headers)
{
var key = headers[i][0].toLowerCa();
var value = headers[i][1];
parts.push(key + ':' + String()));
}
return parts.join('\n');
}
2.1.6 HashedPayload
HashedPayload 是请求有效负载的SHA256哈希值的⼗六进制值。我们选择不计算有效负载hash值 直接给 UNSIGNED-PAYLOAD
2.1.7 代码实现
function canonicalString(request) {
var parts = [];丝瓜炒鸡蛋
//pathname = uriEscapePath(request.pathname);
parts.hod);
parts.push(request.pathname);
parts.push("");
parts.push(canonicalHeaders(request) + '\n');
parts.push(signedHeaders(request));
parts.push('UNSIGNED-PAYLOAD');
console.info(parts);
return parts.join('\n');
}
Request 参数
var header ={
'host' : 'xxxxxx.',
'x-amz-content-sha256':'UNSIGNED-PAYLOAD',
'x-amz-date':date,
查本机ip地址'content-md5':md5
}
var req ={
header : header,
pathname : '/xxxx/test02.jpg',
method : 'PUT',
bodyhash: 'UNSIGNED-PAYLOAD'
}
date参数:
var date = (new Date()).toISOString().replace(/\-|:|\.\d\d\d/g,""); (后⾯所有的date都是这个)
20180313T054459Z (注意时区)
Md5参数:
const hash = ateHash('md5');
hash.update(bitmap);
var md5 = hash.digest('ba64');
console.info(md5);
打印结果( console.info(parts)
[ 'PUT',
'/xxxx/test02.jpg',
'',
'content-md5:ZwmeZ+R9fiKB4KNgjnCUbw==\nhost:xxxxxx.\nx-amz-content-sha256:UNSIGNED-PAYLOAD\nx-amz-date:20180313 'content-md5;host;x-amz-content-sha256;x-amz-date',
'UNSIGNED-PAYLOAD' ]
2.2 StringToSign
本节概述创建要签名的字符串。对于⼀步⼀步的说明,请参阅任务2:创建⼀个字符串来登录在AWS⼀般参考。
要签名的字符串是以下字符串的串联:
"AWS4-HMAC-SHA256" + "\n" +
timeStampISO8601Format + "\n" +
<Scope> + "\n" +
Hex(SHA256Hash(<CanonicalRequest>))
2.2.1 AWS4-HMAC-SHA256
常量字符串AWS4-HMAC-SHA256指定您正在使⽤的哈希算法HMAC-SHA256
2.2.2 timeStampISO8601Format
这timeStamp是ISO 8601格式的当前UTC时间(例如, 20130524T000000Z) 前⾯我们已经获取到了
2.2.3 Scope
Scope将⽣成的签名绑定到特定⽇期,AWS区域和服务
sample:
20180313/us-east-1/s3/aws4_request
code:
function createScope(date, region, rviceName) {
return [
date.substr(0, 8),
region,
rviceName,
'aws4_request'
摊开双手
].join('/');
}
function credentialString(datetime) {
return createScope(
datetime.substr(0, 8),
region,
rviceName
);
}
2.2.4 Hex(SHA256Hash())
将2.1中得到的CanonicalRequest 进⾏sha256 在转成16进制
code:
sha256(string, 'hex'); //基础⽅法已经提供
2.2.5 代码实现
Code:
function stringToSign(datetime, request) {
var parts = [];
parts.push('AWS4-HMAC-SHA256');
parts.push(datetime);
parts.push(credentialString(datetime));
console.info(canonicalString(request));
parts.push(hexEncodedHash(canonicalString(request)));
console.info(parts);
return parts.join('\n');
}