1、引言
業界比較有名的不可逆加密演算法有MD5、SHA和HMAC,MD5和SHA在前面兩講中介紹過,本講介紹一下HMAC的知識和使用方法。
HMAC是Hash-based Message Authentication Code的縮寫,中文可以譯為“基於雜湊的訊息鑑權碼”。
HMAC在IETF的RFC 2104中定義,HMAC提出後,成為了事實上的因特網完全標準,在IPSec、SSL等安全協議中得到了廣泛的應用。使用百度搜索“RFC 2104”,即可得到這篇文件,該文件只有11頁,並提供了HMAC演算法的C語言原始碼,感興趣的朋友可以仔細學習。
2、HMAC原理RFC 2104文件的標題是HMAC: Keyed-Hashing for Message Authentication,從這個標題,可以看出HMAC是一個使用金鑰的雜湊演算法,可以用於訊息鑑權。
如果把金鑰當成加密演算法中的“鹽值”,我們可以粗略地將HMAC當成一個帶鹽值的雜湊演算法。
使用HMAC,我們可以對訊息的完整性進行認證,也可以對訊息傳送源進行身份認證。
HMAC在1997年就作為RFC技術標準提出,二十多年來,經受住了各種安全性攻擊,充分證明了它的安全性。
3、Java對HMAC的支援在JDK中,提供了HMAC功能,我們可以很輕鬆地對其進行使用。HMAC相關的類和介面介紹如下:
SecretKey介面:提供了金鑰操作介面;
KeyGenerator類:用於生成SecretKey物件;
SecretKeySpec類:實現了SecretKey介面,可以透過指定的位元組陣列和演算法,得到SecretKeySpec物件;
Mac類:實現了HMAC的功能。
基本上,使用這四個Java類或介面,就可以實現對HMAC的呼叫。
4、HMAC的使用下面這個例子,透過呼叫JDK的功能,實現了基於SHA256演算法的HMAC演算法的使用。這是完整的原始碼:
package com.flying.hmac;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import javax.crypto.KeyGenerator;import javax.crypto.Mac;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;@SpringBootApplicationpublic class HmacApplication { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { SpringApplication.run(HmacApplication.class, args); KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256"); SecretKey secretKey = keyGenerator.generateKey(); printBytes("secret key", secretKey.getEncoded()); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm()); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKeySpec); byte[] source = "abcd".getBytes(); byte[] result =mac.doFinal(source); printBytes("HMAC of [abcd] is ", result); } private static void printBytes(String prompt, byte[] bytes){ if (prompt == null || bytes == null){ return; } System.out.print(prompt + ":"); for (int i=0; i<bytes.length; i++){ System.out.printf(" %02X", bytes[i]); } System.out.println(); }}
程式的邏輯是每次生成一個金鑰,然後用這個金鑰對“abcd”生成訊息鑑權碼。
這是程式第一次執行的情況:
接著執行第二次,情況如下:
可以看到,由於兩次執行生成的金鑰不同,導致兩次對“abcd”生成的訊息鑑權碼不同。