Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

HMAC 基于哈希的消息认证码

基于哈希的消息认证码

MAC

1. Mac 的命名含义与背景

  • 命名含义
    MacMessage Authentication Code(消息认证码) 的缩写,用于验证数据的完整性和真实性。它通过结合密钥和消息生成一个短小的认证码,确保消息未被篡改且来源可信。
  • 背景
    在密码学中,MAC 是一种对称密钥技术(如 HMAC 算法)。Java 提供 javax.crypto.Mac 类作为标准 API,封装了 MAC 算法的实现,使开发者无需关注底层细节即可使用 HMAC-SHA256 等常见算法。

2. javax.crypto.Mac 类的作用

Mac 类的主要功能是:

  • 数据完整性验证:确保传输或存储的数据未被篡改。
  • 身份认证:验证消息来源的合法性(只有拥有密钥的实体才能生成有效的 MAC)。
  • 应用场景:API 请求签名、安全通信(如 HTTPS)、文件校验等。

3. 核心方法

以下是 Mac 类的关键方法及其作用:

a. 初始化方法

1
void init(SecretKey key)  
  • 作用:用密钥初始化 MAC 实例。
  • 注意:必须先调用此方法才能使用 update()doFinal()
  • 示例
1
2
3
SecretKey key = KeyGenerator.getInstance("HmacSHA256").generateKey();
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);

b. 数据输入方法

1
2
void update(byte[] input)  
void update(byte[] input, int offset, int len)
  • 作用:向 MAC 添加待处理的数据。支持分多次输入(如大文件的分块处理)。
  • 示例
1
2
3
4
byte[] dataPart1 = "Hello".getBytes();
byte[] dataPart2 = "World".getBytes();
mac.update(dataPart1);
mac.update(dataPart2);

c. 生成 MAC 值

1
2
byte[] doFinal()  
byte[] doFinal(byte[] input)
  • 作用:完成 MAC 计算并返回结果。调用后 Mac 对象会被重置,需重新初始化。
  • 示例
1
2
3
byte[] macResult = mac.doFinal();  // 处理已通过 update() 添加的数据
// 或一步处理全部数据
byte[] macResult = mac.doFinal("HelloWorld".getBytes());

d. 获取算法实例

1
static Mac getInstance(String algorithm)  
  • 作用:根据算法名称(如 HmacSHA256)创建 Mac 实例。
  • 示例
1
Mac mac = Mac.getInstance("HmacSHA256");

e. 其他辅助方法

1
2
3
String getAlgorithm()  
int getMacLength()
void reset()
  • 作用
    • getAlgorithm():返回当前 MAC 算法的名称。
    • getMacLength():返回 MAC 值的字节长度。
    • reset():重置对象到初始化后的状态,可复用对象避免重复创建。

使用流程示例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 生成密钥
SecretKey key = KeyGenerator.getInstance("HmacSHA256").generateKey();

// 2. 创建 Mac 实例
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);

// 3. 输入数据
mac.update("Hello".getBytes());
mac.update("World".getBytes());

// 4. 生成 MAC
byte[] macResult = mac.doFinal();

关键注意事项

  1. 密钥管理:MAC 的安全性依赖于密钥的保密性,需妥善存储(如使用硬件安全模块 HSM)。
  2. 算法选择:优先选择 HMAC-SHA256 等强算法,避免 MD5 或 SHA1 等弱算法。
  3. 线程安全Mac ==实例非线程安全==,需避免多线程共享同一实例。

HMAC

HMAC 算法的全名是 Hash-based Message Authentication Code,即基于哈希的消息认证码

什么是 HMAC?

HMAC 是一种通过结合加密哈希函数(如 SHA-256、SHA-1 等)和密钥来生成消息认证码的算法。它主要用于验证消息的完整性和真实性,确保消息在传输过程中没有被篡改,并且确实来自预期的发送方。

HMAC 的工作原理

HMAC 使用一个密钥和一个哈希函数来计算消息的认证码。其基本过程如下:

  1. 输入

    • 消息(Message):需要验证完整性的数据。
    • 密钥(Key):只有通信双方知道的秘密密钥。
    • 哈希函数(Hash Function):如 SHA-256、SHA-1 等。
  2. 计算

    • HMAC 使用密钥对消息进行两次哈希运算,确保即使攻击者知道哈希函数,也无法伪造消息认证码。
    • 公式表示为:
      1
      HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))
      • K:密钥。
      • m:消息。
      • H:哈希函数。
      • K':经过填充或截断后的密钥。
      • opadipad:固定的位模式(外填充和内填充)。
  3. 输出

    • 生成固定长度的 HMAC 值,通常与所使用的哈希函数的输出长度相同(例如,使用 SHA-256 时,输出长度为 256 位)。

HMAC 的特点

  1. 安全性

    • HMAC 的安全性依赖于底层的哈希函数和密钥的保密性。
    • 即使攻击者知道哈希函数,只要密钥保密,攻击者无法伪造有效的 HMAC。
  2. 完整性验证

    • 接收方可以使用相同的密钥和哈希函数重新计算 HMAC,并与接收到的 HMAC 进行比较。如果两者一致,则说明消息未被篡改。
  3. 广泛使用

    • HMAC 被广泛应用于各种安全协议和系统中,例如:
      • API 请求签名:用于验证请求的合法性和完整性。
      • 数据存储:保护存储数据的完整性。
      • 网络通信:如 TLS/SSL 中的握手过程。

示例:HMAC-SHA256

HMAC 可以与不同的哈希函数结合使用,常见的组合包括:

  • HMAC-SHA1 (不推荐)
  • HMAC-SHA256
  • HMAC-MD5(虽然 MD5 已不再推荐使用)

例如,使用 HMAC-SHA256 时,算法会生成一个 256 位(32 字节)的哈希值。

总结

HMAC 的全称是 Hash-based Message Authentication Code,它是一种基于哈希函数的消息认证机制,能够有效保证消息的完整性和真实性。 HMAC 广泛应用于现代密码学和网络安全领域,尤其是在需要验证数据来源和完整性的场景中。

VS MessageDigest

javax.crypto.Macjavax.crypto.MessageDigest 是 Java 中处理数据完整性和认证的两大核心类,但两者在功能、安全性和应用场景上有显著区别。以下是详细对比:

1. 核心功能差异

特性 javax.crypto.Mac javax.crypto.MessageDigest
定义 基于密钥的 消息认证码(MAC) 生成工具 无密钥的 消息摘要(哈希) 生成工具
算法示例 HmacSHA256HmacSHA1 SHA-256MD5
密钥依赖 必须使用密钥初始化(SecretKey 无需密钥
输出目的 验证数据完整性和来源真实性(防篡改+认证) 仅验证数据完整性(防篡改)

2. 安全性对比

  • Mac 的优势

    • 通过密钥增强安全性,攻击者无法伪造哈希值(需同时破解密钥和哈希算法),适用于需要验证数据来源的场景(如 API 签名)。
    • 支持 HMAC 结构(如 HmacSHA256),结合哈希算法(SHA-256)和密钥,抗碰撞性更强。
  • MessageDigest 的局限

    • 仅生成普通哈希值,无密钥保护。攻击者可篡改数据后重新生成相同哈希值(如文件替换攻击),无法验证数据来源。

3. 使用场景

场景 Mac 适用性 MessageDigest 适用性
API 签名 ✔️ 生成带密钥的签名(如 JWT) ❌ 无法验证请求来源
密码存储 ❌ 需结合 PBKDF2 等算法更安全 ✔️ 可用于简单哈希(需加盐)
文件完整性校验 ✔️ 需确保文件未被篡改且来源可信(如软件分发) ✔️ 仅校验文件是否被修改(如下载文件校验)
数据传输认证 ✔️ 保护传输数据(如 HTTPS 握手阶段) ❌ 无法防止中间人篡改

4. 代码实现差异

(1) Mac 使用流程

1
2
3
4
5
6
7
// 1. 初始化密钥
SecretKeySpec key = new SecretKeySpec("secret".getBytes(), "HmacSHA256");
// 2. 创建 Mac 实例并初始化
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
// 3. 计算 MAC 值
byte[] macResult = mac.doFinal("data".getBytes());
  • 关键步骤:必须通过 init() 设置密钥。

(2) MessageDigest 使用流程

1
2
3
4
// 1. 创建 MessageDigest 实例
MessageDigest md = MessageDigest.getInstance("SHA-256");
// 2. 计算哈希值
byte[] hash = md.digest("data".getBytes());
  • 无密钥参与,直接处理数据。

5. 安全性建议

  • **优先选择 Mac**:
    在需要验证数据来源的场景(如 API 调用、敏感数据传输),务必使用 Mac 结合强算法(如 HmacSHA256)。

  • 避免弱算法

    • MessageDigest 避免使用 MD5SHA-1(已被破解)。
    • Mac 避免使用 HmacMD5HmacSHA1

总结

维度 Mac MessageDigest
核心能力 数据完整性 + 来源认证 仅数据完整性
密钥依赖
典型用途 签名、安全传输 文件校验、密码哈希(需加盐)
安全性 高(密钥 + 哈希双重保护) 低(仅哈希)

建议:根据场景选择工具——需要身份验证时用 Mac,只需完整性校验时用 MessageDigest(但需注意算法强度)。

实现

Java的代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.apache.commons.codec.binary.Base64;

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


/**
* 签名工具类
*/
public class SignUtil {

private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");

public static String sign(String content, String secret) {
try {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] keyBytes = secret.getBytes(DEFAULT_ENCODING);
hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, "HmacSHA256"));

byte[] hash = hmacSha256.doFinal(content.getBytes(DEFAULT_ENCODING));
return Base64.encodeBase64String(hash);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}

依赖库 (Base64.encodeBase64String(hash))

1
2
3
4
5
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>