如何验证 PayPal Webhook 签名?

Posted

技术标签:

【中文标题】如何验证 PayPal Webhook 签名?【英文标题】:How to verify PayPal Webhook signature? 【发布时间】:2020-08-22 09:26:49 【问题描述】:

无法验证从 PayPal 收到的 Webhook。可用的示例不多,并且集成商没有足够的加密知识来完成任务。

遵循的指南是Webhook notifications。

不知道sn-p代码有什么问题,结果总是假的。

调用notifications/verify-webhook-signature端点也会导致错误。

        /// <summary>Verify PayPal Webhook Signature</summary>
        /// <param name="webhookId">0YF54686R9851380C</param>
        /// <param name="transmissionId">785bc690-901d-11ea-8fc5-17f05150a6df</param>
        /// <param name="transmissionTime">2020-05-07T04:44:33Z</param>
        /// <param name="certUrl">https://api.sandbox.paypal.com/v1/notifications/certs/CERT-.....</param>
        /// <param name="transmissionSignature"></param>
        /// <param name="webHookEvent">"\"id\":\"WH-5P8216093E7920426-60J97470GW748563B\",\"create_time\":\"2020-05-07T11:43:48.000Z\",....</param>
        private static void VerifyWebhookSignature(string webhookId, string transmissionId, string transmissionTime,
            string certUrl, string transmissionSignature, string webHookEvent)
        
            Crc32 crc32 = new Crc32();
            String hash = string.Empty;

            var arrayOfBytes = Encoding.UTF8.GetBytes(webHookEvent);
            foreach (byte b in crc32.ComputeHash(arrayOfBytes)) hash += b.ToString("x2").ToLower();

            string expectedSignature = String.Format("0|1|2|3", transmissionId,
                transmissionTime, webhookId, hash);

            string certData = new System.Net.WebClient().DownloadString(certUrl);
            X509Certificate2 cert = new X509Certificate2(Encoding.UTF8.GetBytes(certData));

            try
            
                byte[] signature = Convert.FromBase64String(transmissionSignature);
                byte[] expectedBytes = Encoding.UTF8.GetBytes(expectedSignature);

                using (RSA rsa = cert.GetRSAPublicKey())
                
                    var verified = rsa.VerifyData(
                        expectedBytes,
                        signature,
                        HashAlgorithmName.SHA256,
                        RSASignaturePadding.Pkcs1);

                    Console.WriteLine($"Verified: verified");
                
            
            catch (Exception ex)
            
                throw;
            
        

【问题讨论】:

使用编码 utf-8 instread of ascii。 ASCII 编码会删除不可打印的字符。如果这不起作用,我怀疑 key 的填充模式也可能是一个问题。你遇到了什么错误?请参阅:google.com/… 没有错误,只是没有验证。我相信上面的代码中使用了 UTF-8 编码。我们也将填充切换为 Pss,结果相同。我们用来测试的数据来自一个真实的 Webhook 事件,测试是在 Webhook 被触发时完成的,所以理论上我们应该能够验证它。 如果你使用了无效的密钥,你会得到一个异常。此外,如果您使用了错误的加密模式,您会遇到异常。所以我认为用于加密的密钥不是你用来解密的。确保密钥和数据正确。一个字节,你不会得到正确的结果。 【参考方案1】:

我可以确认您的实现工作正常,但是我不确定您使用哪种实现来计算 CRC32 校验和。当使用Crc32.NET 时,以下 sn-p 就足够了:

var arrayOfBytes = Encoding.UTF8.GetBytes(webHookEvent);
string hash = Crc32Algorithm.Compute(arrayOfBytes).ToString();

感谢您在这里提供它,我也在努力寻找一个可行的例子!

PS:刚刚在这里发现了官方实现:https://github.com/paypal/PayPal-NET-SDK/blob/master/Source/SDK/Api/WebhookEvent.cs#L156

【讨论】:

以上是关于如何验证 PayPal Webhook 签名?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 验证 Paypal webhook 签名

验证模拟 PayPal webhook 的签名总是返回“FAILURE”

node.js 中 PayPal webhook 的签名是啥?

无法执行成功的 Paypal webhook 验证

PayPal + RESTful API + WebHooks + 自签名证书

PayPal Webhook URL 中的 HTTP 基本身份验证