以下函数用于接收Lemon Squeezy webhook请求:

public ActionResult lsHook(LemonsqueezyWebhook lemonsqueezyWebhook)
{
...
}

它按照有效载荷数据的预期工作.Lemon Squeezy使用X-Signature来帮助确认请求实际上来自Lemon Squeezy,而不是钓鱼网站.下面是他们的代码示例:

$secret    = '[SIGNING_SECRET]'; // from your webhook settings
$payload   = file_get_contents('php://input');
$hash      = hash_hmac('sha256', $payload, $secret);
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

if (!hash_equals($hash, $signature)) {
    throw new Exception('Invalid signature.');
}

以下是我在ActionResult lsHook(LemonsqueezyWebhook lemonsqueezyWebhook)中执行相同操作的C#代码:

StringBuilder sb = new StringBuilder("X-Signature")
if (Request.Headers.AllKeys.Contains("X-Signature"))
{
    foreach (string sValue in Request.Headers.GetValues("X-Signature"))
    {
        sb.Append(Environment.NewLine).Append(sValue);
    }
    Request.InputStream.Seek(0, SeekOrigin.Begin);
    string sPayload = new StreamReader(Request.InputStream).ReadToEnd();
    using (var sha256 = SHA256.Create())
    {
        byte[] payloadBytes = Encoding.UTF8.GetBytes(sPayload);
        byte[] secretBytes = Encoding.UTF8.GetBytes("mysecret");

        // Combine payload and secret
        byte[] data = payloadBytes.Concat(secretBytes).ToArray();

        // Hash the combined data
        byte[] hash = sha256.ComputeHash(data);
        
        sb.Append(Environment.NewLine + "hash: ").Append(BitConverter.ToString(hash).Replace("-", "").ToLower());
    }
}

以下是我从某人的一个请求中得到的:

      dab5a3c5f205f60aa09ab00098f67917e8fcc6b05e64d510b656d82f35bca97c
hash: 3ee77ac08f050e1338ca3a4a8c0162eb8ff9aa1fb7d89418881492a52da1cc37

它们不相配.sPayload看起来是正确的.它是json对象.有没有人能就可能的原因给出一个提示?

Update:

var sha256 = SHA256.Create()替换为var sha256 = HMACSHA256.Create()后,我得到了以下结果:

      10de6aa343a95017309a7e7d3b683fb35afa4be9f67c41c2d87ae1f9872e0914
hash: 9dceac34439623bff6549b2dd264672e0cc20910

推荐答案

The bug in the posted code was that the C# code only calculated the SHA256 value instead of the HMAC/SHA56 value.
After fixing this, the key was not specified correctly and the Create() method was applied, which requires the specification of the digest, which was missing.


确定HMAC/SHA256可以在.NET上实现,如下所示,使用HMASCSHA256:

using System;
using System.Security.Cryptography;
using System.Text;
...
byte[] secret = Encoding.UTF8.GetBytes("some secret");
byte[] payload = Encoding.UTF8.GetBytes("some payload");
using (var hmacSha256 = new HMACSHA256(secret)) 
{
    byte[] hash = hmacSha256.ComputeHash(payload);
    Console.WriteLine(Convert.ToHexString(hash)); // 22A2E09F97E933DB48BA6EF24C6BE11A5A10024BD9A6A18E662E94BF3C35F257
}

当然,密钥也可以在以后设置:

...
using (var hmacSha256 = new HMACSHA256()) 
{
    hmacSha256.Key = secret;
    byte[] hash = hmacSha256.ComputeHash(payload);
    Console.WriteLine(Convert.ToHexString(hash)); // 22A2E09F97E933DB48BA6EF24C6BE11A5A10024BD9A6A18E662E94BF3C35F257
}
...

Note that HMACSHA256.Create() does not work on .NET 5+ (System.PlatformNotSupportedException: This platform does not allow the automatic selection of an algorithm).
On .NET Framework it works, but returns an HMAC/SHA1 value by default:

...
using (var hmacSha256 = HMACSHA256.Create()) 
{
     hmacSha256.Key = secret;
     byte[] hash = hmacSha256.ComputeHash(payload);
     Console.WriteLine(ByteArrayToString(hash)); // FF81B9A37F9DA6EA0DE2CFF78216B697722E9A08 // for ByteArrayToString() see https://stackoverflow.com/a/311179/9014097
}
...

这可能是您的环境中哈希值较短的原因.为了使用HMAC/SHA 256,必须明确指定:

...
using (var hmacSha256 = HMACSHA256.Create("HMACSHA256"))
{
    hmacSha256.Key = secret;
    byte[] hash = hmacSha256.ComputeHash(payload);
    Console.WriteLine(ByteArrayToString(hash)); // 22A2E09F97E933DB48BA6EF24C6BE11A5A10024BD9A6A18E662E94BF3C35F257 // for ByteArrayToString() see https://stackoverflow.com/a/311179/9014097
}
...

The reason is that HMACSHA256.Create() calls HMAC.Create(), and the latter requires a specification of the digest, otherwise it defaults to HMAC/SHA1, s. here.
Since new HMACSHA256(secret) works in all environments, it is probably most robust to use this (in accordance with the example in the documentation).

Csharp相关问答推荐

在WPF.NET 6中使用C++/WinRT组件(但实际上是任何WinRT组件)

注册通用工厂的C# Dep注入

并行令牌更新

如何使用ConcurentDictionary属性上的属性将自定义System.Text.Json JsonConverter应用于该属性的值?

返回TyedResults.BadRequest<;字符串>;时问题详细信息不起作用

C#-VS2022:全局使用和保存时的代码清理

如何使用自定义负载均衡器管理Ocelot负载均衡器中的多线程和批读取

我如何让我的秒表保持运行场景而不重置

为什么C#认为这个非托管 struct 有一个重叠

在C#中,非静态接口方法的抽象和虚拟是冗余的吗?

为什么Docker中没有转发该端口?

如何在GRPC代码First服务的返回类型上使用多态性?

当try 测试具有协变返回类型的抽象属性时,类似功能引发System.ArgumentException

c#在后台实现类型化数组

如何消除Visual Studio错误,因为它不识别集合表达式的新C#12语法?

在ObservableCollection上使用[NotifyPropertyChangedFor()]源代码生成器不会更新UI

读取测试项目中的应用程序设置

如何对列表<;列表>;使用集合表达式?

CsvHelper在第二次迭代时抛出System.ObjectDisposedException

无法将';无效';转换为';bool';