如何在PHP中验证Paypal Webhook签名?

7
我对SSL和证书不是很了解。我使用这篇文章“如何在PHP中使用hash_hmac()和"SHA256withRSA"?”来看看是否可以让PayPal的Webhooks工作。
我遇到的问题是,在调用openssl_verify()后,返回结果为(0),并出现以下错误:

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

我尝试解决这个问题,但是网上关于这些错误和函数的文档非常少。
我的当前代码如下:
 // 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');

与我使用的参考代码不同的是,我没有使用openssl_pkey_get_details($pubKey),因为除了现有的签名错误之外,我还会收到以下错误消息:

OpenSSL错误openssl_verify error:0906D06C:PEM routines:PEM_read_bio:no start line OpenSSL错误openssl_verify error:04091068:rsa routines:INT_RSA_VERIFY:bad signature

此外,我尝试了一种变化,即不在标题上使用base64_decode(),但将返回相同的结果(0),并显示以下错误消息:

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

签名哪里出了问题?

我认为你不应该使用$json->id,因为这是事件的ID,而不是Web钩子的ID。Web钩子ID不会随事件一起发送 - 你必须在此处使用一个常量。 - IVO GELOV
4个回答

2
您可能需要使用以下代码:

你可以使用这段代码:

$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');
}

我猜@Maxime Rainville提出的使用官方SDK的解决方案可能更好一些。尽管这样你需要再进行一次HTTP调用。 - t1gor

2
公式应为<transmissionId>|<timeStamp>|<webhookId>|<crc32>,而不是<transmissionId>|<timeStamp>|<eventId>|<crc32>。同时请注意,Webhook模拟器事件无法验证。

0

这可能不是您正在寻找的准确解决方案,但手动使用Open SSL验证签名的替代方法可能是使用PayPal PHP Restful API。

PayPal Restful API公开了一个端点,允许您验证Webhook:/v1/notifications/verify-webhook-signature

PayPal-PHP-SDK提供了一个VerifyWebhookSignature类,使调用该端点变得容易。

他们还有一个示例脚本,说明如何使用VerifyWebhookSignature类。


0

正如@JUBEI所提到的,您需要从PayPal帐户中获取WEBHOOK_ID,而不是从您收到的标头中获取,记住第一次注册Webhook事件时,您必须在那里找到您的Webhook ID。

此外,请确保使用OPENSSL_ALGO_SHA256而不是:'sha256WithRSAEncryption',请参阅:https://www.php.net/manual/en/openssl.signature-algos.php

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接