Implementación HMAC-SHA1 en python

Estoy tratando de usar el OAuth de un sitio web, que requiere que el método de firma sea solo ‘HMAC-SHA1’.

Me pregunto cómo implementar esto en Python?

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

Los errores de firma generalmente residen en la cadena base, asegúrese de entender esto (como se indica en la especificación OAuth1.0 aquí: http://tools.ietf.org/html/draft-hammer-oauth-10#section-3.4. 1 ).

Las siguientes entradas se utilizan para generar la Cadena Base de la Firma:

  1. Método HTTP (por ejemplo GET)
  2. Ruta (por ejemplo, http://photos.example.net/photos )
  3. Parámetros, alfabéticamente, como (saltos de línea para facilitar la lectura):

     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 

La concatenación y la URL codifican cada parte y termina como:

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

Por el amor de Dios, si hace ALGO con una verdad, use la biblioteca de requests para Python! Traté de implementar HMAC-SHA1 utilizando la biblioteca hmac en Python y es un montón de dolores de cabeza, tratando de crear la cadena base correcta de oauth y tal. Solo usa las solicitudes y es tan simple como:

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

Solicitud de autenticación

Solicita la biblioteca de Oauth

Ya está allí Key-Hashing para la autenticación de mensajes

Finalmente, aquí hay una solución que realmente funciona (probada con Python 3) utilizando authlib .

Utilizo el primer paso OAuth dado como ejemplo en el RTF oficial 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" 

El valor para oauth_signature es lo que nos gustaría calcular.

Lo siguiente define lo que queremos firmar:

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

Y aquí vamos:

 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 

Hay varias bibliotecas de Python disponibles en el sitio web de oauth , pero si solo te interesa una implementación específica, puedes echar un vistazo a una de ellas .

Puedes probar el siguiente método.

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

En Python 3.7 hay una forma optimizada de hacer esto. HMAC (key, msg, digest) .digest () usa una implementación en C o en línea optimizada, que es más rápida para los mensajes que caben en la memoria.

Devolver el resumen de msg para una clave secreta dada y un resumen. La función es equivalente a HMAC (key, msg, digest) .digest (), pero utiliza una implementación en C o en línea optimizada, que es más rápida para los mensajes que caben en la memoria. Los parámetros key, msg y digest tienen el mismo significado que en new ().

El detalle de la implementación de CPython, la implementación de C optimizada solo se usa cuando el compendio es una cadena y el nombre de un algoritmo de compendio, que es compatible con OpenSSL.

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