【问题标题】:Implementation HMAC-SHA1 in python在 python 中实现 HMAC-SHA1
【发布时间】:2012-01-10 10:32:59
【问题描述】:

我正在尝试使用网站的 OAuth,它要求签名方法仅为“HMAC-SHA1”。

我想知道如何在 Python 中实现这一点?

【问题讨论】:

    标签: python oauth sha1 hmac


    【解决方案1】:

    伪代码:

    def sign_request():
        from hashlib import sha1
        import hmac
    
        # key = b"CONSUMER_SECRET&" #If you dont have a token yet
        key = b"CONSUMER_SECRET&TOKEN_SECRET" 
    
    
        # The Base String as specified here: 
        raw = b"BASE_STRING" # as specified by OAuth
           
        hashed = hmac.new(key, raw, sha1)
        
        # The signature
        return hashed.digest().encode("base64").rstrip('\n')
    

    签名错误通常存在于基本字符串中,请确保您理解这一点(如 OAuth1.0 规范所述:https://datatracker.ietf.org/doc/html/draft-hammer-oauth-10#section-3.4.1)。

    以下输入用于生成签名基本字符串:

    1. HTTP 方法(例如 GET)

    2. 路径(例如http://photos.example.net/photos

    3. 参数,按字母顺序排列,例如(为了便于阅读而换行):

       file=vacation.jpg
       &oauth_consumer_key=dpf43f3p2l4k3l03
       &oauth_nonce=kllo9940pd9333jh
       &oauth_signature_method=HMAC-SHA1
       &oauth_timestamp=1191242096
       &oauth_token=nnch734d00sl2jdk
       &oauth_version=1.0
       &size=original
      

    对每个部分进行连接和 URL 编码,结果如下:

    GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26 oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26 oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26 oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal

    【讨论】:

    • 最后的 [:-1] 是干什么用的?
    • 哦,最后一个字符是\n,不应该是签名的一部分。
    • 末尾的 [:-1] 确实做了一些事情,它删除了结尾的 \n,正如 Jon Nylander 所说。这是一个特别令人讨厌的细节,因为如果你忽略它,你的签名将一直匹配到最后一个字符,很容易错过。
    • 您可以使用.rstrip('\n') 代替[:1],如果您想牺牲一些简洁性以清楚地表明您正在切断尾随换行符。
    • 您能解释一下为什么您使用binascii.b2a_base64 而不能简单地使用hashed.digest().encode("base64") 吗?
    【解决方案2】:

    看在上帝的份上,如果您使用 oauth 做任何事情,请使用 Python 的 requests 库!我尝试使用 Python 中的hmac 库来实现 HMAC-SHA1,但尝试创建正确的 oauth 基本字符串等非常令人头疼。只需使用请求,它就这么简单:

    >>> import requests
    >>> from requests_oauthlib import OAuth1
    
    >>> url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
    >>> auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
    
    >>> requests.get(url, auth=auth)
    

    Requests Authentication

    Requests Oauth Library

    【讨论】:

    • 在寻找我的 api 调用 (stackoverflow.com/questions/39164472/…) 问题的解决方案两天后,我终于使用了你的技巧并继续前进
    • 这回答了我自己的几个问题;我必须将我的私钥和签名类型添加到 OAuth1 对象实例化中(请参阅此处的信息 link 例如:`token_auth = OAuth1(consumer_key, consumer_secret, auth_token,token_secret, rsa_key=pkeystring,signature_type='auth_header')` [警告, 我正在寻找 RSA 签名,所以必须添加 signature_method=SIGNATURE_RSA, 太]
    • @KayCee 很高兴我能帮上忙。在 Python 中记住:简单 > 复杂。
    【解决方案3】:

    【讨论】:

      【解决方案4】:

      最后,这是一个使用 oauthlib 的实际工作解决方案(使用 Python 3 测试)。

      我以官方 RTF 1 中给出的第一个 OAuth 步骤为例:

      Client Identifier: dpf43f3p2l4k3l03
      Client Shared-Secret: kd94hf93k423kf44
      
      POST /initiate HTTP/1.1
      Host: photos.example.net
      Authorization: OAuth realm="Photos",
          oauth_consumer_key="dpf43f3p2l4k3l03",
          oauth_signature_method="HMAC-SHA1",
          oauth_timestamp="137131200",
          oauth_nonce="wIjqoS",
          oauth_callback="http%3A%2F%2Fprinter.example.com%2Fready",
          oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
      

      oauth_signature 的值是我们要计算的值。

      以下定义了我们要签名的内容:

      # There is no query string present.
      # In case of http://example.org/api?a=1&b=2 - the value
      # would be "a=1&b=2".
      uri_query=""
      
      # The oauthlib function 'collect_parameters' automatically
      # ignores irrelevant header items like 'Content-Type' or
      # 'oauth_signature' in the 'Authorization' section.
      headers={
          "Authorization": (
              'OAuth realm="Photos", '
              'oauth_nonce="wIjqoS", '
              'oauth_timestamp="137131200", '
              'oauth_consumer_key="dpf43f3p2l4k3l03", '
              'oauth_signature_method="HMAC-SHA1", '
              'oauth_callback="http://printer.example.com/ready"'
          )
      }
      
      # There's no POST data here - in case it was: x=1 and y=2,
      # then the value would be '[("x","1"),("y","2")]'.
      data=[]
      
      # This is the above specified client secret which we need
      # for calculating the signature.
      client_secret="kd94hf93k423kf44"
      

      我们开始吧:

      import oauthlib.oauth1.rfc5849.signature as oauth
      
      params = oauth.collect_parameters(
          uri_query="",
          body=data, 
          headers=headers,
          exclude_oauth_signature=True, 
          with_realm=False
      )
      
      norm_params = oauth.normalize_parameters(params)
      
      base_string = oauth.construct_base_string(
          "POST", 
          "https://photos.example.net/initiate", 
          norm_params
      )
      
      sig = oauth.sign_hmac_sha1(
          base_string, 
          client_secret, 
          '' # resource_owner_secret - not used
      )
      

      from urllib.parse import quote_plus
      
      print(sig)
      # 74KNZJeDHnMBp0EMJ9ZHt/XKycU=
      
      print(quote_plus(sig))
      # 74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D
      

      【讨论】:

        【解决方案5】:

        你可以试试下面的方法。

        def _hmac_sha1(input_str):
                raw = input_str.encode("utf-8")
                key = 'your_key'.encode('utf-8')
                hashed = hmac.new(key, raw, hashlib.sha1)
                return base64.encodebytes(hashed.digest()).decode('utf-8')
        

        【讨论】:

          【解决方案6】:

          the oauth website 提供了多个 python 库,但如果您只对特定实现感兴趣,可以查看one of them

          【讨论】:

            【解决方案7】:

            在 Python 3.7 中有一种优化的方法可以做到这一点。 HMAC(key, msg, digest).digest() 使用优化的 C 或内联实现,这对于适合内存的消息来说更快。

            返回给定密钥和摘要的消息摘要。功能是 等效于 HMAC(key, msg, digest).digest(),但使用优化的 C 或内联实现,这对于适合的消息更快 记忆。参数 key、msg 和 digest 的含义与 在 new() 中。

            CPython 实现细节,优化后的 C 实现仅 当摘要是一个字符串和摘要算法的名称时使用,即 受 OpenSSL 支持。

            https://docs.python.org/3/library/hmac.html#hmac.digest

            【讨论】:

              【解决方案8】:

              在 Python 3.7 中有一种优化的方法可以做到这一点。 HMAC(key, msg, digest).digest() 使用优化的 C 或内联实现,这对于适合内存的消息来说更快。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-05-22
                • 1970-01-01
                • 2011-03-21
                • 2017-10-04
                • 2012-10-12
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多