[英]AWS S3 - send a signed requests fails
Using the header method works for both regular (AKA permanent) credentials and temporary ones.使用 header 方法适用于常规(AKA 永久)凭证和临时凭证。 Using the query string (AKS stick all headers as part of the uri), works with the permanent credentials but fails for the temporary one with the following error:
使用查询字符串(AKS 将所有标头作为 uri 的一部分),使用永久凭据,但对于临时凭据失败,并出现以下错误:
The request signature we calculated does not match the signature you provided. Check your key and signing method.
It's not related to any clock difference, and I know that the temporary credentials works with this S3 bucket for a fact.它与任何时钟差异无关,而且我知道临时凭证适用于这个 S3 存储桶。
the format of the temporary credentials is as fallow临时凭证的格式如下
[default]
aws_access_key_id = A********
aws_secret_access_key = U*******
aws_session_token = F******
I'm writing a code snippet that needs to handle a get requests from a private S3 bucket.我正在编写一个代码片段,需要处理来自私有 S3 存储桶的获取请求。 I have a working code that uses headers for both permanent and temporary credentials
我有一个使用标头作为永久和临时凭证的工作代码
my goal now is to achieve the same using query string but I'm unable to get it to work for temporary credentials, it's working just fine with the permanent one, so I'm pretty sure that the problem relays in the canonical_querystring somewhere, but nothing I've tried seems to be working我现在的目标是使用查询字符串来实现相同的目标,但我无法让它用于临时凭据,它与永久凭据一起工作得很好,所以我很确定问题在 canonical_querystring 某处中继,但是我尝试过的任何东西似乎都不起作用
I can't use boto3 because it needs to be independent from any external package (the requests package is used only for debugging it won't be a part of the final code) if anyone can tell me what am I doing wrong it would be highly appreciated我不能使用 boto3,因为它需要独立于任何外部 package (请求 package 仅用于调试它不会成为最终代码的一部分)如果有人能告诉我我做错了什么,那将是高度赞赏
hers is my attempt using query string她是我使用查询字符串的尝试
import datetime
import hashlib
import hmac
import re
try:
import httplib
except ImportError:
import http.client as httplib
import requests
import urllib
def get_region(url, host):
conn = httplib.HTTPConnection(url)
headers = {'Host': host}
conn.request('HEAD', '/', headers=headers)
res = conn.getresponse()
status = res.status
if 400 <= status:
return None
return res.getheader('x-amz-bucket-region')
def _sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def _get_signature_key(key, date_stamp, region_name, service_name):
k_date = _sign(('AWS4' + key).encode('utf-8'), date_stamp)
k_region = _sign(k_date, region_name)
k_service = _sign(k_region, service_name)
k_signing = _sign(k_service, 'aws4_request')
return k_signing
def get_sign_headers(access_key, secret_key, url, session_token=None, method='GET', request_parameters=''):
regex = r"^(https://)(([a-zA-z0-9\-]+)\.)((\w+.*)\.amazonaws\.com)([^:^/]*)?(.*)$"
matches = re.match(regex, url, re.MULTILINE)
service = 's3'
host = matches.group(2) + matches.group(4)
canonical_uri = matches.group(7)
region = get_region('s3.us-east-2.amazonaws.com', host)
if matches.group(3) == 's3':
if not region:
region = matches.group(5)
host = service + '.' + region + '.amazonaws.com'
endpoint = 'https://' + host + matches.group(7)
t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ') # Format date as YYYYMMDD'T'HHMMSS'Z'
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
canonical_headers = 'host:' + host + '\n'
signed_headers = 'host'
if session_token:
canonical_headers += 'x-amz-security-token:' + session_token + '\n'
signed_headers += ';x-amz-security-token'
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
canonical_querystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256'
try:
credential = urllib.quote_plus(access_key + '/' + credential_scope)
except AttributeError:
credential = urllib.parse.quote_plus(access_key + '/' + credential_scope)
canonical_querystring += '&X-Amz-Credential=' + credential
canonical_querystring += '&X-Amz-Date=' + amz_date
canonical_querystring += '&X-Amz-Expires=30'
canonical_querystring += '&X-Amz-SignedHeaders=' + signed_headers
payload_hash = 'UNSIGNED-PAYLOAD'
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
'\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
canonical_request.encode('utf-8')).hexdigest()
signing_key = _get_signature_key(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
if session_token:
try:
session_token = urllib.quote_plus(session_token)
except AttributeError:
session_token = urllib.parse.quote_plus(session_token)
canonical_querystring += '&X-Amz-Security-Token={TOKEN}'.format(TOKEN=session_token)
canonical_querystring += '&X-Amz-Signature=' + signature
request_url = endpoint + "?" + canonical_querystring
print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
print('Request URL = ' + request_url)
r = requests.get(request_url)
print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
print('Response code: %d\n' % r.status_code)
print(r.text)
and this is the code that uses headers approach (that works for both):这是使用标头方法的代码(适用于两者):
import datetime
import hashlib
import hmac
import re
import requests
try:
import httplib
except ImportError:
import http.client as httplib
def get_region(url, host):
conn = httplib.HTTPConnection(url)
headers = {'Host': host}
conn.request('HEAD', '/', headers=headers)
res = conn.getresponse()
status = res.status
if 400 <= status:
return None
return res.getheader('x-amz-bucket-region')
def _sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def _get_signature_key(key, date_stamp, region_name, service_name):
k_date = _sign(('AWS4' + key).encode('utf-8'), date_stamp)
k_region = _sign(k_date, region_name)
k_service = _sign(k_region, service_name)
k_signing = _sign(k_service, 'aws4_request')
return k_signing
def get_sign_headers(access_key, secret_key, url, session_token=None, method='GET', request_parameters=''):
regex = r"^(https://)(([a-zA-z0-9\-]+)\.)((\w+.*)\.amazonaws\.com)([^:^/]*)?(.*)$"
matches = re.match(regex, url, re.MULTILINE)
service = 's3'
host = matches.group(2) + matches.group(4)
canonical_uri = matches.group(7)
region = get_region('s3.us-east-2.amazonaws.com', host)
if matches.group(3) == 's3':
if not region:
region = matches.group(5)
host = service + '.' + region + '.amazonaws.com'
endpoint = 'https://' + host
t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
canonical_querystring = request_parameters
canonical_headers = 'host:' + host + '\n' + 'x-amz-content-sha256:UNSIGNED-PAYLOAD' + \
'\n' + 'x-amz-date:' + amz_date + '\n'
signed_headers = 'host;x-amz-content-sha256;x-amz-date'
if session_token:
signed_headers += ';x-amz-security-token'
canonical_headers += 'x-amz-security-token:' + session_token + '\n'
payload_hash = 'UNSIGNED-PAYLOAD'
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
'\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
canonical_request.encode('utf-8')).hexdigest()
signing_key = _get_signature_key(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + \
'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'x-amz-date': amz_date, 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD',
'Authorization': authorization_header}
if session_token:
headers['x-amz-security-token'] = session_token
request_url = endpoint + canonical_uri
print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
print('Request URL = ' + request_url)
print('Headers = {}'.format(headers))
r = requests.get(request_url, headers=headers)
print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
print('Response code: %d\n' % r.status_code)
have you validated that your server time is accurate.您是否验证过您的服务器时间是准确的。 Even a few minutes difference causes signing failures.
即使是几分钟的差异也会导致签名失败。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.