在Java中使用ripemd160和密钥模拟php函数hash_hmac对字符串进行哈希处理

7

我正在尝试在Java中使用ripemd160对字符串进行哈希,以模拟以下PHP的输出:

$string = 'string';
$key = 'test';

hash_hmac('ripemd160', $string, $key);

// outputs: 37241f2513c60ae4d9b3b8d0d30517445f451fa5


尝试1

最初我尝试使用以下方法来模拟它...但我不认为可以使用ripemd160作为 `getInstance`算法?

或许可以,只是我本地没有启用它?

public String signRequest(String uri, String secret) {
    try {

        byte[] keyBytes = secret.getBytes();           
        SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");

        Mac mac = Mac.getInstance("ripemd160");
        mac.init(signingKey);

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

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

        //  Covert array of Hex bytes to a String
        return new String(hexBytes, "UTF-8");
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
} 

第二次尝试

这促使我寻找其他方式来实现以上目标,通过搜索SO和Google,我发现使用BouncyCastle可能是更好的选择。

后来我找到了这篇文章,它讨论了使用与我想要的相同算法和BouncyCastle进行哈希处理,只是没有使用密钥。(Cannot output correct hash in Java. What is wrong?

public static String toRIPEMD160(String in) {
    try {
        byte[] addr = in.getBytes();
        byte[] out = new byte[20];
        RIPEMD160Digest digest = new RIPEMD160Digest();

        byte[] rawSha256 = sha256(addr);
        String encodedSha256 = getHexString(rawSha256);
        byte[] strBytes = base64Sha256.getBytes("UTF-8");
        digest.update(strBytes, 0, strBytes.length);

        digest.doFinal(out, 0);
        return getHexString(out);
    } catch (UnsupportedEncodingException ex) {
        return null;
    }
}

我已经使它按预期运行。

问题

你会注意到在尝试2中目前没有办法提供给哈希一个密钥,我的问题是如何调整此函数以能够提供密钥并完成我需要模拟原始php函数的最后阶段:hash_hmac('ripemd160', $string, $key);


1
你尝试过将salt直接添加到message前面吗?这通常是加盐密码的方法。应该是一样的。 - qwelyt
不,这不是盐,而是与HMAC哈希交换相关的某种对称密钥。 - Asoub
1
@PerryMonschau 这不是用于密码的,请放心! - Dan
1
所以这里的$key是在AB之间共享的,它只用于让您可以哈希到相同的校验和? - qwelyt
1
我也在寻找与RipeMD160一起使用的hmac算法,但是Philip已经超越了我。如果你需要它:似乎flexiprovider也有一个适用于这些算法的Hmac类,你可以与答案1一起使用。 - Asoub
显示剩余10条评论
1个回答

6
使用Bouncy Castle中的RIPEMD160是可以的,但您需要实现HMAC,而不仅仅是对数据进行哈希处理。HMac只是H(K XOR opad, H(K XOR ipad, text)),其中H是您的哈希函数,K是密钥,text是消息,opadipad是预定义常量。为了演示它的工作原理,我从Python的实现中翻译了以下内容:
public static String signRequest(String uri, String secret) throws Exception {
    byte[] r = uri.getBytes("US-ASCII");

    // The keys must have the same block size as your hashing algorithm, in this case
    // 64 bytes right-padded with zeros.
    byte[] k_outer = new byte[64];
    System.arraycopy(secret.getBytes("US-ASCII"), 0, k_outer, 0,
        secret.getBytes("US-ASCII").length);
    byte[] k_inner = new byte[64];
    System.arraycopy(secret.getBytes("US-ASCII"), 0, k_inner, 0, 
        secret.getBytes("US-ASCII").length);

    // You'll create two nested hashes. The inner one is initialized with the
    // key xor 0x36 (byte-wise), the other one with the key xor 0x5c.
    for(int i=0; i<k_outer.length; i++)
        k_outer[i] ^= 0x5c;
    for(int i=0; i<k_inner.length; i++)
        k_inner[i] ^= 0x36;

    // Update inner hash with the key and data you want to sign
    RIPEMD160Digest d_inner = new RIPEMD160Digest();
    d_inner.update(k_inner, 0, k_inner.length);
    d_inner.update(r, 0, r.length);

    // Update outer hash with the key and the inner hash 
    RIPEMD160Digest d_outer = new RIPEMD160Digest();
    d_outer.update(k_outer, 0, k_outer.length);

    byte[] o_inner = new byte[d_inner.getDigestSize()];
    d_inner.doFinal(o_inner, 0);
    d_outer.update(o_inner, 0, o_inner.length);

    // Finally, return the hex-encoded hash
    byte[] o_outer = new byte[d_inner.getDigestSize()];
    d_outer.doFinal(o_outer, 0);

    return new String((new Hex()).encode(o_outer), "US-ASCII");
}

Bouncy Castle在其HMac中实现了此算法,因此此代码的较短变体为:
public static String signRequest(String uri, String secret) throws Exception {
    byte[] r = uri.getBytes("US-ASCII");
    byte[] k = secret.getBytes("US-ASCII");

    HMac hmac = new HMac(new RIPEMD160Digest());
    hmac.init(new KeyParameter(k));
    hmac.update(r, 0, r.length);

    byte[] out = new byte[hmac.getMacSize()];
    hmac.doFinal(out, 0);

    return new String((new Hex()).encode(out), "US-ASCII");
}

在返回行上,我得到了以下错误:“无法从类型Hex中进行静态引用非静态方法encode(byte[])”。 - Dan
@Dan encode(byte[])是一个静态方法!无论如何,实例化一个 Hex 没有什么害处。我会更新答案的,谢谢。 - Phillip
那只是一个Eclipse的问题。上面的代码确实可以工作,但是使用signRequest("string", "test");返回的结果是5bb06714c15005df12f1ebae29f2fda49dcfc17b,这与PHP函数的输出不同? - Dan
@Dan 谢谢。我在重新格式化帖子时误删除了初始化内部哈希的那一行代码...现在已修复,现在它输出的是 37241f2513c60ae4d9b3b8d0d30517445f451fa5,如预期所示。 - Phillip

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