我想集成来自 Amazon Elastic Transcoder 的新服务。由于我的应用程序在 python 2.4 上运行,因此无法使用 boto。
所以我正在写直接的http调用。我遇到的问题是它在我发布请求时告诉我这一点,但适用于获取请求:
我们计算的请求签名与您提供的签名不匹配。检查您的 AWS 秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。
这是代码:
import time
import hmac
import hashlib
import urlparse
import requests
import json
proxy = {
"http" : "http://10.254.252.100:3128",
"https" : "http://10.254.252.100:3128",
}
class AWSSignerV4(object):
'''
AWS V4 signature class.
'''
def __init__(self, access_key, key, http_verb, uri, headers, payload='', service=None, region=None):
'''
Instantiates a AWS V4 request signer (HMAC-256).
:param access_key: AWS access key.
:param key: AWS access secret key.
:param http_verb: Request HTTP verb (e.g., GET or POST)
:param uri: Request URL (http://<aws-service>.<aws-region>.amazonaws.com/[something]
:param headers: dict with the headers used on the request. This dictionary will be updated with the authentication headers.
:param payload: Optional payload for PUT or POST requests.
:param service: Optional service name. By default it will be derived from the uri parameter.
:param region: Optional AWS datacentre region. By default it will be derived from the uri parameter.
'''
self._key = key
self._headers = headers
self._http_verb = http_verb
self._access_key = access_key
self._payload_hash = hashlib.sha256(payload).hexdigest().lower()
parsed_url = urlparse.urlparse(uri)
expanded_netloc = parsed_url.netloc.split('.')
if service is None:
service = expanded_netloc[0]
if region is None:
region = expanded_netloc[1]
self._service = service
self._region = region
if 'x-amz-date' not in headers:
headers['x-amz-date'] = self.generate_request_date()
self._timestamp = headers['x-amz-date']
headers['x-amz-credential'] = self.credential_scope
self._path = parsed_url.path
self._query_string = self._canonicalise_querystring(parsed_url.query.strip())
self._cheaders, self._signed_headers = self._canonicalise_headers(self._headers, parsed_url.netloc)
self._headers['Authorization'] = self.authorization_header
def __call__(self):
'''
This method (that makes an instance of this class a callable object) will return the updated header dictionary, including
the AWS authorization headers.
'''
return self._headers
#############################################
# Instance properties
@property
def authorization_header(self):
return 'AWS4-HMAC-SHA256 Credential={}/{}, SignedHeaders={}, Signature={}'.format(self._access_key, self.credential_scope,
self._signed_headers, self.request_signature)
@property
def request_signature(self):
return hmac.new(self.signature_key, self.string_to_sign, hashlib.sha256).hexdigest()
@property
def canonical_request_hash(self):
request = '{self._http_verb}\n{self._path}\n{self._query_string}\n{self._cheaders}\n{self._signed_headers}\n{self._payload_hash}'.format(**locals())
return hashlib.sha256(request).hexdigest().lower()
@property
def string_to_sign(self):
return 'AWS4-HMAC-SHA256\n{}\n{}\n{}'.format(self._timestamp, self.credential_scope, self.canonical_request_hash)
@property
def credential_scope(self):
request_date = self._timestamp[:8]
return '{}/{}/{}/aws4_request'.format(request_date, self._region, self._service)
@property
def signature_key(self):
sign = lambda k, m: hmac.new(k, m.encode('utf-8'), hashlib.sha256).digest()
k_date = sign(('AWS4' + self._key).encode('utf-8'), self._timestamp[:8])
k_region = sign(k_date, self._region)
k_service = sign(k_region, self._service)
return sign(k_service, 'aws4_request')
#########################################
# Class methods
@staticmethod
def generate_request_date():
return time.strftime('%Y%m%dT%H%M%SZ', time.gmtime())
@staticmethod
def _canonicalise_querystring(query):
assert(query == '') # TODO proper query string support will be added if required!
return ''
@staticmethod
def _canonicalise_headers(headers, host):
canonical_headers = {}
canonical_headers_str = ''
for key, value in headers.iteritems():
canonical_headers[key.lower()] = value.strip() # change to regexp to remove spaces not within "
if 'host' not in canonical_headers:
canonical_headers['host'] = host
for key, value in sorted(canonical_headers.iteritems()):
canonical_headers_str += '{}:{}\n'.format(key, value)
return (canonical_headers_str, ';'.join(sorted(canonical_headers.keys())))
# Sample code
if __name__ == '__main__':
headers = {}
# uri = 'https://elastictranscoder.us-east-1.amazonaws.com/2012-09-25/pipelines/1360339173389-562f0b'
uri = 'https://elastictranscoder.us-east-1.amazonaws.com/2012-09-25/pipelines/'
# uri = 'https://elastictranscoder.us-east-1.amazonaws.com/2012-09-25/jobs/1360339319640-ef1c5b'
# uri = 'https://elastictranscoder.us-east-1.amazonaws.com/2012-09-25/jobs/'
test_obj = AWSSignerV4('acess key', 'private key', 'POST', uri, headers)
headers = test_obj()
payload = {'Name': 'Testweb',
'InputBucket':'testjd5',
'OutputBucket':'testfinals',
'Role':'arn:aws:iam::789823056103:role/Elastic_Transcoder_Default_Role',
'Notifications':{'Progressing': '', 'Completed': '', 'Warning': '', 'Error': ''}
}
# payload = {'Input': {"Key":"9761.webm",
# "FrameRate":"auto",
# "Resolution":"auto",
# "AspectRatio":"auto",
# "Interlaced":"auto",
# "Container":"webm"},
# 'Output':{"Key":"test.mp4",
# "ThumbnailPattern":"",
# "Rotate":"0",
# "PresetId":"1351620000000-100020"},
# 'PipelineId':'1360339173389-562f0b'
# }
# headers['Content-Length']= '%s'%(value)
# print uri
# for key, value in headers.iteritems():
# call_header['%s'%(key)]= '%s'%(value)
# print headers
print requests.post(uri, headers=headers, data=json.dumps(payload), proxies=proxy).text
# print requests.get(uri, headers=headers, proxies=proxy).text
你能看看并告诉我我做错了什么吗?