9

in PHP I have the following function:

base64_encode(hash_hmac('sha256', $data, $secret, false));

I'm trying to create a function in Java that will give the same result for the same "data" and "secret" parameters.

I tried to use this function:

public static String base64sha256(String data, String secret) {
    Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
    SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
    sha256_HMAC.init(secret_key);
    byte[] res = sha256_HMAC.doFinal(data.getBytes());
    return Base64.encodeToString(res, Base64.NO_WRAP);
}

But I get different results for the same input


Update: This function works. Enjoy.

public static String base64sha256(String data, String secret) {
    String hash = null;
    try {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] res = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        hash = getHex(res);
        hash = Base64.encodeToString(hash.getBytes("UTF-8"), Base64.NO_WRAP);
    } catch (Exception e){}
    return hash;
}

static final String HEXES = "0123456789abcdef";
public static String getHex( byte [] raw ) {
    if ( raw == null ) {
        return null;
    }
    final StringBuilder hex = new StringBuilder( 2 * raw.length );
    for ( final byte b : raw ) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4))
                .append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}
gilad s
  • 475
  • 8
  • 16
  • data encoding maybe? ANSI vs UTF-8 for example? – gtgaxiola Jun 26 '14 at 11:52
  • I tried using getBytes("UTF-8") on both parameters and it's still not the same result as the PHP version – gilad s Jun 26 '14 at 12:08
  • See this Q&A http://stackoverflow.com/q/4680661/ where they talk about `String.getBytes()` and this one http://stackoverflow.com/q/23066005/ those were found under "Related". – Funk Forty Niner Jun 26 '14 at 12:08
  • I looked at those links and it seems that non of the examples use a secret key... Nevertheless I created a new updated version of the code based on what I've read there (you can see it above) but still didn't get the result I got from the php code. – gilad s Jun 26 '14 at 13:26
  • This works!. Needed this to generate `appsecret_proof` for Facebook Graph API. This generates the same hash as done in PHP. – A.W. Feb 12 '18 at 12:55
  • cannot find lib for this code Base64.NO_WRAP. its showing error. – Onkar Musale Feb 25 '20 at 09:26

3 Answers3

5

The output of the php function are lowercase hex digits when the fourth parameter is false. Your second java version however produces uppercase hex digits. Either correct the case difference or you could change the fourth parameter of hash_hmac to true and it will probably match with your first Java version.

Eelke
  • 20,897
  • 4
  • 50
  • 76
2

If trying to match output of drupal_hmac_base64 with Java 8, you can use the following code:

final String ALGORITHM = "HmacSHA256";
        Mac mac = Mac.getInstance(ALGORITHM);
        SecretKeySpec secret = new SecretKeySpec(authorizationKey.getBytes(), ALGORITHM);

        mac.init(secret);
        byte[] digest = mac.doFinal(body.getBytes());

        hash = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);

        return signature.equals(hash);

Note that drupal returns a hash using raw binary data (3rd parameter TRUE). Also, base64 encoding in PHP matches the URL and Filename safe base64 encoder in Java https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html#url.

  • this is this the same as base64_encode(hash_hmac('sha256', $data, $secret, false)) with Java 8 (that is what Im trying to achieve) ? – Paul Taylor Jan 27 '17 at 16:50
0

For someone who might be facing a slight change (not working) in Java result compared to PHP, my issue was in returning the hash from HmacSHA256 as String, while you should return it and pass to Hex as byte[]. Here are the working methods to simulate PHP's hash_hmac()

public String hashValue(String message) {
    byte[] hash = toHmacSHA256(message);
    String hashHexed = toHex(hash);
    return hashHexed;
}

private String toHex(byte[] value) {
    String hexed = String.format("%040x", new BigInteger(1, value));
    return hexed;
}

private byte[] toHmacSHA256(String value) {
    byte[] hash = null;
    try {
        SecretKey secretKey = new SecretKeySpec(PRIVATE_KEY.getBytes("UTF-8"), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(secretKey);
        hash = mac.doFinal(value.getBytes("UTF-8"));

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return hash;
}
Qussay Najjar
  • 561
  • 3
  • 7