Skip to main content
  1. Posts/

API Authentication: Generating HMAC digest in PHP and Java

Table of Contents

User authentication is an important part of the web service API design. One of the common approaches is the Hash-based Message Authentication Code – HMAC. Used together with transport level security, it provides a reliable mechanizm of user authentication and message integrity validation.

Imagine, we want to create a java web service for our customers. Data encryption will be guaranteed by using https connection with TLS. We will implement API user authentication by using public API Key ID and a API Key Secret. User should generate an API Key: a unique pair of Key ID and a Key Secret for his application. User should send that Key ID, message payload and a digest with every request. Digest is generated by signing all HTTP headers and message payload with Key Secret. (see Amazon’s recommendations)

In PHP there is a function hash_hmac for generating keyed hash value using the HMAC method. Here is the example:

<?php

$keyId = 'd36cb306-9341-466f-a794-d49fbc485d8b';
$payload = '{"command": "buy", "amount":10, currency":"EURUSD"}';
$secret = 'se1cr2et3w0r4d';

echo 'SHA-512 HMAC Digest: ', hash_hmac('sha512', $keyId . $payload, $secret);

Output:

SHA-512 HMAC Digest: 577a7927f55bc6ed1eaec08f7298e7c7596b6f951c4c6e8f24324fd9a1f0790adfdecbbd5ab73ad543fec7e6c3c23246a5dd8fae526e0b802ae99faccd06a29c

Call PHP function hash_algos to get a list of supported algorithms.

How to validate the digest in Java:

String apiKey = ... // X-KEY
byte[] secret = ... //
String rawPayload = ...
String receivedDigest = ... //
...

Mac digest = Mac.getInstance("HmacSHA512");
SecretKeySpec secretKey = new SecretKeySpec(secret, HMAC_SHA_512);

digest.init(secretKey);
digest.update(apiKey.getBytes(StandardCharsets.UTF_8));
digest.update(rawPayload.getBytes(StandardCharsets.UTF_8));
final byte[] expectedDigest = digest.doFinal();
digest.reset();

final byte[] receivedDigestBytes = DatatypeConverter.parseHexBinary(receivedDigest);
if (!MessageDigest.isEqual(receivedDigestBytes, expectedDigest)) {
    // invalid digest
}

References #