Exercise 4: Computing HMACs in Java

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.

Setting Up

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.

Key Creation

  1. 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.

  2. 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.

Computing The Authentication Tag

  1. 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
    
  2. 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]));
    
  3. 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);
    
  4. 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);
    
  5. 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.

Tag Verification

  1. 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.

  2. 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.

  3. 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.


  1. 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. ↩︎