简体   繁体   English

如何验证 PHP 中的 Paypal webhook 签名?

[英]How to verify Paypal webhook signature in PHP?

I'm not very knowledgeable in SSL and certificates.我对 SSL 和证书不是很了解。 I used the post " How to use hash_hmac() with "SHA256withRSA" on PHP? " to see if I can get webhooks with PayPal working.我使用了帖子“ 如何在 PHP 上使用带有“SHA256withRSA”的 hash_hmac()? ”来查看我是否可以在 PayPal 工作的情况下获得 webhook。

The issue I am have is I am getting the following error after calling openssl_verify() and a return result of (0):我遇到的问题是在调用openssl_verify()和返回结果 (0) 后出现以下错误:

OpenSSL error openssl_verify error:04091068:rsa routines:INT_RSA_VERIFY:bad signature OpenSSL 错误 openssl_verify error:04091068:rsa routines:INT_RSA_VERIFY:bad signature

I've tried to solve this, but documentation on errors and the functions around the web is minimal to none.我试图解决这个问题,但是关于 web 周围的错误和功能的文档很少甚至没有。

My current code looks like this:我当前的代码如下所示:

 // get the header post to my php file by PayPal
 $headers = apache_request_headers();
 // get the body post to me php file by PayPal
 $body = @file_get_contents('php://input');
 $json = json_decode($body);

 // TransmissionId|TransmissionTimeStamp|WebhookId|CRC32 as per PayPal documentation
 $sigString = $headers['Paypal-Transmission-Id'].'|'.$headers['Paypal-Transmission-Time'].'|'.$json->id.'|'.crc32($body);

 // $headers['Paypal-Cert-Url'] contains the "-----BEGIN CERTIFICATE---MIIHmjCCBoKgAwIBAgIQDB8 ... -----END CERTIFICATE-----"
 $pubKey = openssl_pkey_get_public(file_get_contents($headers['Paypal-Cert-Url']));

 // and this is the call to verify that returns result (0)
 $verifyResult = openssl_verify($sigString, base64_decode($headers['Paypal-Transmission-Sig']), $pubKey, 'sha256WithRSAEncryption');

Only different from the reference code I used, is that I do not use openssl_pkey_get_details($pubKey) because I will get below error in addition to the existing signature error:与我使用的参考代码唯一不同的是,我不使用openssl_pkey_get_details($pubKey) ,因为除了现有的签名错误之外,我还会遇到以下错误:

OpenSSL error openssl_verify error:0906D06C:PEM routines:PEM_read_bio:no start line OpenSSL error openssl_verify error:04091068:rsa routines:INT_RSA_VERIFY:bad signature OpenSSL 错误 openssl_verify 错误:0906D06C:PEM 例程:PEM_read_bio:没有起始行 OpenSSL 错误 openssl_verify 错误:04091068:rsa 例程:INT_RSA_VERIFY:bad signature

Also I've tried a variation by not using base64_decode() on the header but that would get the same return result (0) with error stating:此外,我尝试了一种变体,即在 header 上不使用base64_decode() ,但这会得到相同的返回结果 (0),但会出现错误说明:

OpenSSL error openssl_verify error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length OpenSSL 错误 openssl_verify error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length

What is wrong with the signature?签名有什么问题?

This may not be exactly what you were looking for, but an alternative to manually validating the signature with Open SSL could be to use the PayPal PHP Restful API.这可能不是您想要的,但是使用 Open SSL 手动验证签名的替代方法可能是使用 PayPal PHP Restful API。

The PayPal Restful API exposes an endpoint that allows you to validate webhook: /v1/notifications/verify-webhook-signature PayPal Restful API 公开了一个端点,允许您验证 webhook: /v1/notifications/verify-webhook-signature

The PayPal-PHP-SDK provides a VerifyWebhookSignature class that make it easy to make calls to that end point. PayPal-PHP-SDK提供了一个VerifyWebhookSignature类,可以轻松调用该端点。

They also have a Sample Script illustrating how to use VerifyWebhookSignature class.他们还有一个示例脚本,说明如何使用VerifyWebhookSignature类。

The formula is <transmissionId>|<timeStamp>|<webhookId>|<crc32> not <transmissionId>|<timeStamp>|<eventId>|<crc32> .公式是<transmissionId>|<timeStamp>|<webhookId>|<crc32>而不是<transmissionId>|<timeStamp>|<eventId>|<crc32> Also note that Webhook simulator events can't be verified.另请注意,无法验证 Webhook 模拟器事件。

As @JUBEI mentioned, you need to get the WEBHOOK_ID from your PayPal account and NOT from the headers you've received, remember the first time you've registered the webhook event, you must find your webhook ID right there.正如@JUBEI 提到的,您需要从您的 PayPal 帐户中获取 WEBHOOK_ID,而不是从您收到的标头中获取,请记住您第一次注册 webhook 事件时,您必须在此处找到您的 webhook ID。

Plus, make sure to use OPENSSL_ALGO_SHA256 instead of: 'sha256WithRSAEncryption' , refer to: https://www.php.net/manual/en/openssl.signature-algos.php另外,请确保使用OPENSSL_ALGO_SHA256而不是: 'sha256WithRSAEncryption' ,请参阅: https://www.php.net/manual/en/openssl.signature-algos.php

在此处输入图像描述

You may want to use this piece of code:你可能想使用这段代码:

$pubKey = openssl_pkey_get_public(file_get_contents($headers['PAYPAL-CERT-URL']));
$details = openssl_pkey_get_details($pubKey);

$verifyResult = openssl_verify($sigString, base64_decode($headers['PAYPAL-TRANSMISSION-SIG']), $details['key'], 'sha256WithRSAEncryption');

if ($verifyResult === 0) {
    throw new Exception('signature incorrect');
} elseif ($verifyResult === -1) {
    throw new Exception('error checking signature');
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM