Generación de resumen HMAC-SHA1 en Java


Cada vez más APIs utilizan resúmenes (hashes) HMAC-SHA1 porque permiten unificar autenticación del cliente y verficación de integridad del mensaje en un sólo paso. Java sigue siendo el lenguaje de programación más utilizado a nivel empresarial, así que a continuación se explica cómo generar dos resúmenes HMAC-SHA1: primero de forma nativa sin ayudas de librerías externas (paquete javax.crypto) y después con ayuda de la Apache Commons Codec.

Java sin librerías externas (a partir de Java 1.4)

package org.ingenieroinformatico.examples.hmacsha1;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import java.util.Arrays;


public class Main {

	public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException  {
		final String contentToHash = "{\"cpu\": {\"manufacturer\": \"AMD\", \"model\": \"Ryzen 7 1800X\"}}";
		final String key = "DontUseThisPassword";

		/* Se inicializa el generador del algoritmo MAC. El algoritmo es 
		   HmacSHA1, pero podría ser otro: HmacSHA256 o HmacMD5. Si es
		   no es válido el algoritmo se lanza una NoSuchAlgorithmException */
		Mac hmacSha1 = Mac.getInstance("HmacSHA1");
		
		/* Se construye la clave secreta a partir de su valor serializado
		   como String, pero podrían ser los bytes en un fichero. Hay que
		   indicar el algoritmo también. Si la clave contiene bytes no 
		   permitidos o longitud inadecuada se lanza InvalidKeyException */
		SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
		
		// Se le indica al algoritmo la clave privada a usar:
		hmacSha1.init(secretKey);
		
		// Finalmente se generan los bytes del resumen:
		byte[] hmacSha1Hash = hmacSha1.doFinal(contentToHash.getBytes());
		
		/* Mostramos los bytes encriptados. Lo habitual es codificarlos en 
		   base64 y adjuntarlos en una cabecera del servicio web. */
		System.out.println("HMAC-SHA1: " + Arrays.toString(hmacSha1Hash));
	}

}

Si se ejecuta el código anterior se obtiene la salida:

HMAC-SHA1: [-121, 84, -63, -47, -49, 17, -64, -28, -123, -75, 93, 53, 104, 35, -121, 84, -26, 73, 12, -119]

Java con librería Apache Commons Codec 1.10 (o superior)

Obviamente, con la librería de Apache el código se simplifica bastante a cambio de una mayor sobrecarga:

package org.ingenieroinformatico.examples.hmacsha1;

import java.util.Arrays;

import org.apache.commons.codec.digest.HmacUtils;


public class Main {

	public static void main(String[] args) throws IllegalArgumentException {
		
		final String contentToHash = "{\"cpu\": {\"manufacturer\": \"AMD\", \"model\": \"Ryzen 7 1800X\"}}";
		final String key = "DontUseThisPassword";
		
		/* Se generan los bytes del resumen con la clave y el contenido 
		   directamente. Si la clave no es válida arroja 
		   IllegalArgumentException. */
		byte[] hmacSha1Hash = HmacUtils.hmacSha1(key, contentToHash);
		
		/* Mostramos los bytes encriptados. Lo habitual es codificarlos en 
		   base64 y adjuntarlos en una cabecera del servicio web. */
		System.out.println("HMAC-SHA1: " + Arrays.toString(hmacSha1Hash));

	}

}

El código anterior arroja la salida deseada:

HMAC-SHA1: [-121, 84, -63, -47, -49, 17, -64, -28, -123, -75, 93, 53, 104, 35, -121, 84, -26, 73, 12, -119]