Python HMAC-SHA1 vs Java HMAC-SHA1 resultados diferentes

Tomé prestado el código Java HMAC-SHA1 de http://tools.ietf.org/html/rfc6238 y lo adapté ligeramente al código para usar un par de clave / mensaje conocido con salida conocida.

Luego intenté escribir el mismo código en Python para verificar los resultados, sin embargo, estoy obteniendo diferentes valores en Python y Java.

Los valores de Java son conocidos por ser buenos.

Código de Java:

import java.lang.reflect.UndeclaredThrowableException; import java.security.GeneralSecurityException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.math.BigInteger; import java.util.TimeZone; import java.util.Arrays; public class make_hmac { private make_hmac() {} private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text){ try { System.out.println("Key is..." + bytesToHex(keyBytes) + "\n"); Mac hmac; hmac = Mac.getInstance(crypto); SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); hmac.init(macKey); return hmac.doFinal(text); } catch (GeneralSecurityException gse) { throw new UndeclaredThrowableException(gse); } } private static byte[] hexStr2Bytes(String hex){ // Adding one byte to get the right conversion // Values starting with "0" can be converted byte[] bArray = new BigInteger("10" + hex,16).toByteArray(); // Copy all the REAL bytes, not the "first" byte[] ret = new byte[bArray.length - 1]; for (int i = 0; i < ret.length; i++) ret[i] = bArray[i+1]; return ret; } private static final int[] DIGITS_POWER // 0 1 2 3 4 5 6 7 8 = {1,10,100,1000,10000,100000,1000000,10000000,100000000 }; public static String generateTOTP(String key, String time, String returnDigits, String crypto){ int codeDigits = Integer.decode(returnDigits).intValue(); String result = null; // Using the counter // First 8 bytes are for the movingFactor // Compliant with base RFC 4226 (HOTP) while (time.length() < 16 ) time = "0" + time; // Get the HEX in a Byte[] byte[] msg = hexStr2Bytes(time); byte[] k = hexStr2Bytes(key); byte[] hash = hmac_sha(crypto, k, msg); System.out.println("I hashed key " + bytesToHex(k) + " against message " + bytesToHex(msg) + " and got...\n"); System.out.println("HASHED: " + bytesToHex(hash) + "\n"); // put selected bytes into result int int offset = hash[hash.length - 1] & 0xf; int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); int otp = binary % DIGITS_POWER[codeDigits]; result = Integer.toString(otp); while (result.length() < codeDigits) { result = "0" + result; } return result; } public static String bytesToHex(byte[] bytes) { final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[] hexChars = new char[bytes.length * 2]; int v; for ( int j = 0; j >> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } public static void main(String[] args) { // Seed for HMAC-SHA1 - 20 bytes String seed = "3132333435363738393031323334353637383930"; long T0 = 0; long X = 30; long testTime = 1111111109L; String steps = "0"; long T = (testTime - T0)/X; steps = Long.toHexString(T).toUpperCase(); while (steps.length() < 16) steps = "0" + steps; System.out.println(generateTOTP(seed, steps, "8", "HmacSHA1")); } } 

Código Python:

 import hmac from hashlib import sha1 k = "3132333435363738393031323334353637383930" msg = "00000000023523EC" print "I hashed key", k, "against msg", msg, "and got...\n" a = hmac.new(k, msg, sha1) print a.digest().encode('hex') 

Resultados de la ejecución de Java:

 Key is...3132333435363738393031323334353637383930 I hashed key 3132333435363738393031323334353637383930 against message 00000000023523EC and got... HASHED: 278C02E53610F84C40BD9135ACD4101012410A14 07081804 

Resultados de correr Python:

 I hashed key 3132333435363738393031323334353637383930 against msg 00000000023523EC and got... fa9362e87c80a1ac61f705b5f9d5095adaec9525 

La “clave” y el “mensaje” son los mismos, pero la versión de Java obtiene un HMAC diferente al de la implementación de Python.

Sospecho que hay un error sutil en alguna parte del código de Python (porque la versión de Java coincide con los resultados esperados del RFC) pero no estoy seguro de dónde. Se ve tan sencillo.

Creo que el problema es que en Java, estás utilizando los bytes en bruto como la clave (solo convirtiéndolos en una cadena hexadecimal para la salida):

 System.out.println("Key is..." + bytesToHex(keyBytes) + "\n"); // ... SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); 

Pero en Python, estás usando la cadena hexadecimal:

 k = "3132333435363738393031323334353637383930" 

Parece que puedes decodificar la cadena hexadecimal con:

 raw_key = k.decode('hex')