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
}