2012-10-22 62 views
5

tôi mượn mã HMAC-SHA1 Java từ http://tools.ietf.org/html/rfc6238 và thích nghi một chút để hardcode nó sử dụng một tiếng cặp khóa/nhắn với sản lượng được biết đến.Python HMAC-SHA1 vs Java HMAC-SHA1 kết quả khác nhau

sau đó tôi đã cố gắng để viết mã tương tự bằng Python để xác minh kết quả, tuy nhiên tôi nhận được giá trị khác nhau bằng Python và Java.

giá trị Java được biết đến là tốt. đang

Java:

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

Kết quả chạy Java:

Key is...3132333435363738393031323334353637383930 

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

HASHED: 278C02E53610F84C40BD9135ACD4101012410A14 

07081804 

Kết quả chạy Python:

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

fa9362e87c80a1ac61f705b5f9d5095adaec9525 

Các "then chốt" và "thông báo" là như nhau, nhưng phiên bản Java được một HMAC khác so với việc thực hiện Python không.

Tôi nghi ngờ có một lỗi tinh tế ở đâu đó trong các mã Python (vì phiên bản Java phù hợp với kết quả dự kiến ​​từ RFC) nhưng tôi không chắc chắn nơi. Nó trông rất đơn giản.

Trả lời

8

Tôi nghĩ rằng vấn đề là ở Java, bạn đang sử dụng các byte thô như phím (chỉ chuyển đổi chúng sang một chuỗi hex cho đầu ra):

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

Nhưng trong Python, bạn đang sử dụng chuỗi hex:

k = "3132333435363738393031323334353637383930" 

Dường như bạn có thể decode the hex string với:

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

Good catch. Khắc phục: a = hmac.new (k.decode ('hex'), msg.decode ('hex'), sha1) cảm ơn! – ashgromnies

+3

Tôi cũng nhận thấy điều này trong RFC - "" "Phụ lục B. Kiểm tra Vectors ||| Bí mật kiểm tra dấu hiệu chia sẻ sử dụng các giá trị chuỗi ASCII '123456789' Với Time Bước X = 30, và kỷ nguyên Unix như giá trị ban đầu để. đếm các bước thời gian, trong đó T0 = 0, thuật toán TOTP sẽ hiển thị các giá trị sau cho các dấu thời gian các chế độ được chỉ định. "" "Nó tiếp tục nói rằng" Giá trị của T (hex) "là" 00000000023523EC "trong bảng. Vì vậy, tôi nghĩ rằng có một chút nhầm lẫn về các yếu tố đầu vào khác nhau. –

Các vấn đề liên quan