In this short paper I would like to present a thread-safety issue when using MAC (Message Authentication Code) in Java crypto library.
MAC and HMAC overview
MAC is a widely used mechanism for enhancing API security by signing request messages, to verify their integrity and authenticity by the receiver.
Messages are signed using a secret key that is shared between the requester and receiver. As opposed to traditional API authentications with tokens, the secret key is never transmitted over the wire in each request. Instead, it is used to sign the request message.
HMAC (Keyed-Hash Message Authentication Code) is a specific type of MAC where secret key is used along with a cryptographically secure hash function (typically SHA-256) to generate a hash for signing the message.
MAC in Java
In Java, Mac class (javax.crypto.Mac) can create a message authentication code (MAC) from binary data and a secret key. Following snippet demonstrates this:
Concurrency Issue
However, problems arise when we calculate MAC values in parallel. Let’s take for example an HTTP service that communicates with a 3rd-party API which requires MAC-based authentication. When serving multiple concurrent requests, our HTTP service fails to calculate the correct MAC value, thus authentication with 3rd-party API fails.
This behaviour should make sense because:
- Mac object is stateful since it retains internal state about the encryption process
- Most MAC algorithms are sequential (including HMAC).
Since javax.crypto.Mac is not thread-safe, new MAC instance should be used for each calculation. For example:
Moreover, since Mac object is cloneable, we can clone existing Mac object instead of initiating it for each calculation: