IIWAB HMAC-SHA256 算法 - IIWAB

HMAC-SHA256 算法

IIWAB 10天前 ⋅ 36 阅读

一、HMAC-SHA256 核心讲解

HMAC-SHA256 本质不是“加密”(加密可逆,它不可逆),而是带密钥的哈希消息认证码,核心是把「密钥」和「SHA-256 哈希算法」结合,既能验证数据的完整性(数据没被篡改),又能验证来源真实性(只有持有密钥的人能生成有效认证码)。

1. 核心特性

  • 输出固定 256 位(32 字节),通常转成 64 位十六进制字符串;
  • 不可逆:无法从生成的 HMAC 值反推原始数据或密钥;
  • 抗篡改:哪怕原始数据改 1 个字符,生成的 HMAC 值会完全不同;
  • 需共享密钥:只有持有相同密钥的双方,才能验证 HMAC 的有效性。

2. 核心计算逻辑

简单来说,HMAC-SHA256 会先对密钥做预处理(补位/哈希),再和数据分两次结合 SHA-256 计算:

HMAC(K, M) = SHA256( (密钥⊕外填充) || SHA256( (密钥⊕内填充) || 消息 ) )

(⊕=异或,||=字节拼接,内填充=0x36重复64次,外填充=0x5C重复64次)

二、Java 代码示例(完整可运行)

Java 内置了 javax.crypto.Mac 类专门处理 HMAC 算法,无需引入第三方库,以下是完整示例,包含「生成 HMAC-SHA256 签名」和「验证签名」两个核心功能:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * HMAC-SHA256 工具类(生成签名 + 验证签名)
 */
public class HmacSha256Utils {

    // 算法名称(固定格式:Hmac + 哈希算法名)
    private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

    /**
     * 生成 HMAC-SHA256 签名(返回十六进制字符串,更易读)
     * @param data 待签名的原始数据
     * @param secretKey 共享密钥(需双方保密)
     * @return 64位十六进制签名串
     * @throws NoSuchAlgorithmException 算法不存在(理论上不会出现)
     * @throws InvalidKeyException 密钥无效(如空密钥)
     */
    public static String generateHmacSha256Hex(String data, String secretKey) 
            throws NoSuchAlgorithmException, InvalidKeyException {
        // 1. 创建 SecretKeySpec 对象(封装密钥和算法)
        SecretKeySpec secretKeySpec = new SecretKeySpec(
                secretKey.getBytes(StandardCharsets.UTF_8),
                HMAC_SHA256_ALGORITHM
        );

        // 2. 获取 Mac 实例并初始化
        Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
        mac.init(secretKeySpec);

        // 3. 计算 HMAC 字节数组
        byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));

        // 4. 转成十六进制字符串(比 Base64 更直观)
        return bytesToHex(hmacBytes);
    }

    /**
     * 验证签名是否有效
     * @param data 原始数据
     * @param secretKey 共享密钥
     * @param sign 待验证的签名(十六进制字符串)
     * @return true=签名有效,false=无效(数据被篡改/密钥错误)
     */
    public static boolean verifyHmacSha256(String data, String secretKey, String sign) {
        try {
            // 重新生成签名,和待验证签名对比
            String generatedSign = generateHmacSha256Hex(data, secretKey);
            return generatedSign.equalsIgnoreCase(sign);
        } catch (Exception e) {
            // 算法/密钥异常时,直接判定签名无效
            return false;
        }
    }

    /**
     * 字节数组转十六进制字符串(工具方法)
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            // 转成两位十六进制,不足补0
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    // 测试主方法
    public static void main(String[] args) {
        // 测试数据
        String originalData = "Hello, HMAC-SHA256!"; // 待签名数据
        String secretKey = "my_secure_key_123456";   // 共享密钥(实际需用更复杂的密钥)

        try {
            // 1. 生成签名
            String sign = generateHmacSha256Hex(originalData, secretKey);
            System.out.println("原始数据:" + originalData);
            System.out.println("HMAC-SHA256 签名(十六进制):" + sign);

            // 2. 验证签名(正常情况:数据和密钥都正确)
            boolean isValid = verifyHmacSha256(originalData, secretKey, sign);
            System.out.println("签名验证结果(数据未篡改):" + isValid); // 输出 true

            // 3. 验证篡改后的数据(模拟数据被改)
            String tamperedData = "Hello, HMAC-SHA256?!";
            boolean isTamperedValid = verifyHmacSha256(tamperedData, secretKey, sign);
            System.out.println("签名验证结果(数据被篡改):" + isTamperedValid); // 输出 false

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码关键解释

  1. 核心类
    • SecretKeySpec:封装密钥和算法,把字符串密钥转成算法可识别的密钥对象;
    • Mac:Java 处理 HMAC 算法的核心类,getInstance("HmacSHA256") 指定算法,init() 初始化密钥,doFinal() 计算最终的 HMAC 字节数组。
  2. 字节转十六进制bytesToHex 是工具方法,把二进制的 HMAC 结果转成易读的十六进制字符串(也可改用 Base64,只需替换该方法为 Base64.getEncoder().encodeToString(hmacBytes))。
  3. 签名验证:核心逻辑是“用相同密钥+相同数据重新生成签名,和待验证签名对比”,只要数据/密钥有一个不对,结果就不匹配。

运行结果示例

原始数据:Hello, HMAC-SHA256!
HMAC-SHA256 签名(十六进制):f8b8b7c9e8a7d6b5c4a3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1
签名验证结果(数据未篡改):true
签名验证结果(数据被篡改):false

三、使用注意事项

  1. 密钥安全:密钥必须保密且足够复杂(建议至少 32 字节,可随机生成),绝对不能硬编码在代码里,生产环境建议从配置中心/密钥管理服务读取;
  2. 字符编码:统一使用 UTF-8 编码(避免因编码不一致导致签名不匹配);
  3. 异常处理:实际项目中不要直接打印异常,建议用日志框架记录,并对 InvalidKeyException 做针对性处理(如密钥为空/格式错误);
  4. 性能:HMAC-SHA256 计算效率高,适合高频场景(如 API 签名)。

总结

  1. HMAC-SHA256 是「密钥+SHA-256」的组合,核心用于验证数据完整性和来源真实性,而非加密;
  2. Java 中可通过 Mac 类快速实现 HMAC-SHA256,核心步骤是“封装密钥→初始化算法→计算签名→验证签名”;
  3. 密钥安全和编码统一是使用该算法的关键,否则会导致签名验证失败或安全漏洞。

全部评论: 0

    我有话说: