ASP.NET MVC生成Lemon Squeezy X-Signature哈希不匹配求助
解决Lemon Squeezy Webhook签名验证哈希不匹配问题
核心问题分析
你的代码存在两个关键错误:
- 最初使用
SHA256.Create()直接拼接payload与密钥,这和PHP的hash_hmac逻辑完全不同(HMAC是用密钥对消息做哈希运算,而非简单拼接后哈希) - 换成
HMACSHA256.Create()后,仍未正确传入密钥,且可能受请求流读取方式影响导致计算偏差
修正后的C#代码
[HttpPost] public ActionResult lsHook() { string signingSecret = "你的Lemon Squeezy签名密钥"; string receivedSignature = Request.Headers["X-Signature"]; if (string.IsNullOrEmpty(receivedSignature)) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "缺少签名头"); } // 重置请求流指针,确保读取原始请求体(避免模型绑定已读取流导致内容异常) Request.InputStream.Seek(0, SeekOrigin.Begin); using (var streamReader = new StreamReader(Request.InputStream, Encoding.UTF8)) { string payload = streamReader.ReadToEnd(); // 正确计算HMAC-SHA256哈希 using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signingSecret))) { byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); string computedHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); if (!computedHash.Equals(receivedSignature.ToLowerInvariant())) { return new HttpStatusCodeResult(HttpStatusCode.Unauthorized, "签名验证失败"); } // 验证通过,手动反序列化处理业务逻辑 var webhookData = JsonConvert.DeserializeObject<LemonsqueezyWebhook>(payload); // ... 你的业务代码 return new HttpStatusCodeResult(HttpStatusCode.OK); } } }
关键注意事项
- 避免模型绑定干扰:如果原方法参数包含
LemonsqueezyWebhook lemonsqueezyWebhook,ASP.NET MVC会自动读取请求流进行反序列化,导致后续Seek(0)读取的内容不完整或为空。建议去掉模型绑定参数,手动反序列化payload;或者配置Request.BufferInput = true确保流可重复读取。 - 编码一致性:全程使用
Encoding.UTF8处理字节转换,和PHP的file_get_contents默认编码保持一致,不要使用Encoding.Default。 - HMAC正确用法:
HMACSHA256构造函数需传入签名密钥,再调用ComputeHash传入payload字节数组,这才和PHP的hash_hmac('sha256', $payload, $secret)逻辑完全匹配。 - 大小写统一:将计算出的哈希与接收的签名统一转为小写(或大写)后再对比,避免大小写差异导致的不匹配。
额外排查步骤
- 确认签名密钥和Lemon Squeezy后台设置完全一致,无空格、换行或特殊字符错误。
- 打印
payload和receivedSignature的原始值,与Lemon Squeezy测试工具中的payload对比,确保未被ASP.NET自动修改(如添加转义字符、改变JSON格式)。 - 用PHP代码和C#代码分别计算相同payload与密钥的哈希,对比结果是否一致,定位问题出在计算逻辑还是请求读取环节。
内容的提问来源于stack exchange,提问作者Hong
相关产品推荐
相关产品推荐

