Implementation HMAC-SHA1 in python

Question:

I am trying to use the OAuth of a website, which requires the signature method to be ‘HMAC-SHA1’ only.

I am wondering how to implement this in Python?

Asked By: xiaohan2012

||

Answers:

There are multiple python libraries available at the oauth website, but if you’re just interested in a specific implementation you could have a look at one of them.

Answered By: Till
Answered By: Andrey Atapin

Pseudocodish:

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')

Signature errors usually reside in the base-string, make sure you understand this (as stated by the OAuth1.0 spec here: https://datatracker.ietf.org/doc/html/draft-hammer-oauth-10#section-3.4.1).

The following inputs are used to generate the Signature Base String:

  1. HTTP Method (for example GET)

  2. Path (for example http://photos.example.net/photos)

  3. Parameters, alphabetically, such as (line breaks for readability):

     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
    

Concatenate and URL encode each part and it ends up as:

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

Answered By: Jon Nylander

For the love of God, if you do ANYTHING with oauth, use the requests library for Python! I tried to implement HMAC-SHA1 using the hmac library in Python and it’s a lot of headaches, trying to create the correct oauth base string and such. Just use requests and it’s as simple as:

>>> 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

Answered By: Blairg23

Finally here’s an actually working solution (tested with Python 3) utilizing oauthlib.

I use the first OAuth step given as an example in the official RTF 1:

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"

The value for oauth_signature is what we would like to calculate.

The following defines what we want to sign:

# 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"

And here we go:

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
Answered By: Raffael

You can try following method.

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')
Answered By: Qy Zuo

In Python 3.7 there is an optimized way to do this. HMAC(key, msg, digest).digest() uses an optimized C or inline implementation, which is faster for messages that fit into memory.

Return digest of msg for given secret key and digest. The function is
equivalent to HMAC(key, msg, digest).digest(), but uses an optimized C
or inline implementation, which is faster for messages that fit into
memory. The parameters key, msg, and digest have the same meaning as
in new().

CPython implementation detail, the optimized C implementation is only
used when digest is a string and name of a digest algorithm, which is
supported by OpenSSL.

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

Answered By: SuperNova

In Python 3.7 there is an optimized way to do this. HMAC(key, msg, digest).digest() uses an optimized C or inline implementation, which is faster for messages that fit into memory.

Answered By: bồn vệ sinh
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.