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

Ballerina生成HMAC SHA256签名与预期结果不符的问题排查

问题

我有一个供应商通过HMAC SHA256对发送到Webhook的请求做身份验证,但用Ballerina验证签名时始终匹配不上。为排查问题,我用虚构的私钥和payload在CodeBeautify上生成了签名,同时在Ballerina里实现了对应的签名生成逻辑,但运行后两者签名完全不匹配:

  • Ballerina生成的签名字节数组:[33,1,109,19,199,246,135,242,0,50,145,48,243,133,137,180,127,109,85,52,231,239,247,107,22,220,97,67,65,131,121,172]
  • CodeBeautify生成的签名字节数组:[57,54,57,52,100,101,101,51,55,101,57,50,50,49,53,52,56,102,48,51,53,97,99,97,54,52,99,52,99,97,51,48,51,97,50,48,98,102,99,57,49,54,54,49,100,55,50,51,51,100,55,97,51,101,53,50,53,99,55,49,48,102,99,52]
  • CodeBeautify生成的签名字符串:9694dee37e9221548f035aca64c4ca303a20bfc91661d7233d7a3e525c710fc4

另外尝试转换Ballerina生成的字节数组为字符串时,会报错:error: FailedToDecodeBytes {"message":"array contains invalid UTF-8 byte value"},但CodeBeautify的字节数组可以正常还原为字符串。

附上我的Ballerina代码:

import ballerina/crypto;
import ballerina/io;

public function main() returns error? {
    // strGeneratedSignature produced with strHMACKey and jsonHMACMessage on CodeBeautify's HMAC tool

    string strHMACKey = "f785a0dfd2b63f06caf9205497c67777";
    json jsonHMACMessage = {"message": "Hello Ballerina HMAC"};
    string strGeneratedSignature = "9694dee37e9221548f035aca64c4ca303a20bfc91661d7233d7a3e525c710fc4";
    byte[] generatedSignatureBytes = strGeneratedSignature.toBytes();
    string strBody = jsonHMACMessage.toString();
    byte[] data = strBody.toBytes();
    byte[] hmacKeyBytes = strHMACKey.toBytes();
    byte[] hmacSignatureBytes = check crypto:hmacSha256(data, hmacKeyBytes);
    io:println(`Ballerina-generated signature: `, hmacSignatureBytes);
    io:println(`CodeBeautify-generated signature: `, generatedSignatureBytes);
    
    string strRegeneratedSignature = check string:fromBytes(generatedSignatureBytes);
    // string strBallerinaSignature = check string:fromBytes(hmacSignatureBytes);

    io:println(`Regenerated CodeBeautify Signature: `, strRegeneratedSignature);
    // io:println(`Ballerina Signature: `, strBallerinaSignature);

 }
解决方案

问题出在签名的编码格式不匹配,核心有两个点:

  1. HMAC签名本质是二进制数据,CodeBeautify返回的是它的十六进制字符串表示

    • Ballerina的crypto:hmacSha256直接返回原始二进制字节数组,这些字节不是合法的UTF-8字符,所以无法直接用string:fromBytes转换。
    • 你把CodeBeautify的十六进制字符串直接转成字节数组,相当于把字符串的每个字符(比如'9'、'6')转成对应的ASCII字节,这和原始的HMAC二进制数据完全不是一回事。
  2. JSON序列化的格式差异

    • Ballerina的json.toString()生成的字符串可能和CodeBeautify使用的序列化格式有细微差别(比如空格、引号格式),这也会导致HMAC输入数据不一致,最终签名不同。

修正后的代码

要让两者匹配,需要做两件事:

  • 将Ballerina生成的二进制签名转换为十六进制字符串,再和CodeBeautify的结果对比。
  • 确保JSON序列化的格式和CodeBeautify一致(比如去掉多余空格)。
import ballerina/crypto;
import ballerina/io;
import ballerina/encoding;

public function main() returns error? {
    string strHMACKey = "f785a0dfd2b63f06caf9205497c67777";
    json jsonHMACMessage = {"message": "Hello Ballerina HMAC"};
    string strGeneratedSignature = "9694dee37e9221548f035aca64c4ca303a20bfc91661d7233d7a3e525c710fc4";

    // 确保JSON序列化格式和CodeBeautify一致(无空格紧凑格式)
    string strBody = check jsonHMACMessage.toJsonString();
    byte[] data = strBody.toBytes();
    byte[] hmacKeyBytes = strHMACKey.toBytes();
    byte[] hmacSignatureBytes = check crypto:hmacSha256(data, hmacKeyBytes);

    // 将Ballerina生成的二进制签名转为十六进制字符串
    string ballerinaSignatureHex = encoding:hexEncode(hmacSignatureBytes);
    
    io:println(`Ballerina-generated hex signature: ${ballerinaSignatureHex}`);
    io:println(`CodeBeautify-generated hex signature: ${strGeneratedSignature}`);
    
    // 对比两个签名
    if ballerinaSignatureHex == strGeneratedSignature {
        io:println("签名匹配成功!");
    } else {
        io:println("签名不匹配!");
    }
}

关键说明

  • 使用encoding:hexEncode()将二进制签名转为十六进制字符串,这才是和CodeBeautify结果对应的格式。
  • 用json.toJsonString()替代toString(),生成紧凑无空格的JSON字符串,确保输入数据和CodeBeautify完全一致。
  • 原始二进制签名不是UTF-8字符串,所以不能直接用string:fromBytes转换,必须用十六进制或Base64这类编码格式来表示。

内容的提问来源于stack exchange,提问作者Bennett Reddin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 05:40:21