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

C#验证API HMAC SHA256签名时message参数取值问题

Webhook HMAC SHA256 签名校验实现

参数对应说明

  • 传入哈希函数的key:Client Secret客户端密钥转换得到的字节数组,统一使用UTF-8编码转换即可,无特殊说明不要用其他编码
  • 传入哈希函数的message:接收到POST请求时读取到的、未经过任何修改的完整原始请求体字节流

关键提醒:不要把请求体反序列化为对象后再重新序列化的JSON内容作为待计算内容。不同JSON序列化工具的字段排序、空格缩进、换行规则、特殊字符转义逻辑存在差异,哪怕JSON语义完全一致,字节层面的变化也会导致最终计算出的签名完全不匹配。

标准校验步骤

  • 接收Webhook请求时,优先读取请求原始Body流的全量字节并留存,不要先绑定到强类型模型、不要做格式处理
  • 从请求头中提取X-Signature-SHA256字段值,提前确认签名格式:绝大多数服务商返回的是十六进制小写字符串,少部分使用Base64编码,对应转换格式即可
  • 以客户端密钥为key、留存的原始请求体字节为message,计算HMAC SHA256值
  • 使用恒定时间比较方法对比你计算出的签名和请求头携带的签名,完全一致则校验通过

参考实现代码(C#)

using System.Security.Cryptography;
using System.Text;

private static bool VerifySignature(string clientSecret, string signatureFromHeader, byte[] rawRequestBody)
{
    byte[] keyBytes = Encoding.UTF8.GetBytes(clientSecret);
    using HMACSHA256 hmac = new HMACSHA256(keyBytes);
    byte[] computedHashBytes = hmac.ComputeHash(rawRequestBody);
    
    // 转换为十六进制小写字符串,若服务商返回Base64签名则替换为Convert.ToBase64String(computedHashBytes)
    string computedSignature = BitConverter.ToString(computedHashBytes).Replace("-", "").ToLowerInvariant();
    
    // 固定时间比较,避免时序攻击风险
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(computedSignature),
        Encoding.UTF8.GetBytes(signatureFromHeader.Trim().ToLowerInvariant())
    );
}

常见验签失败原因

  • 读取请求体时框架自动做了字段名格式转换(比如驼峰转下划线)、日期格式统一处理,修改了原始字节内容
  • 编码不匹配:密钥、请求体的字节转换编码和服务端不一致
  • 误将请求URL、时间戳、其他请求头字段拼入待签名内容,根据文档说明,该场景下签名仅针对原始JSON请求体计算,不需要追加其他内容
  • 普通字符串相等比较存在时序攻击风险,且容易受首尾空格、大小写影响导致匹配失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:09:16