2012-10-22 126 views
5

我借用了http://tools.ietf.org/html/rfc6238中的HMAC-SHA1 Java代碼,稍作修改以硬編碼它以使用一個已知輸出的已知密鑰/消息對。Python HMAC-SHA1與Java HMAC-SHA1不同的結果

然後我試圖在Python中編寫相同的代碼來驗證結果,但是我在Python和Java中獲得了不同的值。

Java值已知很好。

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

運行Java的結果:運行的Python

Key is...3132333435363738393031323334353637383930 

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

HASHED: 278C02E53610F84C40BD9135ACD4101012410A14 

07081804 

結果:

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

fa9362e87c80a1ac61f705b5f9d5095adaec9525 

「鍵」和「消息」是相同的,但Java版本獲得與Python實現不同的HMAC。

我懷疑Python代碼中存在一個細微的錯誤(因爲Java版本與RFC的預期結果相匹配),但我不確定它在哪裏。它看起來很直接。

回答

8

我認爲問題是,在Java中,你使用的原始字節爲重點(僅將它們轉換爲十六進制字符串輸出):

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

但是在Python中,你使用十六進制字符串:

k = "3132333435363738393031323334353637383930" 

看起來你可以用decode the hex string

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

良好的漁獲物。修正:a = hmac.new(k.decode('hex'),msg.decode('hex'),sha1) 謝謝! – ashgromnies

+3

我在RFC中也注意到了這一點 - 「」「附錄B測試向量|||測試令牌共享密鑰使用ASCII字符串值」123456789「,時間步長X = 30,Unix紀元作爲初始值計數時間步長,其中T0 = 0,TOTP算法將顯示指定模式時間戳的以下值。「」「繼續說」表格中的「T(十六進制)的值」爲「00000000023523EC」。所以我認爲對不同的投入有一些困惑。 –

相關問題