Skip to content

Hmac Algorithms

When discussing hash algorithms earlier, we mentioned that when storing hashed passwords for users, we should add a salt to prevent rainbow table attacks.

Revisiting Hash Algorithms

Let’s recap hash algorithms:

digest = hash(input)

Because the same input always produces the same output, the purpose of adding a salt is to introduce variation in the input:

digest = hash(salt + input)

This salt can be considered an additional "authentication code." With the same input but different authentication codes, different outputs are produced. Therefore, to verify the hash output, the "authentication code" must also be provided.

What is the Hmac Algorithm?

The Hmac algorithm is a key-based message authentication code algorithm, officially known as Hash-based Message Authentication Code. It is a more secure message digest algorithm.

Hmac algorithms are always used in conjunction with a specific hash algorithm. For example, when using the MD5 algorithm, the corresponding Hmac algorithm is HmacMD5, which is akin to a "salted" MD5:

HmacMD5 ≈ md5(secure_random_key, input)

Therefore, HmacMD5 can be seen as an MD5 with a secure key. Using HmacMD5 instead of MD5 with a salt offers the following advantages:

  • Key Length: HmacMD5 uses a key length of 64 bytes, making it more secure.
  • Standardization: Hmac is a standard algorithm and can be used with other hash algorithms like SHA-1.
  • Consistent Output Length: Hmac produces outputs of the same length as the original hash algorithm.

Essentially, Hmac incorporates the key into the digest algorithm. When verifying this hash, both the original input data and the key must be provided.

Generating a Secure Key with Java's KeyGenerator

To ensure security, we do not specify the key ourselves. Instead, we generate a secure random key using Java's standard library KeyGenerator. Below is the code for using HmacMD5:

java
import javax.crypto.*;
import java.util.HexFormat;

public class Main {
    public static void main(String[] args) throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
        SecretKey key = keyGen.generateKey();
        // Print the randomly generated key:
        byte[] skey = key.getEncoded();
        System.out.println(HexFormat.of().formatHex(skey));
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(key);
        mac.update("HelloWorld".getBytes("UTF-8"));
        byte[] result = mac.doFinal();
        System.out.println(HexFormat.of().formatHex(result));
    }
}

Compared to MD5, using HmacMD5 involves the following steps:

  1. Obtain a KeyGenerator Instance: Use the name "HmacMD5" to get a KeyGenerator instance.
  2. Generate a SecretKey Instance: Create a SecretKey instance using the KeyGenerator.
  3. Obtain a Mac Instance: Use the name "HmacMD5" to get a Mac instance.
  4. Initialize the Mac Instance: Initialize the Mac instance with the SecretKey.
  5. Input Data: Repeatedly call update(byte[]) on the Mac instance to input data.
  6. Compute the Final Hash: Call doFinal() on the Mac instance to obtain the final hash value.

We can use the Hmac algorithm to replace the original custom salting method. Therefore, the database structure for storing usernames and passwords would look like this:

usernamesecret_key (64 bytes)password
boba8c06e05f92e...5e167e0387872a57c85ef6dddbaa12f376de
alicee6a343693985...f4bec1f929ac2552642b302e739bc0cdbaac
timf27a973dfdc0...6003af57651c3a8a73303515804d4af43790

Verifying Hmac Hashes

With the Hmac-computed hash and the SecretKey, how do we perform verification? At this point, the SecretKey cannot be generated anew using KeyGenerator; instead, it must be restored from a byte[] array:

java
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.HexFormat;

public class Main {
    public static void main(String[] args) throws Exception {
        byte[] hkey = HexFormat.of().parseHex(
                "b648ee779d658c420420d86291ec70f5" + 
                "cf97521c740330972697a8fad0b55f5c" + 
                "5a7924e4afa99d8c5883e07d7c3f9ed0" + 
                "76aa544d25ed2f5ceea59dcc122babc8");
        SecretKey key = new SecretKeySpec(hkey, "HmacMD5");
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(key);
        mac.update("HelloWorld".getBytes("UTF-8"));
        byte[] result = mac.doFinal();
        System.out.println(HexFormat.of().formatHex(result)); // 4af40be7864efaae1473a4c601b650ae
    }
}

The statement to restore the SecretKey is:

java
new SecretKeySpec(hkey, "HmacMD5");

Exercise

Use the Hmac algorithm provided by BouncyCastle to compute a hash.

Summary

  • The Hmac algorithm is a standard key-based hash algorithm that can be used with hash algorithms like MD5 and SHA-1. It produces digests of the same length as the original hash algorithms.
  • Hmac enhances security by incorporating a secure key, making it more resistant to attacks compared to simple hashing with salts.
  • When using third-party algorithms like Hmac, it is essential to generate and manage keys securely, typically using Java's KeyGenerator.
  • Hmac ensures that even if two users have the same password, their hashed passwords will differ due to unique keys, thereby mitigating rainbow table attacks.
Hmac Algorithms has loaded