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.
Download 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 javac
and java
commands are
accessible.
We have provided a complete program to generate an HMAC key, in the
file 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 KeyTemplates
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:
./gradlew createkey
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[0])
);
After adding these lines, and after each subsequent addition of code in the steps below, check that the code still compiles, with this:
./gradlew classes
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[1]));
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:
Files.write(Paths.get(args[2]), tag);
To test the finished program, do
./gradlew computetag
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
named build/message.txt.tag
.
Now open 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
incomplete.
Add the required code under the various comments. You should be able to
figure out what is needed, based on what you added to ComputeTag
.
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
./gradlew verifytag
You can also run all three programs in sequence, with
./gradlew run
The final verifytag
step should report that the authentication tag
is valid.
Edit data/message.txt
, changing a single character of the file.
Run the verification step one more time, with
./gradlew verifytag
This time, it should report that the tag is not valid.
□
Omit the ./
if running this on Windows. If you are using Linux or
macOS and this script won’t run, you might need to set execute
permissions using 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. ↩︎