0

我想集成来自 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

你能看看并告诉我我做错了什么吗?

4

1 回答 1

0

因为我对 AWS sqs api 和请求登录 python 3 感到非常沮丧,所以我决定在 github 上创建一个项目。我刚刚推动了这个项目,所以它处于非常早期的阶段,但我很快就会添加更多功能。好消息是它已经具备您所需要的:AWS 版本 4 签名算法:https ://github.com/rcosnita/aws-tests

看看: - https://github.com/rcosnita/aws-tests/blob/master/aws/sqs/tests/itest_sqs_client.py

这应该让您开始使用 aws 请求签名。

祝你好运,如果它对你有帮助,请告诉我。

于 2013-03-12T19:32:16.857 回答