2012-10-22 17 views
5

Ho preso in prestito il codice Java HMAC-SHA1 da http://tools.ietf.org/html/rfc6238 e adattato leggermente a hardcode per utilizzare una coppia chiave/messaggio nota con output noto.Python HMAC-SHA1 vs Java HMAC-SHA1 risultati diversi

Ho quindi provato a scrivere lo stesso codice in Python per verificare i risultati, tuttavia ottengo diversi valori in Python e Java.

I valori Java sono noti per essere buoni. Codice

Java:

codice
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 < bytes.length; j++) { 
      v = bytes[j] & 0xFF; 
      hexChars[j * 2] = hexArray[v >>> 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")); 
    } 
} 

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

Risultati di Java in esecuzione:

Key is...3132333435363738393031323334353637383930 

I hashed key 3132333435363738393031323334353637383930 against message 00000000023523EC and got... 

HASHED: 278C02E53610F84C40BD9135ACD4101012410A14 

07081804 

risultati dell'esecuzione Python:

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

fa9362e87c80a1ac61f705b5f9d5095adaec9525 

La "chiave" e "messaggio" sono gli stessi, ma la versione di Java ottiene un HMAC diverso rispetto all'implementazione Python.

Sospetto che ci sia un piccolo errore da qualche parte nel codice Python (perché la versione di Java corrisponde ai risultati attesi dalla RFC) ma non sono sicuro di dove. Sembra così semplice.

risposta

8

Credo che il problema è che in Java, che si sta utilizzando i byte grezzi come la chiave (solo convertendoli in una stringa esadecimale per l'uscita):

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

Ma in Python, che si sta utilizzando la stringa esadecimale:

k = "3132333435363738393031323334353637383930" 

sembra che si può decode the hex string con:

raw_key = k.decode('hex') 
+0

Buona cattura. Correzione: a = hmac.new (k.decode ('hex'), msg.decode ('hex'), sha1) grazie! – ashgromnies

+3

L'ho notato anche nell'RFC - "" "Appendice B. Vettori test ||| Il segreto condiviso del token di test utilizza il valore di stringa ASCII" 123456789". Con il passo temporale X = 30 e l'epoca Unix come valore iniziale per conteggio dei passi temporali, dove T0 = 0, l'algoritmo TOTP visualizzerà i seguenti valori per i timestamp delle modalità specificate. "" "Si prosegue affermando che" Il valore di T (hex) "è" 00000000023523EC "nella tabella. Quindi penso che ci sia un po 'di confusione sui diversi input. –

Problemi correlati