天天看點

「後端」加密算法之 HMAC 算法

作者:架構思考
昨天我們介紹了《「後端」加密算法之 SHA 算法》,今天我們開始介紹消息摘要算法中的 HMAC(Keyed-Hashing for Message Authentication)消息認證碼算法,MAC(Message Authentication Code,消息認證碼算法)是含有密鑰散列函數算法,相容了 MD 和 SHA 算法的特性,并在此基礎上加上了密鑰,是以MAC算法也經常被稱作 HMAC 算法。

MAC

在開始之前,我們先說下MAC算法。在現代的網絡中,身份認證是一個經常會用到的功能,在身份認證過程中,有很多種方式可以保證使用者資訊的安全,而 MAC(message authentication code) 就是一種常用的方法。

消息認證碼是對消息進行認證并确認其完整性的技術。通過使用發送者和接收者之間共享的密鑰,就可以識别出是否存在僞裝和篡改行為。

MAC 是通過【MAC 算法 + 密鑰 + 要加密的資訊】三個要素一起計算得出的。

同hash算法(消息摘要)相比,消息摘要隻能保證消息的完整性,即該消息摘要B是這個消息A生成的。而MAC算法能夠保證消息的正确性,即判斷确實發的是消息A而不是消息C。

同公私鑰體系相比,因為MAC的密鑰在發送方和接收方是一樣的,是以發送方和接收方都可以來生成MAC,而公私鑰體系因為将公鑰和私鑰分開,是以增加了不可抵賴性。

MAC有很多實作方式,比較通用的是基于hash算法的MAC,比如今天我們要講的HMAC算法。還有一種是基于分組密碼的實作,比如OMAC, CBC-MAC and PMAC等。

HMAC

HMAC 算法首先它是基于資訊摘要算法的。目前主要集合了MD和SHA兩大系列消息摘要算法。其中MD系列的算法有HmacMD2、HmacMD4、HmacMD5三種算法;SHA系列的算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512五種算法。

HMAC 算法除了需要資訊摘要算法外,還需要一個密鑰。HMAC的密鑰可以是任何長度,如果密鑰的長度超過了摘要算法資訊分組的長度,則首先使用摘要算法計算密鑰的摘要作為新的密鑰。一般不建議使用太短的密鑰,因為密鑰的長度與安全強度是相關的。通常選取密鑰長度不小于所選用摘要算法輸出的資訊摘要的長度。

MD 算法的對比

算法 摘要長度(bit) 實作方
HmacMD5 128 JDK、Bouncy Castle、Commons Codec
HmacSHA1 160 JDK、Bouncy Castle、Commons Codec
HmacSHA224 224 JDK、Bouncy Castle、Commons Codec
HmacSHA256 256 JDK、Bouncy Castle、Commons Codec
HmacSHA384 384 JDK、Bouncy Castle、Commons Codec
HmacSHA512 512 JDK、Bouncy Castle、Commons Codec

HMAC 算法實作

JDK 的 HMAC 算法實作

// 擷取 HMAC Key
public static byte[] getHmacKey(String algorithm) {
    try {
        // 1、建立密鑰生成器
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
        // 2、産生密鑰
        SecretKey secretKey = keyGenerator.generateKey();
        // 3、擷取密鑰
        byte[] key = secretKey.getEncoded();
        // 4、傳回密鑰
        return key;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

// HMAC 加密
public static String encryptHmac(byte[] data, byte[] key, String algorithm) {
    try {
        // 1、還原密鑰
        SecretKey secretKey = new SecretKeySpec(key, algorithm);
        // 2、建立MAC對象
        Mac mac = Mac.getInstance(algorithm);
        // 3、設定密鑰
        mac.init(secretKey);
        // 4、資料加密
        byte[] bytes = mac.doFinal(data);
        // 5、生成資料
        String rs = encodeHex(bytes);
        // 6、傳回十六進制加密資料
        return rs;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}           

Bouncy Castle 的HMAC 算法實作

// 擷取 HMAC Key
public static byte[] getHmacKey(String algorithm) {
    try {
        // 1、建立密鑰生成器
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
        // 2、産生密鑰
        SecretKey secretKey = keyGenerator.generateKey();
        // 3、擷取密鑰
        byte[] key = secretKey.getEncoded();
        // 4、傳回密鑰
        return key;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

// HMAC 加密
public static String encryptHmac(byte[] data, byte[] key, String algorithm) {
    HMac hmac = generateHmacByAlgorithm(algorithm);
    KeyParameter keyParameter = new KeyParameter(key);
    hmac.init(keyParameter);
    hmac.update(data, 0, data.length);
    byte[] rsData = new byte[hmac.getMacSize()];
    hmac.doFinal(rsData, 0);

    return Hex.toHexString(rsData);
}           

Commons Codec 的HMAC 算法實作

// 擷取 HMAC Key
public static byte[] getHmacKey(String algorithm) {
    try {
        // 1、建立密鑰生成器
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
        // 2、産生密鑰
        SecretKey secretKey = keyGenerator.generateKey();
        // 3、擷取密鑰
        byte[] key = secretKey.getEncoded();
        // 4、傳回密鑰
        return key;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

// HMAC 加密
public static String encryptHmac(byte[] data, byte[] key, String algorithm) {
    Mac mac = HmacUtils.getInitializedMac(HmacAlgorithms.HMAC_MD5, key);
    return Hex.encodeHexString(mac.doFinal(data));
}           

HMAC 算法的應用

1、可以驗證對端使用者的合法性

HMAC算法的一個典型應用是用在“挑戰/響應”(Challenge/Response)身份認證中,認證流程如下:

(1) 先由用戶端向伺服器發出一個驗證請求,(假設是浏覽器的GET請求)。

(2) 伺服器接到此請求後生成一個随機數并通過網絡傳輸給用戶端(此為挑戰)。

(3) 用戶端将收到的随機數與自己的密鑰進行HMAC運算并得到一個結果作為認證證據傳回給伺服器(此為響應)。

(4) 與此同時,伺服器也使用該随機數與存儲在伺服器資料庫中的該客戶密鑰進行HMAC運算,如果伺服器的運算結果與用戶端傳回的響應結果相同,則認為用戶端是一個合法使用者 。

在這個過程中,可能遭到安全攻擊的是伺服器發送的随機數和用戶端傳回的HMAC結果,而對于截獲了這兩個值的黑客而言這兩個值是沒有意義的,随機值的引入使HMAC隻在目前會話中有效,大大增強了安全性和實用性。

2、發消息給對方(或從對方接收消息)

比如你和對方共享了一個密鑰K,現在你要發消息給對方,既要保證消息沒有被篡改(完整性),又要能證明資訊确實是你本人發的(源認證),那麼就把原資訊和密鑰K經 HMAC 計算的值一起發過去。對方接到之後,使用自己手中的密鑰K和原消息計算一下HMAC的值,如果和你發送的HMAC一緻,那麼可以認為這個消息既沒有被篡改也沒有冒充。

普通雜湊演算法和 HMAC 算法的差別

普通雜湊演算法就是通過hash對要輸出的資料進行摘要,接收到資料時,再同樣對源資料進行散列,與給定的散列值比較,看收到的資料與計算的hash值是否一緻就可以了。

通常來說,傳輸的資料和散列值是不同的管道給出的,比如網頁上顯示MD5或SHA的散列值,但是下載下傳連結是某個鏡像網站的,這并不影響你下載下傳到的檔案的正确性。

如果要通過同一個管道發送資料和散列值的話(比如消息認證碼),就要考慮資料和散列值是否會同時被篡改的問題,如果第三方修改了資料,然後進行MD5散列,并一塊發給接收方,接收方并不能察覺到資料被篡改。

而HMAC算法就可以用一把發送方和接收方都有的密鑰key進行計算,而沒有這個密鑰key的第三方是無法計算出正确的散列值的,這樣就可以防止資料的來源方被篡改。

總結

HMAC 算法目前的應用場景相對還比較少,通常會直接使用加密算法來保證來源方的正确性,不過HMAC在特定場景下還是有一定用途的,大家可以根據自己的業務特點來選擇使用哪種加密算法。

文章來源:https://zhuanlan.zhihu.com/p/344092294