web-dev-qa-db-fra.com

Implémentation HMAC-SHA1 en python

J'essaie d'utiliser l'OAuth d'un site Web, qui requiert que la méthode de signature soit uniquement 'HMAC-SHA1'.

Je me demande comment implémenter cela en Python?

39
xiaohan2012

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

Les erreurs de signature résident généralement dans la chaîne de base, assurez-vous de bien comprendre cela (comme indiqué par la spécification OAuth1.0: http://tools.ietf.org/html/draft-hammer-oauth-10#section- 3.4.1 ).

Les entrées suivantes sont utilisées pour générer la chaîne de base de signature:

  1. Méthode HTTP (par exemple, GET)
  2. Chemin (par exemple http://photos.example.net/photos )
  3. Paramètres, par ordre alphabétique, tels que (sauts de ligne pour la lisibilité): 

    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
    

Concaténer et URL encoder chaque partie et il se termine comme:

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

70
Jon Nylander

Pour l'amour de Dieu, si vous faites quelque chose avec oauth, utilisez la bibliothèque requests pour Python! J'ai essayé d'implémenter HMAC-SHA1 à l'aide de la bibliothèque hmac en Python et c'est un casse-tête, d'essayer de créer la chaîne de base correcte oauth, etc. Il suffit d'utiliser des demandes et c'est aussi simple que:

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

Authentification des demandes

Requests Oauth Library

11
Blairg23
7
Andrey Atapin

Enfin, voici une solution qui fonctionne (testée avec Python 3) et qui utilise oauthlib .

J'utilise la première étape OAuth donnée à titre d'exemple dans le document officiel 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"

La valeur de oauth_signature correspond à ce que nous aimerions calculer.

Ce qui suit définit ce que nous voulons signer:

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

Et c'est reparti:

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

Il existe plusieurs bibliothèques python disponibles sur le site Web oauth , mais si vous êtes simplement intéressé par une implémentation spécifique, vous pouvez consulter l'une d'elles .

1
Till

Dans Python 3.7, il existe un moyen optimisé de le faire. HMAC (clé, msg, digest) .digest () utilise une implémentation optimisée en C ou en ligne, qui est plus rapide pour les messages stockés dans la mémoire.

Renvoie le résumé de msg pour la clé secrète donnée et le résumé. La fonction est équivalent à HMAC (clé, msg, digérer) .digest (), mais utilise un C .__ optimisé. ou une implémentation en ligne, ce qui est plus rapide pour les messages qui entrent dans Mémoire. Les paramètres key, msg et digest ont la même signification que dans new ().

Détail d'implémentation CPython, l'implémentation C optimisée est seulement utilisé lorsque digest est une chaîne et le nom d'un algorithme de digest, qui est supporté par OpenSSL.

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

0
SuperNova

Vous pouvez essayer la méthode suivante.

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')
0
Qy Zuo