Java中等价于PHP的hmac-SHA1

51

我正在寻找一个Java等效于这个PHP调用:

hash_hmac('sha1', "test", "secret")

我试过使用java.crypto.Mac,但两者不一致:

String mykey = "secret";
String test = "test";
try {
    Mac mac = Mac.getInstance("HmacSHA1");
    SecretKeySpec secret = new SecretKeySpec(mykey.getBytes(),"HmacSHA1");
    mac.init(secret);
    byte[] digest = mac.doFinal(test.getBytes());
    String enc = new String(digest);
    System.out.println(enc);  
} catch (Exception e) {
    System.out.println(e.getMessage());
}

键名为"secret"和"test"的输出似乎不匹配。


2
它们有什么不同?哪一个匹配sha1的测试模式?粗略查看PHP文档显示了“raw_output”选项。 - user166390
发布一些测试的输入和输出(对于二进制参数,使用十六进制编码或base-64编码)。 - erickson
7个回答

41

事实上,它们是一致的。
正如Hans Doggen已经指出的那样,除非你将原始输出参数设置为true,否则PHP会使用十六进制表示法输出消息摘要。
如果您想在Java中使用相同的表示法,可以使用类似以下内容的代码:

for (byte b : digest) {
    System.out.format("%02x", b);
}
System.out.println();

按照要求格式化输出。


20

你可以尝试在Java中实现以下代码:

private static String computeSignature(String baseString, String keyString) throws GeneralSecurityException, UnsupportedEncodingException {

    SecretKey secretKey = null;

    byte[] keyBytes = keyString.getBytes();
    secretKey = new SecretKeySpec(keyBytes, "HmacSHA1");

    Mac mac = Mac.getInstance("HmacSHA1");

    mac.init(secretKey);

    byte[] text = baseString.getBytes();

    return new String(Base64.encodeBase64(mac.doFinal(text))).trim();
}

5
这是我的实现:
        String hmac = "";

    Mac mac = Mac.getInstance("HmacSHA1");
    SecretKeySpec secret = new SecretKeySpec(llave.getBytes(), "HmacSHA1");
    mac.init(secret);
    byte[] digest = mac.doFinal(cadena.getBytes());
    BigInteger hash = new BigInteger(1, digest);
    hmac = hash.toString(16);

    if (hmac.length() % 2 != 0) {
        hmac = "0" + hmac;
    }

    return hmac;

这是我唯一有效的解决方案。非常感谢! - Gerhard Hagerer

3
这样我就可以得到与在php中使用hash_hmac时完全相同的字符串。
String result;

try {
        String data = "mydata";
        String key = "myKey";
        // Get an hmac_sha1 key from the raw key bytes
        byte[] keyBytes = key.getBytes();
        SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");

        // Get an hmac_sha1 Mac instance and initialize with the signing key
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);

        // Compute the hmac on input data bytes
        byte[] rawHmac = mac.doFinal(data.getBytes());

        // Convert raw bytes to Hex
        byte[] hexBytes = new Hex().encode(rawHmac);

        //  Covert array of Hex bytes to a String
        result = new String(hexBytes, "ISO-8859-1");
        out.println("MAC : " + result);
}
catch (Exception e) {

}

2
这应该是正确的答案,许多人错过了十六进制编码步骤。 - Krishnaraj
1
对不起,我很无知,请问我在哪里可以找到“Hex().encode(bytes[] rawHmac)”方法? - Pedro Joaquín

3

我的HmacMD5实现-只需将算法更改为HmacSHA1:

SecretKeySpec keySpec = new SecretKeySpec("secretkey".getBytes(), "HmacMD5");
Mac mac = Mac.getInstance("HmacMD5");
mac.init(keySpec);
byte[] hashBytes = mac.doFinal("text2crypt".getBytes());
return Hex.encodeHexString(hashBytes);

3
似乎PHP使用HEX表示法来表示Java生成的字节(1a = 26)- 但我没有检查整个表达式。如果您在页面上运行字节数组,会发生什么?请注意保留HTML标记。

1

虽然我没有测试过,但你可以试试这个:

        BigInteger hash = new BigInteger(1, digest);
        String enc = hash.toString(16);
        if ((enc.length() % 2) != 0) {
            enc = "0" + enc;
        }

这是我方法的快照,可以使Java的md5和sha1与PHP匹配。

当然,使用新的StringBuilder(digest.length * 2),一个for/next循环和append(String.format("%02X"), digest[i] & 0xFF)会更少占用内存,可能也更易读。 - Maarten Bodewes
if语句需要改为while循环。否则在多个前导零的情况下无法正常工作。 - slushi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接