Post Thumbnail

How to Generate a HMAC Signature in Java

1. Overview

Hashed Based Message Authentication Code (HMAC) is a cryptography procedure that generates a code from a given data or String using a secret-key and a hash function like SHA256. The resulting code is called a Message Authentication Code (MAC) or signature.

HMAC does not involve encryption/decryption, as long as the same secret-key is used to sign the same message, the generated signature will always be the same.

This means we can validate the authenticity of HTTP requests, WebSocket messages, events etc. by counter-signing the incoming data and comparing the signatures.

2. Generating a Signature

We only need two components to generate an HMAC signature: a secret-key and the data to hash.

The secret-key can be a simple String of alphanumeric characters. The longer the number of characters, the better. The APIs we will use are already part of the JDK, so we don’t need to install additional libraries or dependencies.

Listing 2.1

public static String generateHMACSignature(String message, String secret) throws Exception {
    Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
    SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
    byte[] signatureBytes = hmacSHA256.doFinal(message.getBytes());
    return Base64.getEncoder().encodeToString(signatureBytes);

We converted the final signature bytes to base64 format for easy processing across different systems. The message can be JSON, XML or a simple String data; it really doesn’t matter.

Let’s see a typical usage of the function above in a unit test.

Listing 2.2

void givenSecretKeyAndMessage_whenGenerateHMACSignature_thenReturnSignature() throws Exception {
    var secretKey = "demokeynotforproduction1234567890";
    var message = "Hello World";
    var signature = HmacService.generateHMACSignature(message, secretKey);

3. Security Applications

3.1 Request Forgery Protection

By nature of the HMAC mechanism we can use it to protect against request forgery. How? The request Sender and Receiver would have shared the secret-key via a secure channel in a one-time process, and subsequent communications will follow this pattern:

  • The Sender intend to send a Hello World message
  • The Sender will generate a HMAC signature of the message using the shared secret-key
  • The Sender will send both the resulting signature and the plain Hello World message
  • The Receiver, upon receiving the message, will also generate its own signature for the received plain Hello World message with its own copy of the shared secret-key
  • The Receiver will now compare the received signature with its own generated signature
  • If the signature is exactly the same, then the Receiver will process the message. Otherwise, will reject the message

If at any point the message is modified by bad actors between the Sender and the Receiver, the message will have different signatures and become invalid.

3.2 Replay Attack Protection

We can enhance this protocol by adding one more ingredient - the current unix timestamp. Both the Sender and the Receiver will agree on a duration for which a request can be considered valid.

For example, any request older than 10 seconds will be deemed stale and won’t be processed.

  • The Sender will sign Hello World + current unix timestamp in milliseconds with the shared secret-key
  • The Sender will send Hello World, timestamp and signature to the Receiver
  • The Receiver will compute the time difference between the received timestamp and its own current unix timestamp
  • If the time difference is more than the agreed upon limit, the request will be rejected as stale
  • The Receiver, upon validating the timestamp, will then proceed to sign the message Hello World + received timestamp and compare the signature as usual to determine the message authenticity

This protocol will prevent a middleman from replaying old requests or modifying the requests in-flight. These techniques are usually used to secure API endpoints like web callbacks or internal APIs.

4. Conclusion

HMAC has multiple practical applications, simple to use and highly compatible across different programming languages. These attributes make it suitable for system security and more.

Beyond SHA256, it is also possible to use SHA512, SHA3-512 etc. The strength of HMAC is related to the length of the secret-key and the hash function used. It is advisable to use longer secret-key and secure hash functions like SHA256 or SHA512.

The complete source code is on GitHub.

Happy Coding