文章目錄
- Pre
- HMAC概述
- 常見的Hmac算法
- Code
- 隨機的key的生成 KeyGenerator
- HmacMD5
- 用Hmac算法取代原有的自定義的加鹽算法
- HmacMD5 VS MD5
- HmacSHA256
Pre
加密與安全_深入了解哈希算法中我們提到,
存儲用戶的哈希口令時,要加鹽存儲,目的就在于抵御彩虹表攻擊。
digest = hash(input)
正是因為相同的輸入會產生相同的輸出,我們加鹽的目的就在于,使得輸入有所變化:
digest = hash(salt + input)
這個salt可以看作是一個額外的“認證碼”,同樣的輸入,不同的認證碼,會產生不同的輸出。因此,要驗證輸出的哈希,必須同時提供“認證碼”。
Hmac算法就是一種基于密鑰的消息認證碼算法,它的全稱是Hash-based Message Authentication Code,是一種更安全的消息摘要算法。
Hmac算法總是和某種哈希算法配合起來用的。例如,我們使用MD5算法,對應的就是HmacMD5算法,它相當于“加鹽”的MD5:
HmacMD5 ≈ md5(secure_random_key, input)
HMAC概述
HMAC(Hash-based Message Authentication Code)算法是一種基于哈希函數的消息認證碼算法,用于驗證消息的完整性和認證消息的發送者。它結合了哈希函數和密鑰,通過將密鑰與消息進行哈希運算來生成消息認證碼。
HMAC的計算過程如下:
- 首先,選擇一個適當的哈希函數(如MD5、SHA-1、SHA-256等)和一個密鑰。
- 將密鑰進行適當的填充和處理,以滿足哈希函數的輸入長度要求。
- 將消息與填充后的密鑰按照特定的方式進行組合。
- 對組合后的數據進行哈希運算。
- 將哈希結果作為消息認證碼輸出。
接收方在接收到消息后,也會使用相同的密鑰和哈希函數來計算消息的HMAC值,并與發送方發送的HMAC值進行比較。如果兩者一致,則消息完整且來自合法發送者;否則,可能存在消息被篡改或來自未經授權的發送者的風險。
通俗地講,HMAC算法就像是一種“密碼驗證器”,它確保數據在傳輸過程中不被篡改。
想象你要給朋友寄一封信,但你擔心信被別人篡改了。你可以用HMAC來解決這個問題。首先,你會在信封上寫下你的簽名。但這次不是用筆簽名,而是用一種特殊的技巧來生成一個“密鑰”。這個密鑰就像是你的個人密碼,只有你和你的朋友知道。
然后,你把這個簽名和信一起寄出去。你的朋友收到信后,也知道這個密鑰。他會用同樣的方法再次生成簽名,然后比對你寄來的簽名。如果兩個簽名一樣,說明信沒有被篡改,因為只有你和你的朋友知道這個特殊的“密碼”。
所以,HMAC就是通過一種雙重的“密碼”驗證機制,確保數據的完整性和安全性。HMAC算法具有較強的安全性和廣泛的應用,常用于網絡通信、數據傳輸、數字簽名等領域,以確保數據的完整性和安全性。
常見的Hmac算法
HMAC(Hash-based Message Authentication Code)算法可以與許多哈希函數結合使用,常用的哈希函數包括:
- HMAC-MD5:使用MD5哈希函數生成HMAC。
- HMAC-SHA1:使用SHA-1哈希函數生成HMAC。
- HMAC-SHA256:使用SHA-256哈希函數生成HMAC。
- HMAC-SHA512:使用SHA-512哈希函數生成HMAC。
這些算法提供了不同的哈希函數選項,可以根據安全性需求和性能考慮選擇適合的算法。通常情況下,較新的SHA-256和SHA-512算法被認為比MD5和SHA-1更安全,因此在安全要求較高的場景中更常用。
Code
隨機的key的生成 KeyGenerator
通過使用Java標準庫中的KeyGenerator
生成安全的隨機密鑰,可以確保密鑰的隨機性和安全性,從而增強了加密算法的安全性。
KeyGenerator
類提供了生成對稱密鑰的功能,可以根據指定的算法和安全隨機數生成器來生成密鑰。通常情況下,可以使用
KeyGenerator.getInstance(String algorithm)
方法來獲取KeyGenerator
實例,- 然后使用
KeyGenerator.init(int keysize)
方法指定密鑰的長度, - 最后通過
KeyGenerator.generateKey()
方法生成密鑰。
這樣生成的密鑰通常會具有足夠的長度和隨機性,能夠抵御常見的密碼攻擊,如窮舉搜索和字典攻擊。因此,使用Java標準庫中的KeyGenerator
生成安全的隨機密鑰是一種推薦的做法,有助于提高系統的安全性。
HmacMD5
HmacMD5
可以看作帶有一個安全的key的MD5
。使用HmacMD5
而不是用MD5加salt,有如下好處:
HmacMD5
使用的key長度是64字節,更安全;Hmac
是標準算法,同樣適用于SHA-1等其他哈希算法;Hmac
輸出和原有的哈希算法長度一致。
可見,Hmac本質上就是把key混入摘要的算法。驗證此哈希時,除了原始的輸入數據,還要提供key。
package com.artisan.securityalgjava.hmac;import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.math.BigInteger;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class HmacTest {public static void main(String[] args) throws Exception{// 創建 KeyGenerator 實例并指定算法為 HmacMD5KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");// 生成隨機密鑰SecretKey key = keyGen.generateKey();// 打印隨機生成的密鑰byte[] skey = key.getEncoded();System.out.println("隨機生成的密鑰:" + new BigInteger(1, skey).toString(16));// 創建 Mac 實例并指定算法為 HmacMD5Mac mac = Mac.getInstance("HmacMD5");// 初始化 Mac 實例mac.init(key);// 更新消息mac.update("HellArtisan".getBytes("UTF-8"));// 計算 HMAC 值byte[] result = mac.doFinal();// 打印 HMAC 值System.out.println("HMAC 值:" + new BigInteger(1, result).toString(16));}
}
使用Java標準庫生成HmacMD5算法的隨機密鑰,并計算給定消息(“HellArtisan
”)的HMAC值。
用Hmac算法取代原有的自定義的加鹽算法
我們可以用Hmac算法取代原有的自定義的加鹽算法,因此,存儲用戶名和口令的數據庫結構如下:
| username | secret_key | password |
|----------|----------------------------------|---------------------------------------|
| bob | a8c06e05f92e...5e16 | 7e0387872a57c85ef6dddbaa12f376de |
| alice | e6a343693985...f4be | c1f929ac2552642b302e739bc0cdbaac |
| tim | f27a973dfdc0...6003 | af57651c3a8a73303515804d4af43790 |
每行包含用戶名(username)、隨機生成的密鑰(secret_key,長度為64字節),以及使用Hmac算法生成的密碼哈希值(password)。密鑰用于計算Hmac,確保密碼的安全性。
package com.artisan.securityalgjava.hmac;import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;/*** HMAC示例:使用預先生成的密鑰計算HMAC值* @author artisan*/
public class HmacVerifyTest {public static void main(String[] args) throws Exception {// 預先生成的密鑰byte[] hkey = new byte[]{106, 70, -110, 125, 39, -20, 52, 56, 85, 9, -19, -72, 52, -53, 52, -45, -6, 119, -63,30, 20, -83, -28, 77, 98, 109, -32, -76, 121, -106, 0, -74, -107, -114, -45, 104, -104, -8, 2, 121, 6,97, -18, -13, -63, -30, -125, -103, -80, -46, 113, -14, 68, 32, -46, 101, -116, -104, -81, -108, 122,89, -106, -109};// 創建SecretKey對象SecretKey key = new SecretKeySpec(hkey, "HmacMD5");// 獲取Mac實例并指定算法為HmacMD5Mac mac = Mac.getInstance("HmacMD5");// 使用密鑰初始化Mac實例mac.init(key);// 更新消息mac.update("HelloArtisan".getBytes("UTF-8"));// 計算HMAC值byte[] result = mac.doFinal();// 打印HMAC值System.out.println(Arrays.toString(result));// [-22, 82, 110, 65, -70, -122, 93, 121, 48, 96, -40, -78, 126, 46, -47, 112]}
}
// 創建SecretKey對象,使用預先生成的密鑰字節數組和算法名稱"HmacMD5"
SecretKey key = new SecretKeySpec(hkey, "HmacMD5");
這行代碼的作用是創建一個SecretKey對象,使用預先生成的密鑰字節數組(hkey)作為密鑰,同時指定算法名稱為"HmacMD5"。
這就是恢復SecretKey
的代碼。
HmacMD5 VS MD5
相比于直接使用MD5哈希算法,使用HmacMD5算法需要經過一些額外的步驟來生成哈希值。
下面是使用HmacMD5算法生成哈希值的步驟:
- 通過名稱"
HmacMD5
"獲取KeyGenerator
實例。 - 通過
KeyGenerator
創建一個SecretKey
實例,這個密鑰將用于初始化Mac
實例。 - 通過名稱"
HmacMD5
"獲取Mac實例。 - 用
SecretKey
初始化Mac
實例,以指定使用的密鑰。 - 對
Mac
實例反復調用update(byte[])
輸入數據,可以多次調用update
方法以輸入數據的不同部分。 - 調用
Mac
實例的doFinal()
方法獲取最終的哈希值。
這些步驟確保了使用HmacMD5
算法生成哈希值時的安全性和正確性。 HmacMD5
算法結合了MD5
哈希算法和密鑰,提供了更高的安全性和防御性,適用于需要對消息進行完整性驗證和身份認證的場景。
HmacSHA256
https://github.com/aperezdc/hmac-sha256/blob/master/hmac-sha256.c
package com.artisan.securityalgjava.hmac;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/public class HmacSHA256Example {public static void main(String[] args) {// 要加密的消息String message = "Hello, HMAC!";// 密鑰String key = "secretKey";try {// 計算HMAC-SHA256值byte[] result = calculateHmacSHA256(message, key);// 將字節數組轉換成十六進制字符串String hmacSHA256 = bytesToHex(result);// 打印HMAC-SHA256值System.out.println("HMAC-SHA256: " + hmacSHA256);} catch (NoSuchAlgorithmException | InvalidKeyException e) {e.printStackTrace();}}/*** 計算HMAC-SHA256值* @param message* @param key* @return* @throws NoSuchAlgorithmException* @throws InvalidKeyException*/public static byte[] calculateHmacSHA256(String message, String key)throws NoSuchAlgorithmException, InvalidKeyException {// 創建HmacSHA256實例Mac hmacSHA256 = Mac.getInstance("HmacSHA256");// 創建密鑰對象SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");// 使用密鑰初始化Mac實例hmacSHA256.init(secretKey);// 計算消息的HMAC-SHA256值并返回return hmacSHA256.doFinal(message.getBytes());}/*** 將字節數組轉換成十六進制字符串* @param bytes* @return*/public static String bytesToHex(byte[] bytes) {StringBuilder result = new StringBuilder();for (byte b : bytes) {result.append(String.format("%02x", b));}return result.toString();}
}
首先定義了要加密的消息和密鑰。然后,使用calculateHmacSHA256
方法計算消息的HMAC-SHA256
值。最后,將計算得到的字節數組轉換成十六進制字符串,并打印輸出。