Hmac
2024年11月11日约 1019 字大约 3 分钟
2.3 Hmac
Hmac算法就是一种基于密钥的 消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。
HMAC是一种基于Hash函数的MAC,其核心思想是将消息(Message)与密钥(Key)通过特定的算法生成一个认证码(Authentication Code),用于验证消息的完整性和真实性。
Hmac算法总是和某种哈希算法配合起来用的。例如,我们使用MD5算法,对应的就是HmacMD5算法,它相当于“加盐”的MD5。
HMAC原理
HMAC算法可以分为三个主要步骤:
- (1)选择Hash函数:HMAC采用一个固定的Hash函数,如SHA-256、MD5等。Hash函数将任意长度的输入数据转化为固定长度的输出,具有良好的单向性和抗碰撞性。
- (2)预处理:将消息分成若干块,并对每一块进行填充和分组操作。填充目的是保证消息长度满足Hash函数的要求,分组则是为了方便后续计算。
- (3)计算认证码:将填充后的消息块与密钥依次经过Hash函数计算,得到认证码。最后,将所有认证码拼接在一起,形成完整的HMAC值。
HMAC应用场景
- 数据完整性验证:在传输过程中,发送方和接收方可以使用HMAC技术对数据进行加密处理。接收方在接收到数据后,通过相同的HMAC算法计算HMAC值,与发送方提供的HMAC值进行对比,验证数据的完整性。
- 数字签名:HMAC可用于实现数字签名,确保数据的来源和完整性。发送方使用私钥对HMAC值进行加密,接收方使用公钥进行解密和验证。
- 访问控制:HMAC可应用于访问控制场景,如HTTP请求头的认证。客户端在发送请求时,附带经过HMAC处理的请求头,服务器端收到请求后,使用相同的HMAC算法计算HMAC值,验证客户端的权限。
- 无线通信:在无线通信领域,HMAC可用于认证和加密,确保通信的安全性。如WiFi认证中,AP(Access Point)和STA(Station)之间采用HMAC算法进行密钥协商和数据加密。
算法类型
- HmacMD5
- HmacSHA1
- HmacSHA256
- HmacSHA512
- ...
HmacMD5
HmacMD5 ≈ md5(secure_random_key, input)
使用HmacMD5而不是用MD5加salt,有如下好处:
- HmacMD5使用的key长度是64字节,更安全;
- Hmac是标准算法,同样适用于SHA-1等其他哈希算法;
- Hmac输出和原有的哈希算法长度一致。
为了保证安全,我们不会自己指定key,而是通过Java标准库的KeyGenerator生成一个安全的随机的key。
代码样例
private static void hmacMD5() throws Exception {
String[] input = {"Hello", "World"};
// 还原 SecretKey , 便于验证,比如登录。 此数组可以来自数据库中存储的某个字段。
byte[] hkey = HexFormat.of().parseHex(
"b648ee779d658c420420d86291ec70f5" +
"cf97521c740330972697a8fad0b55f5c" +
"5a7924e4afa99d8c5883e07d7c3f9ed0" +
"76aa544d25ed2f5ceea59dcc122babc8");
SecretKey key = new SecretKeySpec(hkey, "HmacMD5");
// 打印随机生成的key:
byte[] skey = key.getEncoded();
System.out.println("key: " + HexFormat.of().formatHex(skey));
// 实例化 Mac
Mac mac = Mac.getInstance(key.getAlgorithm());
// 初始化 Mac
mac.init(key);
for (String s : input) {
mac.update(s.getBytes("UTF-8"));
}
// 执行消息摘要
byte[] result = mac.doFinal();
// 16 bytes
Preconditions.checkArgument(result.length == 16, "不符合期望");
String rs = HexFormat.of().formatHex(result);
System.out.println(rs);
Preconditions.checkArgument(
StringUtils.equals(rs, "4af40be7864efaae1473a4c601b650ae"), "不符合期望");
}
const hmacHasher =
CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA512, /* 密钥 */'key');
hmacHasher.reset();
hmacHasher.update('a12345678');
const hash = hmacHasher.finalize();
return hash + '';
HmacSHA256
private static void hmacSHA256() throws Exception {
String[] input = {"Hello", "World"};
// 每次均生成新的 SecretKey。 比如注册。
KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA256");
SecretKey key = keyGen.generateKey();
// 打印随机生成的key:
byte[] skey = key.getEncoded();
System.out.println("key: " + HexFormat.of().formatHex(skey));
// 实例化 Mac
Mac mac = Mac.getInstance(key.getAlgorithm());
// 初始化 Mac
mac.init(key);
for (String s : input) {
mac.update(s.getBytes("UTF-8"));
}
// 执行消息摘要
byte[] result = mac.doFinal();
Preconditions.checkArgument(result.length == 32, "不符合期望");
String rs = HexFormat.of().formatHex(result);
// 由于key是每次生成的。因此 rs 不固定。因此不做断言判断。
System.out.println(rs);
}
function shaxxx(){
return CryptoJS.SHA512('a12345678');
}
HmacRIPEMD
return CryptoJS.HmacRIPEMD160('a12345678', 'key');