You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

两个HMAC-SHA256实现结果差异原因及正确性确认求助

HMAC-SHA256签名结果不一致问题排查

问题背景

需要计算HMAC签名与消息中的签名对比,但得到两个不同结果:

  • 消息:7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true
  • 密钥:44782def547aaa06c910c43932b1eb0c71fc68d9d0c057550c48ec2acf6ba056
  • 结果1:/b1O7eDkBtlZ3I1xH+qMl/I1aRBDel8Y4sbLZXnDKEI=
  • 结果2:coqCmt/IZ4E3CzPvMY8zTjQVL5hYJUiBRg8UU+iCWo0=

猜测结果2正确,以下是生成两个结果的Java代码及问题分析。

核心差异:密钥的处理方式

两个代码的本质区别在于密钥的解析逻辑:

生成结果1的代码(错误逻辑)

此代码直接将密钥字符串按UTF-8编码转为字节数组当作HMAC密钥:

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Base64;

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

public class HMAC {
    static public byte[] calcHmacSha256(byte[] secretKey, byte[] message) {
        byte[] hmacSha256 = null;
        try {
          Mac mac = Mac.getInstance("HmacSHA256");
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
          mac.init(secretKeySpec);
          hmacSha256 = mac.doFinal(message);
        } catch (Exception e) {
          throw new RuntimeException("Failed to calculate hmac-sha256", e);
        }
        return hmacSha256;
      }
    
      public static void main(String[] args) {
          String message = "7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true";
          String key = "44782def547aaa06c910c43932b1eb0c71fc68d9d0c057550c48ec2acf6ba056";
          
            try {
              byte[] hmacSha256 = HMAC.calcHmacSha256(key.getBytes("UTF-8"), message.getBytes("UTF-8"));
              
              //Output of HEX
              System.out.println(String.format("Hex: %064x", new BigInteger(1, hmacSha256)));
              System.out.println("Base64: " + Base64.getEncoder().encodeToString(hmacSha256)); 
            } catch (UnsupportedEncodingException e) {
              e.printStackTrace();
            }
          }
}

问题在于:给定的密钥是64位十六进制字符串,代表32字节的原始二进制密钥,但这段代码将每个十六进制字符(如'4'、'd')当作UTF-8字符转字节,得到的是64字节的密钥,与实际需要的原始密钥完全不符。

生成结果2的代码(正确逻辑)

此代码将密钥字符串按十六进制解码为原始字节数组,符合HMAC-SHA256的密钥要求:

import java.nio.charset.StandardCharsets;
import java.security.SignatureException;

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

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

public class HMACValidator {
    
    public static void main(String[] args) throws IllegalArgumentException, SignatureException {
        

          String message = "7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true";
          String key = "44782def547aaa06c910c43932b1eb0c71fc68d9d0c057550c48ec2acf6ba056";
        
        HMACValidator demo = new HMACValidator();
        String result = demo.calculateHMAC(message, key);
        
        System.out.println(result);
    }
    
    public static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

    // To calculate the HMAC SHA-256
    public String calculateHMAC(String data, String key) throws IllegalArgumentException, SignatureException {
        try {
            if (data == null || key == null) {
                throw new IllegalArgumentException();
            }

            byte[] rawKey = Hex.decodeHex(key.toCharArray());
            // Create an hmac_sha256 key from the raw key bytes
            SecretKeySpec signingKey = new SecretKeySpec(rawKey, HMAC_SHA256_ALGORITHM);
            
            
            // Get an hmac_sha256 Mac instance and initialize with the signing key
            Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);

            mac.init(signingKey);

            // Compute the hmac on input data bytes
            byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            
            
            // Base64-encode the hmac
            return new String(Base64.encodeBase64(rawHmac));
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Missing data or key.");
        } catch (Exception e) {
            throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
        }
    }
}

这段代码通过Hex.decodeHex(key.toCharArray())将十六进制字符串解码为32字节的原始密钥,这是正确的处理方式,因此生成的结果2是有效的。

在线工具差异原因

不同在线工具的结果不一致,是因为它们默认的密钥解析方式不同:

  • 部分工具默认将密钥当作UTF-8字符串处理(和第一个错误代码逻辑一致),导致结果错误;
  • 正确的工具允许选择将密钥按十六进制解码(和第二个正确代码逻辑一致),因此得到正确结果。

结论

结果2是正确的签名值,差异的根源在于密钥的解析方式错误:第一个代码错误地将十六进制密钥当作普通UTF-8字符串处理,而正确的做法是将十六进制字符串解码为原始二进制字节作为HMAC密钥。

内容的提问来源于stack exchange,提问作者Christian Rockrohr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:35:26