2010-07-08 3 views
45

Estoy buscando implementar una aplicación que obtenga Twitter authorization via Oauth en Java. El primer paso es getting a request token. Aquí hay un Python example para el motor de la aplicación.¿Cómo generar un HMAC en Java equivalente a un ejemplo de Python?

Para probar mi código, estoy ejecutando Python y comprobando los resultados con Java. Aquí está un ejemplo de Python generar un mensaje Hash-Based Authentication Code (HMAC):

#!/usr/bin/python 

from hashlib import sha1 
from hmac import new as hmac 

key = "qnscAdgRlkIhAUPY44oiexBKtQbGY0orf7OV1I50" 
message = "foo" 

print "%s" % hmac(key, message, sha1).digest().encode('base64')[:-1] 

Salida:

$ ./foo.py 
+3h2gpjf4xcynjCGU5lbdMBwGOc= 

¿Cómo funciona un replicar este ejemplo en Java?

He visto un example of HMAC en Java:

try { 
    // Generate a key for the HMAC-MD5 keyed-hashing algorithm; see RFC 2104 
    // In practice, you would save this key. 
    KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); 
    SecretKey key = keyGen.generateKey(); 

    // Create a MAC object using HMAC-MD5 and initialize with key 
    Mac mac = Mac.getInstance(key.getAlgorithm()); 
    mac.init(key); 

    String str = "This message will be digested"; 

    // Encode the string into bytes using utf-8 and digest it 
    byte[] utf8 = str.getBytes("UTF8"); 
    byte[] digest = mac.doFinal(utf8); 

    // If desired, convert the digest into a string 
    String digestB64 = new sun.misc.BASE64Encoder().encode(digest); 
} catch (InvalidKeyException e) { 
} catch (NoSuchAlgorithmException e) { 
} catch (UnsupportedEncodingException e) { 
} 

Utiliza javax.crypto.Mac, todo bien. Sin embargo, los constructores SecretKey toman bytes y un algoritmo.

¿Cuál es el algoritmo en el ejemplo de Python? ¿Cómo se puede crear una clave secreta de Java sin un algoritmo?

Respuesta

64

HMACSHA1 parece ser el nombre del algoritmo que necesita:

SecretKeySpec keySpec = new SecretKeySpec(
     "qnscAdgRlkIhAUPY44oiexBKtQbGY0orf7OV1I50".getBytes(), 
     "HmacSHA1"); 

Mac mac = Mac.getInstance("HmacSHA1"); 
mac.init(keySpec); 
byte[] result = mac.doFinal("foo".getBytes()); 

BASE64Encoder encoder = new BASE64Encoder(); 
System.out.println(encoder.encode(result)); 

produce:

+3h2gpjf4xcynjCGU5lbdMBwGOc= 

Nota que he utilizado sun.misc.BASE64Encoder para una implementación rápida aquí, pero que es mejor usar algo que no depende del Sun JRE. The base64-encoder in Commons Codec sería una mejor opción, por ejemplo.

+0

buena! Gracias. – dfrankow

+0

@Bruno hola, podría explicar cómo puedo "cero almohadilla" la clave secreta aquí si es más pequeña que el tamaño de bloque recomendado, que es de 160 bits para SHA1 tnx – Spring

+0

Muchas gracias, gran ejemplo. Sin embargo, para el registro, creo que definitivamente cambias la muestra de tu código con el Apache Commons Codec o el codificador Guava;) – Brice

20

Una cuestión menor, pero si está buscando un equivalente a hmac (clave, mensaje), entonces de forma predeterminada la biblioteca de python utilizará el algoritmo MD5, por lo que debe utilizar el algoritmo HmacMD5 en Java.

Mencioné esto porque tuve este problema exacto y encontré esta respuesta que fue útil, pero me perdí la parte donde se pasó un método de digestión a hmac() y así descendí por un agujero de conejo. Con suerte, esta respuesta evitará que otros hagan lo mismo en el futuro.

p. Ej. en Python REPL

>>> import hmac 
>>> hmac.new("keyValueGoesHere", "secretMessageToHash").hexdigest() 
'1a7bb3687962c9e26b2d4c2b833b2bf2' 

Esto es equivalente al método Java:

import org.apache.commons.codec.binary.Hex; 
import javax.crypto.Mac; 
import javax.crypto.spec.SecretKeySpec; 

public class HashingUtility { 
    public static String HMAC_MD5_encode(String key, String message) throws Exception { 

     SecretKeySpec keySpec = new SecretKeySpec(
       key.getBytes(), 
       "HmacMD5"); 

     Mac mac = Mac.getInstance("HmacMD5"); 
     mac.init(keySpec); 
     byte[] rawHmac = mac.doFinal(message.getBytes()); 

     return Hex.encodeHexString(rawHmac); 
    } 
} 

Tenga en cuenta que en mi ejemplo estoy haciendo el equivalente de .hexdigest()

+3

encodeHexString no funcionaba en Android, así que tuve que usar esto en su lugar: return new String (Hex.encodeHex (rawHmac)); – JustinMorris

+0

Excelente traducción de Python a Java. Funcionó ! –