1. 程式人生 > >Java加密解密之MAC(訊息認證碼)

Java加密解密之MAC(訊息認證碼)

上一篇帖子,我們講了訊息摘要(數字摘要),它是把一個文字/檔案 通過摘要函式(hash函式)計算出一個結果。然後把文字/檔案和摘要結果一同發給接受者
接受者接收到檔案之後,也進行摘要,把兩個摘要結果進行對比。如果一致就說明文字/檔案和摘要是一致的


但是,這裡有個問題,假設A把檔案和摘要發給B,中途被C截獲了。C把檔案改了,同時把改後的檔案進行摘要。然後把改後的檔案和重新生成的摘要發給B。

B收到結果之後,進行摘要,對比發現,是一致的。但是此時檔案是被篡改過的,B也不知道。接收方並不能察覺到資料被篡改。

所以說,普通的訊息摘要不能驗證身份和防篡改

為了解決這個問題,我們可以使用MAC(訊息認證碼(帶金鑰的hash函式))去解決

MAC,全稱 Message Authentication Code,也稱為訊息認證碼(帶金鑰的Hash函式),通訊實體雙方使用的一種驗證機制,保證訊息資料完整性的一種工具

在傳送資料之前,傳送方首先使用通訊雙方協商好的雜湊函式計算其摘要值。在雙方共享的會話金鑰作用下,由摘要值獲得訊息驗證碼。之後,它和資料一起被髮送。接收方收到報文後,首先利用會話金鑰還原摘要值,同時利用雜湊函式在本地計算所收到資料的摘要值,並將這兩個資料進行比對。若兩者相等,則報文通過認證。

說白了就是計算摘要的時候,需要一個祕鑰key,沒有祕鑰key就無法計算

注意:相同的訊息,不同的key,摘要結果不同。

下面使用Java(1.8.0_144)演示計算apache-tomcat-8.5.23.zip檔案的訊息摘要

package com.security.dgst;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;

public class MacTest {

	//祕鑰(必須要是通訊雙方共享的)
	static final String STR_KEY = "266f5fe18e714688a083df4ca9f78064";
	
	/**
	 * 其中,Mac.getInstance支援的演算法有:HmacMD5、HmacSHA1、HmacSHA256等等
	 * 全部支援的演算法見官方文件:
	 * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac
	 */
	public static byte[] mac(String algorithm, Key key, byte[] data) throws Exception {
		Mac mac = Mac.getInstance(algorithm);
		//這裡是關鍵,需要一個key(這裡就是和普通的訊息摘要的區別點)
		mac.init(key);
		
		byte[] result = mac.doFinal(data);
		return result;
	}
	
	public static void main(String[] args) throws Exception {
		byte[] data = Files.readAllBytes(Paths.get("c:/tmp/apache-tomcat-8.5.23.zip"));
		
		Key key = new SecretKeySpec(STR_KEY.getBytes(), "");
		
		//使用MD5演算法計算摘要
		byte[] md5Digest = mac("HmacMD5", key, data);
		
		//使用SHA256演算法計算摘要
		byte[] shaDigest = mac("HmacSHA256", key, data);
		
		//把摘要後的結果轉換成十六進位制的字串(也可以使用Base64進行編碼)
		System.out.println(Hex.encodeHexString(md5Digest));
		System.out.println(Hex.encodeHexString(shaDigest));
	}
}
輸出結果為:
ce078fe3134fa8b50c595e4e984f88e0
d90eec24b04b81cd235ff8d4e5a9aeb00183e253e44b6ed763328ff97f856200

然後,我們可以使用OpenSSL,加上上面使用的祕鑰key,計算摘要


對比結果,發現是一致的。

上面的,Mac.getInstance(algorithm)  引數algorithm可以支援的值除了參考官方文件,還可以通過如下程式碼得出

Security.getAlgorithms("Mac").forEach(System.out::println);
在Java8中,輸出結果如下:
PBEWITHHMACSHA512
PBEWITHHMACSHA224
PBEWITHHMACSHA256
HMACSHA384
PBEWITHHMACSHA384
HMACSHA256
HMACPBESHA1
HMACSHA224
HMACMD5
PBEWITHHMACSHA1
SSLMACSHA1
HMACSHA512
SSLMACMD5
HMACSHA1