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 approach is the Hash-based Message Authentication Code – HMAC. Used together with transport level security it provides reliable mechanizm of user authentication and message integrity validation.
Imagine, we want to create 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: 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
}