7

I'm trying to hash a String in Java using ripemd160 to emulate the output of the following php:

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

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

// outputs: 37241f2513c60ae4d9b3b8d0d30517445f451fa5


Attempt 1

Initially I'd tried to emulate it using the following... however I don't believe it's possible to use ripemd160 as a getInstance` algorithm?

Or maybe it is and I just don't have it locally enabled?

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);
    }
} 

Attempt 2

This led me to look for other ways to accomplish the above, through SO and Google I found that looking at BouncyCastle may be a better way to go.

I then found this post that talks about hashing using the same algorithm as I'd like and also BouncyCastle, it just doesn't use a key. (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;
    }
}

I have this working as it would be expected to.

Issue

You'll note that in attempt 2 there is currently no way to supply a key for the hashing, my question is how can I adapt this function to be able to supply a key and accomplish the last stage of what I need to do to be able to emulate the original php function: hash_hmac('ripemd160', $string, $key);

Dan
  • 11,914
  • 14
  • 49
  • 112
  • What is `$key` supposed to do here? Is it a `salt`? – qwelyt Sep 19 '16 at 08:32
  • @Dan that doesn't sound like a salt, maybe I'm not reading it right but, salting is adding a randomly generated string to the source that's unique to each user to prevent identical sources looking the same after hashing. – Perry Monschau Sep 19 '16 at 08:36
  • 1
    Have you tried just pre-pending your `salt` to your `message`? That is usually how you do when salting passwords. Should be the same. – qwelyt Sep 19 '16 at 08:38
  • No it's not salt, it's some kind of symmetry key proper to the hmac hash exchange. – Asoub Sep 19 '16 at 08:41
  • Sorry no it's not a salt, it's a key: `Shared secret key used for generating the HMAC variant of the message digest.` https://secure.php.net/manual/en/function.hash-hmac.php Monday morning! – Dan Sep 19 '16 at 08:42
  • If you're storing passwords I highly highly recommend you salt them. For why: https://www.youtube.com/watch?v=8ZtInClXe1Q. – Perry Monschau Sep 19 '16 at 08:51
  • I'm not really sure, but they seem to be using an Engine class to add key. However, I'm not sure it is for the hmac key: http://direct.massapi.com/source/manual/lcrypto-j2me-126/src/org/bouncycastle/crypto/test/ISO9796Test.java.html#499 just search "RIPEMD160Digest" – Asoub Sep 19 '16 at 08:53
  • 1
    @PerryMonschau it isn't for passwords, don't worry! – Dan Sep 19 '16 at 08:58
  • 1
    So `$key` here is shared between `A` and `B`, and it's only used so you can hash to the same checksum? – qwelyt Sep 19 '16 at 09:04
  • @Chewtoy precisely – Dan Sep 19 '16 at 10:52
  • Then it is pretty much like salting, only you have a known static salt. – qwelyt Sep 19 '16 at 12:17
  • Have you tried https://rosettacode.org/wiki/RIPEMD-160#Java ? – rkosegi Sep 21 '16 at 08:36
  • @rkosegi please see `Attempt 2`? – Dan Sep 21 '16 at 08:38
  • I found an example: https://llvm.org/svn/llvm-project/llvm-gcc-4.2/tags/RELEASE_22/libjava/classpath/gnu/java/security/hash/RipeMD160.java – ACV Sep 21 '16 at 08:58
  • 1
    I was also looking for hmac algorithm to use with RipeMD160, but Philip beat me. If you need it: it seems that flexiprovider also have a Hmac class for this algorithms, that you can use with answer 1. – Asoub Sep 21 '16 at 09:49

1 Answers1

6

Using RIPEMD160 from Bouncy Castle is fine, but you'll have to implement HMAC, not just hash your data. HMac it just H(K XOR opad, H(K XOR ipad, text)), where H is your hash function, K is the secret, text the message and opad and ipad are predefined constants. To demonstrate how it works, I translated the following from Python's implementation:

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 implements this algorithm in its HMac class, so a shorter variant of this code is

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");
}
Phillip
  • 13,448
  • 29
  • 41
  • On the return line, I get: `Cannot make a static reference to the non-static method encode(byte[]) from the type Hex` – Dan Sep 21 '16 at 09:11
  • @Dan [But `encode(byte[])` is a static method](https://people.eecs.berkeley.edu/~jonah/bc/org/bouncycastle/util/encoders/Hex.html)!? Whatever, there's no harm in instanciating a Hex.. I'll update the answer, thanks. – Phillip Sep 21 '16 at 09:12
  • Just an eclipse issue there then. The above does work but using `signRequest("string", "test");` returns `5bb06714c15005df12f1ebae29f2fda49dcfc17b` which isn't the same output as the php function? – Dan Sep 21 '16 at 09:20
  • @Dan Thanks. I accidentally deleted the line that initialized the inner hash when I reformatted the post.. fixed that now, now it prints `37241f2513c60ae4d9b3b8d0d30517445f451fa5`, as expected. – Phillip Sep 21 '16 at 09:27