Python HMAC-SHA1与Java HMAC-SHA1的结果不同 [英] Python HMAC-SHA1 vs Java HMAC-SHA1 different results

查看:392
本文介绍了Python HMAC-SHA1与Java HMAC-SHA1的结果不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从 http://tools.ietf.org/html/rfc6238借用了HMAC-SHA1 Java代码并略微调整以对其进行硬编码以使用已知输出的已知密钥/消息对。

I borrowed the HMAC-SHA1 Java code from http://tools.ietf.org/html/rfc6238 and adapted slightly to hardcode it to use one known key/message pair with known output.

然后我尝试在Python中编写相同的代码以验证结果,但是我在Python和Java中获得了不同的值。

I then tried to write the same code in Python to verify the results, however I'm getting different values in Python and 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的结果:

Key is...3132333435363738393031323334353637383930

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

HASHED: 278C02E53610F84C40BD9135ACD4101012410A14

07081804

运行Python的结果:

Results of running Python:

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

fa9362e87c80a1ac61f705b5f9d5095adaec9525

密钥和消息是相同的,但是J ava版本获得了与Python实现不同的HMAC。

The "key" and "message" are the same, but the Java version gets a different HMAC than the Python implementation does.

我怀疑Python代码中存在一个微妙的错误(因为Java版本符合RFC的预期结果) )但我不知道在哪里。它看起来很简单。

I suspect there's a subtle error somewhere in the Python code(because the Java version matches the expected results from the RFC) but I'm not sure where. It looks so straightforward.

推荐答案

我认为问题是在Java中,你使用原始字节作为密钥(只将它们转换为十六进制字符串以便输出):

I think the problem is that in Java, you're using the raw bytes as the key (only converting them to a hex string for output):

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

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

But in Python, you're using the hex string:

k = "3132333435363738393031323334353637383930"

It看起来你可以解码十六进制字符串 with:

It looks like you can decode the hex string with:

raw_key = k.decode('hex')

这篇关于Python HMAC-SHA1与Java HMAC-SHA1的结果不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆