如何在 Java 中生成与 Python 示例等效的 HMAC?

Posted

技术标签:

【中文标题】如何在 Java 中生成与 Python 示例等效的 HMAC?【英文标题】:How to generate an HMAC in Java equivalent to a Python example? 【发布时间】:2011-03-13 14:20:24 【问题描述】:

我正在考虑在 Java 中实现一个获取 Twitter authorization via Oauth 的应用程序。第一步是getting a request token。这是应用引擎的Python example。

为了测试我的代码,我正在运行 Python 并使用 Java 检查输出。以下是 Python 生成基于哈希的消息验证码 (HMAC) 的示例:

#!/usr/bin/python

from hashlib import sha1
from hmac import new as hmac

key = "qnscAdgRlkIhAUPY44oiexBKtQbGY0orf7OV1I50"
message = "foo"

print "%s" % hmac(key, message, sha1).digest().encode('base64')[:-1]

输出:

$ ./foo.py
+3h2gpjf4xcynjCGU5lbdMBwGOc=

如何在 Java 中复制这个示例?

我在 Java 中看到了 example of HMAC:

try 
    // Generate a key for the HMAC-MD5 keyed-hashing algorithm; see RFC 2104
    // In practice, you would save this key.
    KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
    SecretKey key = keyGen.generateKey();

    // Create a MAC object using HMAC-MD5 and initialize with key
    Mac mac = Mac.getInstance(key.getAlgorithm());
    mac.init(key);

    String str = "This message will be digested";

    // Encode the string into bytes using utf-8 and digest it
    byte[] utf8 = str.getBytes("UTF8");
    byte[] digest = mac.doFinal(utf8);

    // If desired, convert the digest into a string
    String digestB64 = new sun.misc.BASE64Encoder().encode(digest);
 catch (InvalidKeyException e) 
 catch (NoSuchAlgorithmException e) 
 catch (UnsupportedEncodingException e) 

它使用javax.crypto.Mac,一切都很好。但是,SecretKey 构造函数采用字节和算法。

Python 示例中的算法是什么?没有算法如何创建 Java 密钥?

【问题讨论】:

【参考方案1】:

HmacSHA1 似乎是您需要的算法名称:

SecretKeySpec keySpec = new SecretKeySpec(
        "qnscAdgRlkIhAUPY44oiexBKtQbGY0orf7OV1I50".getBytes(),
        "HmacSHA1");

Mac mac = Mac.getInstance("HmacSHA1");
mac.init(keySpec);
byte[] result = mac.doFinal("foo".getBytes());

BASE64Encoder encoder = new BASE64Encoder();
System.out.println(encoder.encode(result));

产生:

+3h2gpjf4xcynjCGU5lbdMBwGOc=

请注意,我在这里使用sun.misc.BASE64Encoder 进行快速实现,但您可能应该使用不依赖于 Sun JRE 的东西。例如,The base64-encoder in Commons Codec 会是更好的选择。

【讨论】:

@Bruno 嗨,如果密钥小于推荐的块大小(SHA1 tnx 为 160 位),你能解释一下我如何在此处“零填充”密钥 非常感谢,很好的例子。不过,作为记录,我认为您肯定会使用 Apache Commons Codec 或 Guava 编码器更改您的代码示例;) "string".getBytes() 看起来很奇怪。它将返回平台相关字节(指定编码!),并且不会覆盖整个字节值范围。我希望要么使用编码(如 bae64、base32 或 hexdigest)来表示密钥的字符串,要么使用密码强化(PBKDF)来返回纯分布良好的字节。 (当然这也是 spec/python 示例的问题)。 @eckes 很好,我自己几乎总是使用getBytes("UTF-8")。如您所说,Python示例中的字符串不是以u"...."开头的,我们也不知道是不是Python 3。 关于base64编码,在2016年,我们通过String java.xml.bind.DatatypeConverter.printBase64Binary(byte[] val)【参考方案2】:

一件小事,但如果您正在寻找与 hmac(key,message) 等效的东西,那么默认情况下,python 库将使用 MD5 算法,因此您需要在 Java 中使用 HmacMD5 算法。

我提到这一点是因为我遇到了这个确切的问题并发现这个答案很有帮助,但我错过了将摘要方法传递给 hmac() 的部分,因此陷入了困境。希望这个答案能防止其他人在未来做同样的事情。

例如在 Python REPL 中

>>> import hmac
>>> hmac.new("keyValueGoesHere", "secretMessageToHash").hexdigest()
'1a7bb3687962c9e26b2d4c2b833b2bf2'

这相当于Java方法:

import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class HashingUtility 
    public static String HMAC_MD5_encode(String key, String message) throws Exception 

        SecretKeySpec keySpec = new SecretKeySpec(
                key.getBytes(),
                "HmacMD5");

        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(keySpec);
        byte[] rawHmac = mac.doFinal(message.getBytes());

        return Hex.encodeHexString(rawHmac);
    

请注意,在我的示例中,我执行的操作相当于 .hexdigest()

【讨论】:

encodeHexString 在 android 上不起作用,所以我不得不改用它:return new String(Hex.encodeHex(rawHmac));

以上是关于如何在 Java 中生成与 Python 示例等效的 HMAC?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java(Android)中生成与 .Net 中相同的 AES 密钥?

java - 如何在java中生成与openssl生成的相同类型的ecdsa密钥对?

有没有办法在java中生成与字母组合的ID?

如何在 Python 中生成和绘制示例收益率曲线

在 Python 中生成马尔可夫转移矩阵

python中用于定义全局变量的空等效项[重复]