Reputation: 485
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();
}
Upvotes: 9
Views: 10509
Reputation: 21
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.
Upvotes: 2
Reputation: 591
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;
}
Upvotes: 0
Reputation: 21993
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.
Upvotes: 5