Appearance
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:
- Obtain a KeyGenerator Instance: Use the name "HmacMD5" to get a
KeyGenerator
instance. - Generate a SecretKey Instance: Create a
SecretKey
instance using theKeyGenerator
. - Obtain a Mac Instance: Use the name "HmacMD5" to get a
Mac
instance. - Initialize the Mac Instance: Initialize the
Mac
instance with theSecretKey
. - Input Data: Repeatedly call
update(byte[])
on theMac
instance to input data. - Compute the Final Hash: Call
doFinal()
on theMac
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:
username | secret_key (64 bytes) | password |
---|---|---|
bob | a8c06e05f92e...5e16 | 7e0387872a57c85ef6dddbaa12f376de |
alice | e6a343693985...f4be | c1f929ac2552642b302e739bc0cdbaac |
tim | f27a973dfdc0...6003 | af57651c3a8a73303515804d4af43790 |
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.