认证流程概述
V4签名认证用于对请求进行安全验证,确保请求在传输过程中未被篡改,并且确认请求来源合法。以下是实现 V4 签名认证的详细过程。
生成签名步骤
提取基本元素
最终的请求鉴权头 Authorization 头中主要包含如下元素:
algorithm
Credential
SignedHeaders
Signature
algorithm元素与生成步骤
algorithm表示签名的哈希算法,V4签名通常取值是AWS4-HMAC-SHA256。格式如下:
algorithm="AWS4-HMAC-SHA256"
Credential元素与生成步骤
Credential 格式如下:
Credential={accessKey}/{credentialScope}
其中 credentialScope 格式如下:
credentialScope="{dateStamp}/{regionName}/{serviceName}/aws4_request"
SignedHeaders元素与生成步骤
SignedHeaders主要是由参与签名的请求头headerName按自然序排列并转为小写后用 ; 连接进行拼接而成,如:
signedHeaders="headerName1;headerName2;headerName3"
Signature
生成 stringToSign
Signature 的生成需要先生成 stringToSign,stringToSign的格式如下:
stringToSign="{algorithm}\n{requestDate}\n{credentialScope}\n{canonicalRequestHash}"
其中计算 canonicalRequestHash 需要先生成 canonicalRequest,其格式如下:
canonicalRequest="{httpMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}"
- httpMethod: 请求方法
- canonicalUri: 标准化的 URI,如:
/{bucketName}/{objectKey}
- canonicalQueryString: 标准化的查询字符串
- canonicalHeaders: 标准化的请求头,如:
headerName1:headerValue1\nheaderName2:headerValue2\n
- signedHeaders: 已签名的请求头列表
- payloadHash: 请求负载的 SHA256 哈希值(未签名的负载使用 UNSIGNED-PAYLOAD)
得到canonicalRequest后,就可以计算canonicalRequestHash = HMAC-SHA256(canonicalRequest),并得到stringToSign
生成签名密钥
依次生成签名密钥,步骤如下:
kDate=HMAC-SHA256("AWS4"+{SecrectKey}, dateStamp)
kRegion=HMAC-SHA256(kDate, regionName)
kService=HMAC-SHA256(kRegion, serviceName)
kSigning=HMAC-SHA256(kService, "aws4_request")
得到签名密钥kSigning后,就可以计算得到Signature
signature=Hex(HMAC-SHA256(kSigning, stringToSign))
构造 Authorization Header
根据生成的签名构造 Authorization Header:
Authorization: {algorithm} Credential={Credential}, SignedHeaders={SignedHeaders}, Signature={signature}
shell示例
以上传对象为例:
#!/bin/sh
accessKey="访问密钥ID"
secretKey="私有访问密钥"
regionName="cn"
serviceName="s3"
# http or https
protocol="https"
host="gdoss.xstore.ctyun.cn"
bucketName="test-bucket"
objectKey="testobj"
sourceFile="/path/to/file"
httpMethod="PUT"
dateStamp=$(date -u +"%Y%m%d")
requestDate=$(date -u +"%Y%m%dT%H%M%SZ")
acl="public-read-write"
contentType="text/plain"
# Canonical Request
canonicalUri="/${bucketName}/${objectKey}"
canonicalQueryString=""
canonicalHeaders="content-type:${contentType}\nhost:${host}\nx-amz-acl:${acl}\nx-amz-date:${requestDate}\n"
signedHeaders="content-type;host;x-amz-acl;x-amz-date"
canonicalRequest="${httpMethod}\n${canonicalUri}\n${canonicalQueryString}\n${canonicalHeaders}\n${signedHeaders}\nUNSIGNED-PAYLOAD"
# Canonical Request Hash
canonicalRequestHash=$(echo -en "${canonicalRequest}" | iconv -t utf-8 | openssl dgst -sha256 | awk '{print $2}')
# String to Sign
algorithm="AWS4-HMAC-SHA256"
credentialScope="${dateStamp}/${regionName}/${serviceName}/aws4_request"
stringToSign="${algorithm}\n${requestDate}\n${credentialScope}\n${canonicalRequestHash}"
# 生成签名密钥
kDate=$(echo -en "${dateStamp}" | openssl dgst -sha256 -hmac "AWS4${secretKey}" -binary)
kRegion=$(echo -en "${regionName}" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(echo -en "${kDate}" | xxd -p -c 256) -binary)
kService=$(echo -en "${serviceName}" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(echo -en "${kRegion}" | xxd -p -c 256) -binary)
kSigning=$(echo -en "aws4_request" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(echo -en "${kService}" | xxd -p -c 256) -binary)
# 生成最终签名
signature=$(echo -en "${stringToSign}" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(echo -en "${kSigning}" | xxd -p -c 256) | awk '{print $2}')
# Authorization Header
authorizationHeader="${algorithm} Credential=${accessKey}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}"
curl -v -X ${httpMethod} \
-T "${sourceFile}" \
-H "Content-Type: ${contentType}" \
-H "x-amz-acl: ${acl}" \
-H "x-amz-date: ${requestDate}" \
-H "Authorization: ${authorizationHeader}" \
"${protocol}://${host}/${bucketName}/${objectKey}"