This exercise demonstrates how you can compute HMACs in Java. We use Google’s Tink library for this, as it provides a high-level API that is substantially cleaner and more user-friendly than what is provided by the Java standard library.
If working on your own PC, you will need to have a recent release of the Java Development Kit installed.
tink-hmac.zip and unzip it. This should create
a directory named
tink-hmac. You can work from the command line inside
this directory or, if you prefer IDEs, open the directory as a new
IntelliJ Java project. The rest of these instructions assume the use
of the command line.
Either way, Gradle is used to manage downloading of dependencies
and the compilation & execution of the code - see the README file for further
information on this. Note that you do not need to install Gradle yourself
here; the only prerequisite for this exercise is a properly installed JDK.
If you are working from the command line, you will need to make sure that
your PATH variable is set up so that the
java commands are
We have provided a complete program to generate an HMAC key, in the
CreateKey.java. You’ll find this file and the other source code
files in the
src/main/java subdirectory. Spend a few minutes studying
the code in a text editor or your IDE. Notice how the
class is used to provide a template for key generation. In this
particular case, the template is for a SHA-256 HMAC, but other options
are available - see the Tink API docs for further information.
Compile and run the program like so1:
This will output the key to the file
build/key.json. Take a moment to
examine the contents of this file. The file consists of minified
JSON data, which is easier to parse if you ‘pretty print’ it. You can
do this using Python 3, with:
python -m json.tool build/key.json
Alternatively, if you don’t have convenient access to Python, you can use one of the many online JSON pretty printers.
Open the program
ComputeTag.java in a text editor or your IDE. This
program is supposed to use a key generated by
CreateKey to compute an
HMAC authentication tag for a file. This file and the file containing
the key are specified as command line arguments. The authentication tag
will be written to another file, also specified as a command line argument.
The program is incomplete but contains comments to indicate what needs
to be added.
Under the ‘Load key details’ comment, add code to read the HMAC key from a JSON file, assuming that the name of this file is specified as the first command line argument:
KeysetHandle key = CleartextKeysetHandle.read( JsonKeysetReader.withPath(args) );
After adding these lines, and after each subsequent addition of code in the steps below, check that the code still compiles, with this:
Under the relevant comment, add this line to read the bytes of the input file, assuming that the name of this file is specified as the second command line argument:
byte data = Files.readAllBytes(Paths.get(args));
Next, add code to compute the authentication tag. This involves two
steps. The first step is to obtain a MAC primitive from the object
representing the key. The second step is to call the primitive’s
computeMac() method, passing to it the data that needs to be
authenticated. The tag will be returned from this method call as an
array of bytes:
Mac primitive = key.getPrimitive(Mac.class); byte tag = primitive.computeMac(data);
The final step is to write the bytes of the authentication tag out to the file, assuming that this file has been named as a third command line argument. This can be accomplished with a single line of code:
To test the finished program, do
This will use key file
build/key.json and the sample data file
data/message.txt, writing the authentication tag to a new binary file
VerifyTag.java. This program is supposed to verify the
authentication tag previously computed for a file. It takes the same
command line arguments as
ComputeTag and, like that program, it is
Add the required code under the various comments. You should be able to
figure out what is needed, based on what you added to
Refer to the Tink API docs if you need to.
Note that the MAC primitive’s
verifyMac() method will throw a
GeneralSecurityException if verification fails - so you’ll need to
wrap the call using
try...catch. Arrange things so that the program
prints “Auth tag is NOT valid” if the exception is thrown, and
“Auth tag is valid” if there is no exception.
Test the finished program with
You can also run all three programs in sequence, with
verifytag step should report that the authentication tag
data/message.txt, changing a single character of the file.
Run the verification step one more time, with
This time, it should report that the tag is not valid.
./ if running this on Windows. If you are using Linux or
macOS and this script won’t run, you might need to set execute
chmod u+x gradlew.
NOTE: This command may be very slow the first time that it runs, as it may need to download Gradle and the Tink library to your PC. ↩︎