2012-10-22 22 views
5

Pożyczyłem kod Java HMAC-SHA1 od http://tools.ietf.org/html/rfc6238 i zaadaptowałem go nieznacznie do kodu twardego, aby użyć jednej znanej pary klucz/komunikat o znanych wynikach.Python HMAC-SHA1 vs Java HMAC-SHA1 inne wyniki

Próbowałem następnie napisać ten sam kod w Pythonie, aby zweryfikować wyniki, jednak otrzymuję różne wartości w Pythonie i Javie.

Wartości Java są znane jako dobre. Kod

Java:

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

Wyniki z systemem Java:

Key is...3132333435363738393031323334353637383930 

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

HASHED: 278C02E53610F84C40BD9135ACD4101012410A14 

07081804 

Rezultaty prowadzenia Python:

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

fa9362e87c80a1ac61f705b5f9d5095adaec9525 

"Klucz" i "wiadomość" są takie same, ale wersja Java otrzymuje inne HMAC niż implementacja Pythona.

Podejrzewam, że w kodzie Pythona występuje subtelny błąd (ponieważ wersja Java odpowiada oczekiwanym wynikom z RFC), ale nie jestem pewien, gdzie. Wygląda to tak prosto.

Odpowiedz

8

Myślę, że problemem jest to, że w języku Java, używasz surowych bajtów jako klucz (tylko przekonwertować je na sznurku hex na wyjściu):

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

Ale w Pythonie, którego używasz ciąg hex:

k = "3132333435363738393031323334353637383930" 

wygląda możesz decode the hex string z:

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

dobry połów. Napraw: a = hmac.new (k.decode ("hex"), msg.decode ("hex"), sha1) dzięki! – ashgromnies

+3

Zauważyłem to również w RFC - "" "Dodatek B. Wektory testowe || Tajemny klucz udostępniony tokenu testowego używa wartości ciągu ASCII" 123456789. "Z przedziałem czasu X = 30, a Epoka uniksowa jako wartość początkowa Zliczaj czas, w którym T0 = 0, algorytm TOTP wyświetli następujące wartości dla określonych trybów znaczników czasu. "" Dalej mówi się, że "Wartość T (hex)" to "00000000023523EC" w tabeli. Myślę więc, że na różnych wejściach jest trochę zamieszania. –