【问题标题】:How to sign amazon web service requests from the python app engine?如何签署来自 python 应用引擎的亚马逊网络服务请求?
【发布时间】:2010-11-08 11:47:34
【问题描述】:

我在我的 Google 应用引擎应用程序中使用 Amazon Web 服务 API。亚马逊表示他们只会接受 2009 年 8 月 15 日之后的签名请求。虽然他们给出了简单的instructions 进行签名,但我对 SHA256 的 Python 库不太了解。应用程序引擎文档says 它支持 pycrypto,但我只是想知道(读起来很懒)是否有人已经这样做了。您可以分享任何代码 sn-ps 吗?我可能在这里遗漏的任何问题?

【问题讨论】:

    标签: google-app-engine


    【解决方案1】:

    这是一个基于较低级别(然后是 boto)库的 REST 请求示例。解决方案取自http://cloudcarpenters.com/blog/amazon_products_api_request_signing

    您只需要 AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY 的有效条目

    def amazon_test_url():
        import base64, hashlib, hmac, time
        from urllib import urlencode, quote_plus
    
        AWS_ACCESS_KEY_ID = 'YOUR_KEY'
        AWS_SECRET_ACCESS_KEY = 'YOUR_SECRET_KEY'  
        TEST_ISBN = '9780735619678' #http://stackoverflow.com/questions/1711/what-is-the-single-most-influential-book-every-programmer-should-read
    
        base_url = "http://ecs.amazonaws.com/onca/xml"
        url_params = dict(
            Service='AWSECommerceService', 
            Operation='ItemLookup', 
            IdType='ISBN', 
            ItemId=TEST_ISBN,
            SearchIndex='Books',
            AWSAccessKeyId=AWS_ACCESS_KEY_ID,  
            ResponseGroup='Images,ItemAttributes,EditorialReview,SalesRank')
    
        #Can add Version='2009-01-06'. What is it BTW? API version?
    
    
        # Add a ISO 8601 compliant timestamp (in GMT)
        url_params['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
    
        # Sort the URL parameters by key
        keys = url_params.keys()
        keys.sort()
        # Get the values in the same order of the sorted keys
        values = map(url_params.get, keys)
    
        # Reconstruct the URL parameters and encode them
        url_string = urlencode(zip(keys,values))
    
        #Construct the string to sign
        string_to_sign = "GET\necs.amazonaws.com\n/onca/xml\n%s" % url_string
    
        # Sign the request
        signature = hmac.new(
            key=AWS_SECRET_ACCESS_KEY,
            msg=string_to_sign,
            digestmod=hashlib.sha256).digest()
    
        # Base64 encode the signature
        signature = base64.encodestring(signature).strip()
    
        # Make the signature URL safe
        urlencoded_signature = quote_plus(signature)
        url_string += "&Signature=%s" % urlencoded_signature
    
        print "%s?%s\n\n%s\n\n%s" % (base_url, url_string, urlencoded_signature, signature)
    

    【讨论】:

    【解决方案2】:

    Pycrypto 可以正常工作 - App Engine 支持它,尽管公共密码是用 Python 而不是 C 实现的。您还应该能够使用现有的 AWS 库之一,因为 App 支持 urlfetch/httplib引擎。

    我有一个将图像上传到 S3 的应用程序,并且我自己实现了请求签名,但主要是因为我在 urlfetch/httplib 可用之前编写了它。但是,它工作得很好。

    【讨论】:

    • 感谢您的回复。我不明白使用现有 AWS 库进行请求签名的选项。有这样的图书馆吗?它不需要我发送我的密钥吗?我肯定在这里遗漏了一些东西。
    • 所有 AWS 库都需要支持请求签名,因为据我所知,这是访问 AWS 的唯一方式。您需要将密钥上传到 App Engine,但不需要将其发送到 AWS。 Python 的 boto 库应该符合您的要求:code.google.com/p/boto
    【解决方案3】:

    根据http://jjinux.blogspot.com/2009/06/python-amazon-product-advertising-api.html 的代码示例让这个工作正常进行 这是一个小的改进版本,可让您在调用之前将调用特定参数的字典与基本参数合并。

    keyFile = open('accesskey.secret', 'r')
    # I put my secret key file in .gitignore so that it doesn't show up publicly
    AWS_SECRET_ACCESS_KEY = keyFile.read()
    keyFile.close()
    
    def amz_call(self, call_params):
    
        AWS_ACCESS_KEY_ID = '<your-key>'
        AWS_ASSOCIATE_TAG = '<your-tag>'
    
        import time
        import urllib
        from boto.connection import AWSQueryConnection
        aws_conn = AWSQueryConnection(
            aws_access_key_id=AWS_ACCESS_KEY_ID,
            aws_secret_access_key=Amz.AWS_SECRET_ACCESS_KEY, is_secure=False,
            host='ecs.amazonaws.com')
        aws_conn.SignatureVersion = '2'
        base_params = dict(
            Service='AWSECommerceService',
            Version='2008-08-19',
            SignatureVersion=aws_conn.SignatureVersion,
            AWSAccessKeyId=AWS_ACCESS_KEY_ID,
            AssociateTag=AWS_ASSOCIATE_TAG,
            Timestamp=time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()))
        params = dict(base_params, **call_params)
        verb = 'GET'
        path = '/onca/xml'
        qs, signature = aws_conn.get_signature(params, verb, path)
        qs = path + '?' + qs + '&Signature=' + urllib.quote(signature)
        print "verb:", verb, "qs:", qs
        return aws_conn._mexe(verb, qs, None, headers={})
    

    示例用法:

    result = self.amz_call({'Operation' : 'ItemSearch' , 'Keywords' : searchString , 'SearchIndex' : 'Books' , 'ResponseGroup' : 'Small' })
    if result.status == 200:
        responseBodyText = result.read()
        # do whatever ...
    

    【讨论】:

    • 这对我不起作用 - 我得到“AttributeError: 'AWSQueryConnection' object has no attribute 'get_signature'”
    【解决方案4】:

    请参阅http://sowacs.appspot.com/AWS/Downloads/#python 了解 GAE Python 签名服务 web 应用程序。使用原生 Python 库。

    【讨论】:

      【解决方案5】:

      我写了另一个简单的例子,它只使用核心 python 3 库(不是 boto)并使用 AWS 签名协议的版本 2:

      http://xocoatl.blogspot.com/2011/03/signing-ec2-api-request-in-python.html

      我知道它在 GAE 中不起作用,但可能对像我一样只是在寻找 AWS 身份验证示例的任何人有用。

      【讨论】:

        【解决方案6】:

        我使用这个使用pycrypto 来生成自定义策略:

        import json                                                                                                                                                                 
        import time                                                                                                                                                                 
        
        from Crypto.Hash import SHA                                                                                                                                                 
        from Crypto.PublicKey import RSA                                                                                                                                            
        from Crypto.Signature import PKCS1_v1_5                                                                                                                                     
        from base64 import b64encode                                                                                                                                                
        
        url = "http://*"                                                                                                                                                            
        expires = int(time.time() + 3600)
        
        pem = """-----BEGIN RSA PRIVATE KEY-----
        ...
        -----END RSA PRIVATE KEY-----"""
        
        key_pair_id = 'APK.....'
        
        policy = {}                                                                                                                                                                 
        policy['Statement'] = [{}]                                                                                                                                                  
        policy['Statement'][0]['Resource'] = url                                                                                                                                    
        policy['Statement'][0]['Condition'] = {}                                                                                                                                    
        policy['Statement'][0]['Condition']['DateLessThan'] = {}                                                                                                                    
        policy['Statement'][0]['Condition']['DateLessThan']['AWS:EpochTime'] = expires
        
        policy = json.dumps(policy) 
        
        private_key = RSA.importKey(pem)                                                                                                                                            
        policy_hash = SHA.new(policy)                                                                                                                                               
        signer = PKCS1_v1_5.new(private_key)                                                                                                                                        
        signature = b64encode(signer.sign(policy_hash))
        
        print '?Policy=%s&Signature=%s&Key-Pair-Id=%s' % (b64encode(policy),                                                                                                        
                                                          signature,                                                                                                                
                                                          key_pair_id)
        

        这让我可以对多个项目使用一个键,例如:

        http://your_domain/image1.png?Policy...
        http://your_domain/image2.png?Policy...
        http://your_domain/file1.json?Policy...
        

        不要忘记通过将此行添加到 app.yaml 来启用 pycrypto

        libraries:
        - name: pycrypto
          version: latest 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-14
          • 2023-04-10
          • 2013-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多