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

时间:2016-02-07 18:36:31

标签: php ssl paypal openssl webhooks

我对SSL和证书知之甚少。我用过这个帖子 " How to use hash_hmac() with "SHA256withRSA" on PHP?"看看我是否可以通过PayPal工作来获取webhook。

我遇到的问题是,在调用openssl_verify()并返回结果为(0)后,我收到以下错误:

  

OpenSSL错误openssl_verify错误:04091068:rsa例程:INT_RSA_VERIFY:错误签名

我试图解决此问题,但有关错误和网络功能的文档几乎没有。

我目前的代码如下:

 // 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错误:0906D06C:PEM例程:PEM_read_bio:无起始行   OpenSSL错误openssl_verify错误:04091068:rsa例程:INT_RSA_VERIFY:错误签名

此外,我通过在标题上不使用base64_decode()尝试了变体,但是会得到相同的返回结果(0)并显示错误:

  

OpenSSL错误openssl_verify错误:04091077:rsa例程:INT_RSA_VERIFY:签名长度错误

签名有什么问题?

3 个答案:

答案 0 :(得分:1)

这可能不是您想要的,但使用Open SSL手动验证签名的替代方法可能是使用PayPal PHP Restful API。

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

PayPal-PHP-SDK提供了一个VerifyWebhookSignature类,可以轻松调用该终点。

他们还有Sample Script说明如何使用VerifyWebhookSignature类。

答案 1 :(得分:0)

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

答案 2 :(得分:-1)

您可能想要使用这段代码:

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