我使用以下代码使用版本4签名向AWS S3发出简单的PUT请求:
from collections import OrderedDict
from dateutil import parser
import datetime
import hashlib
import hmac
import requests
key_id = "REDACTED"
secret = "REDACTED"
bucket_name = "REDACTED"
def hashb16(message):
return hashlib.sha256(message).hexdigest()
def HMAC(key, message):
return hmac.new(key, message, hashlib.sha256)
current_time = datetime.datetime.utcnow()
url = "https://s3-eu-west-2.amazonaws.com/{}/sometest.txt".format(bucket_name)
payload = "Welcome to Amazon S3."
headers = {
'date': current_time.strftime("%a, %d %b %Y %H:%m:%S GMT"),
'host': "s3-eu-west-2.amazonaws.com",
'x-amz-content-sha256': hashb16(payload),
'x-amz-date': current_time.strftime('%Y%m%dT%H%M%SZ'),
'x-amz-storage-class': 'REDUCED_REDUNDANCY'
}
sorted_headers = sorted([k for k in headers])
region = "eu-west-2"
service = "s3"
# step 1
HTTPRequestMethod = "PUT"
CanonicalURI = "/sometest.txt"
CanonicalQueryString = ""
CanonicalHeaders = ""
for key in sorted_headers:
CanonicalHeaders += "{}:{}\n".format(key.lower(), headers[key])
SignedHeaders = "{}".format(";".join(sorted_headers))
HexEncondeHashRequestPayload = hashb16(payload)
CanonicalRequest = "{}\n{}\n{}\n{}{}\n{}".format(HTTPRequestMethod, CanonicalURI, CanonicalQueryString, CanonicalHeaders, SignedHeaders, HexEncondeHashRequestPayload)
# step 2
Algorithm = "AWS4-HMAC-SHA256"
RequestDateTime = current_time.strftime('%Y%m%dT%H%M%SZ')
CredentialScope = "{}/{}/{}/{}".format(current_time.strftime("%Y%m%d"), region, service, "aws4_request")
HashedCanonicalRequest = hashb16(CanonicalRequest)
StringToSign = "{}\n{}\n{}\n{}".format(Algorithm, RequestDateTime, CredentialScope, HashedCanonicalRequest)
#step 3
kDate = HMAC("AWS4" + secret, current_time.strftime("%Y%m%d")).digest()
kRegion = HMAC(kDate, region).digest()
kService = HMAC(kRegion, service).digest()
kSigning = HMAC(kService, "aws4_request").digest()
signature = HMAC(kSigning, StringToSign).hexdigest()
#step 4
Authorization = "AWS4-HMAC-SHA256 Credential={}/{},SignedHeaders={},Signature={}".format(key_id, CredentialScope, ";".join(sorted_headers), signature)
headers["Authorization"] = Authorization
response = requests.request("PUT", url, headers=headers)
print response.status_code
print response.text
上述步骤符合AWS documentation。我使用this page中的示例测试了散列函数,然后检查出来。
不幸的是,在执行实际请求时,我得到403状态代码,其中包含通常无效的签名消息。我在上面的代码中错过了什么?
答案 0 :(得分:1)
您的StringToSign不正确。具体而言,HashedCanonicalRequest不正确。
Amazon错误响应将显示确切的StringToSign。这将帮助您找出问题所在。
[编辑 - 我修改了你的代码。注意:我删除了有效负载计算并将其更改为UNSIGNED-PAYLOAD。有两个小问题:
1)在StringToSign中,未在URL中指定存储桶名称(在行current_time之后)
2)你在CanonicalRequest中错过了一个\ n。
from collections import OrderedDict
from dateutil import parser
import datetime
import hashlib
import hmac
import requests
key_id = ""
secret = "/N6VFTasQCJic3CqL9tj80UGB6Ba1B"
region = ""
bucket_name = ""
service = "s3"
def hashb16(message):
return hashlib.sha256(message).hexdigest()
def HMAC(key, message):
return hmac.new(key, message, hashlib.sha256)
current_time = datetime.datetime.utcnow()
url = "https://s3-us-west-2.amazonaws.com/{}/sometest.txt".format(bucket_name)
payload = "Welcome to Amazon S3."
headers = {
'date': current_time.strftime("%a, %d %b %Y %H:%m:%S GMT"),
'host': "s3-us-west-2.amazonaws.com",
'x-amz-content-sha256': 'UNSIGNED-PAYLOAD',
'x-amz-date': current_time.strftime('%Y%m%dT%H%M%SZ'),
'x-amz-storage-class': 'REDUCED_REDUNDANCY'
}
sorted_headers = sorted([k for k in headers])
# step 1
HTTPRequestMethod = "PUT"
CanonicalURI = "/{}/sometest.txt".format(bucket_name)
CanonicalQueryString = ""
CanonicalHeaders = ""
for key in sorted_headers:
CanonicalHeaders += "{}:{}\n".format(key.lower(), headers[key])
SignedHeaders = "{}".format(";".join(sorted_headers))
HexEncondeHashRequestPayload = hashb16(payload)
CanonicalRequest = "{}\n{}\n{}\n{}\n{}\n{}".format(HTTPRequestMethod, CanonicalURI, CanonicalQueryString, CanonicalHeaders, SignedHeaders, 'UNSIGNED-PAYLOAD')
# step 2
Algorithm = "AWS4-HMAC-SHA256"
RequestDateTime = current_time.strftime('%Y%m%dT%H%M%SZ')
CredentialScope = "{}/{}/{}/{}".format(current_time.strftime("%Y%m%d"), region, service, "aws4_request")
HashedCanonicalRequest = hashb16(CanonicalRequest)
StringToSign = "{}\n{}\n{}\n{}".format(Algorithm, RequestDateTime, CredentialScope, HashedCanonicalRequest)
#step 3
kDate = HMAC("AWS4" + secret, current_time.strftime("%Y%m%d")).digest()
kRegion = HMAC(kDate, region).digest()
kService = HMAC(kRegion, service).digest()
kSigning = HMAC(kService, "aws4_request").digest()
signature = HMAC(kSigning, StringToSign).hexdigest()
#step 4
Authorization = "AWS4-HMAC-SHA256 Credential={}/{},SignedHeaders={},Signature={}".format(key_id, CredentialScope, ";".join(sorted_headers), signature)
headers["Authorization"] = Authorization
response = requests.request("PUT", url, headers=headers)
print response.status_code
print response.text