使用 java Event.validateReceivedEvent 验证 webhook 始终无法通过签名验证

Posted

技术标签:

【中文标题】使用 java Event.validateReceivedEvent 验证 webhook 始终无法通过签名验证【英文标题】:validate webhook using java Event.validateReceivedEvent always fails signature validation 【发布时间】:2016-06-16 01:11:04 【问题描述】:

我在我的网站中准备了一个 servlet,以接收来自 PayPal webhook 的通知。 servlet 的开发版本记录了 http 标头和正文。这是一个示例的屏幕截图:

我创建了一个显示问题的“独立测试应用程序”。

package com.rsws.renew;

import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;

import com.paypal.api.payments.Event;
import com.paypal.base.Constants;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.base.rest.PayPalResource;

/**
 * @author Ignacio
 *
 */
public class TestWebHook 

 public static void main(String[] argv) 

    try 
        InputStream is = InvoicePaid.class 
                .getResourceAsStream("/sdk_config.properties"); 
        try  
            PayPalResource.initConfig(is); 
         catch (PayPalRESTException e)  
            e.printStackTrace();
         

        APIContext apiContext = new APIContext();
        Map<String, String> map = new HashMap<>(PayPalResource.getConfigurations());
        apiContext.setConfigurationMap(map);

        Map<String,String> headers = new HashMap<String,String>();

        // this is the data provided by PayPal sandbox
        map.put(Constants.PAYPAL_WEBHOOK_ID, "3W2725225F637605K");
        String payload = "\"id\":\"WH-0T490472X6099635W-7LJ29748BW389372K\",\"create_time\":\"2015-09-25T23:14:14Z\",\"resource_type\":\"invoices\",\"event_type\":\"INVOICING.INVOICE.PAID\",\"summary\":\"An invoice was created\",\"resource\":\"id\":\"INV2-8FSD-3HT6-BRHR-UHYV\",\"number\":\"MM00063\",\"status\":\"PAID\",\"merchant_info\":\"email\":\"example@outlook.com\",\"first_name\":\"Dennis\",\"last_name\":\"Doctor\",\"business_name\":\"Medical Professional LLC\",\"address\":\"line1\":\"1234 Main St\",\"line2\":\"Apt 302\",\"city\":\"Portland\",\"state\":\"OR\",\"postal_code\":\"97217\",\"country_code\":\"US\",\"billing_info\":[\"email\":\"example@example.com\",\"business_name\":\"Medical Professionals LLC\",\"language\":\"en_US\"],\"items\":[\"name\":\"Sample Item\",\"quantity\":1,\"unit_price\":\"currency\":\"USD\",\"value\":\"1.00\",\"unit_of_measure\":\"QUANTITY\"],\"invoice_date\":\"2015-09-28 PDT\",\"payment_term\":\"term_type\":\"DUE_ON_RECEIPT\",\"due_date\":\"2015-09-28 PDT\",\"tax_calculated_after_discount\":true,\"tax_inclusive\":false,\"total_amount\":\"currency\":\"USD\",\"value\":\"1.00\",\"payments\":[\"type\":\"PAYPAL\",\"transaction_id\":\"22592127VV907111U\",\"transaction_type\":\"SALE\",\"method\":\"PAYPAL\",\"date\":\"2015-09-28 14:37:13 PDT\"],\"metadata\":\"created_date\":\"2015-09-28 14:35:46 PDT\",\"last_updated_date\":\"2015-09-28 14:37:13 PDT\",\"first_sent_date\":\"2015-09-28 14:35:47 PDT\",\"last_sent_date\":\"2015-09-28 14:35:47 PDT\",\"paid_amount\":\"paypal\":\"currency\":\"USD\",\"value\":\"1.00\",\"links\":[\"rel\":\"self\",\"href\":\"https://api.paypal.com/v1/invoicing/invoices/INV2-8FSD-3HT6-BRHR-UHYV\",\"method\":\"GET\"],\"links\":[\"href\":\"https://api.paypal.com/v1/notifications/webhooks-events/WH-0T490472X6099635W-7LJ29748BW389372K\",\"rel\":\"self\",\"method\":\"GET\",\"href\":\"https://api.paypal.com/v1/notifications/webhooks-events/WH-0T490472X6099635W-7LJ29748BW389372K/resend\",\"rel\":\"resend\",\"method\":\"POST\"]";
        headers.put("PAYPAL-CERT-URL", "https://api.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-df8cd2d5");
        headers.put("PAYPAL-TRANSMISSION-ID", "464163d0-e0ae-11e5-af72-51ae350aaff1");
        headers.put("PAYPAL-TRANSMISSION-TIME", "2016-03-02T19:38:01Z");
        headers.put("PAYPAL-AUTH-ALGO", "SHA256withRSA");
        headers.put("PAYPAL-TRANSMISSION-SIG", "S3AjY87GLp1MP/UsGAWPoEes+laa7xbV4X7pMi9PdC0QR7MoNC/L/O2UThAh1IBzDZ5DGXvkEDvXK9fF0IfoS2QtLJUBm5+UFoo1jJMlH+QCiJUEHSuio2UrFGbxoqaIPcA1PN0tmd5FwikDRPCnpht6pvMvCZV1FEQbBMr9ld3d3XoWBKeWQG+oxAWSTNYJiKQIrM6l/8+hKVQ1LZID8dtR3c7y6eFxNFsDQ3WgwChZZ15vpyhDWQ4t08m3PsWFyjvsQmNRyXQyUeAC8xw96sBwGmHsgwKJwbAamVrWicQqQ/tXuUcqx9Y0pg3P4LuGNPFKzktq9L3ZImTEJxpRLA==");
        // this shows invalid
        System.out.println(Event.validateReceivedEvent(apiContext, headers, payload) ? "valid" : "invalid");

        // this is the data provided in the sdk examples https://github.com/paypal/PayPal-Java-SDK/blob/master/rest-api-sdk/src/test/java/com/paypal/base/ValidateCertTest.java
        map.put(Constants.PAYPAL_WEBHOOK_ID, "3RN13029J36659323");
        payload = "\"id\":\"WH-2W7266712B616591M-36507203HX6402335\",\"create_time\":\"2015-05-12T18:14:14Z\",\"resource_type\":\"sale\",\"event_type\":\"PAYMENT.SALE.COMPLETED\",\"summary\":\"Payment completed for $ 20.0 USD\",\"resource\":\"id\":\"7DW85331GX749735N\",\"create_time\":\"2015-05-12T18:13:18Z\",\"update_time\":\"2015-05-12T18:13:36Z\",\"amount\":\"total\":\"20.00\",\"currency\":\"USD\",\"payment_mode\":\"INSTANT_TRANSFER\",\"state\":\"completed\",\"protection_eligibility\":\"ELIGIBLE\",\"protection_eligibility_type\":\"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE\",\"parent_payment\":\"PAY-1A142943SV880364LKVJEFPQ\",\"transaction_fee\":\"value\":\"0.88\",\"currency\":\"USD\",\"links\":[\"href\":\"https://api.sandbox.paypal.com/v1/payments/sale/7DW85331GX749735N\",\"rel\":\"self\",\"method\":\"GET\",\"href\":\"https://api.sandbox.paypal.com/v1/payments/sale/7DW85331GX749735N/refund\",\"rel\":\"refund\",\"method\":\"POST\",\"href\":\"https://api.sandbox.paypal.com/v1/payments/payment/PAY-1A142943SV880364LKVJEFPQ\",\"rel\":\"parent_payment\",\"method\":\"GET\"],\"links\":[\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2W7266712B616591M-36507203HX6402335\",\"rel\":\"self\",\"method\":\"GET\",\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2W7266712B616591M-36507203HX6402335/resend\",\"rel\":\"resend\",\"method\":\"POST\"]";         
        headers.put("PAYPAL-CERT-URL", "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77");
        headers.put("PAYPAL-TRANSMISSION-ID", "b2384410-f8d2-11e4-8bf3-77339302725b");
        headers.put("PAYPAL-TRANSMISSION-TIME", "2015-05-12T18:14:14Z");
        headers.put("PAYPAL-AUTH-ALGO", "SHA256withRSA");
        headers.put("PAYPAL-TRANSMISSION-SIG", "vSOIQFIZQHv8G2vpbOpD/4fSC4/MYhdHyv+AmgJyeJQq6q5avWyHIe/zL6qO5hle192HSqKbYveLoFXGJun2od2zXN3Q45VBXwdX3woXYGaNq532flAtiYin+tQ/0pNwRDsVIufCxa3a8HskaXy+YEfXNnwCSL287esD3HgOHmuAs0mYKQdbR4e8Evk8XOOQaZzGeV7GNXXz19gzzvyHbsbHmDz5VoRl9so5OoHqvnc5RtgjZfG8KA9lXh2MTPSbtdTLQb9ikKYnOGM+FasFMxk5stJisgmxaefpO9Q1qm3rCjaJ29aAOyDNr3Q7WkeN3w4bSXtFMwyRBOF28pJg9g==");         
        // this shows valid
        System.out.println(Event.validateReceivedEvent(apiContext, headers, payload) ? "valid" : "invalid");

     catch (InvalidKeyException e) 
        e.printStackTrace();
     catch (NoSuchAlgorithmException e) 
        e.printStackTrace();
     catch (SignatureException e) 
        e.printStackTrace();
     catch (PayPalRESTException e) 
        e.printStackTrace();
    


当数据来自示例时,代码显示valid,当数据来自paypal网站时,代码显示invalid

我想知道为什么这无法验证。欢迎任何帮助。

【问题讨论】:

我在github.com/paypal/PayPal-Java-SDK/issues/157 向 PayPal-Java-SDK 添加了一个问题,因为developer.paypal.com/docs/integration/direct/… 此处的文档很好地说明了如何计算签名的种子,我可以一步一步进行我的应用程序,直到它也被计算出来。 (是这个)464163d0-e0ae-11e5-af72-51ae350aaff1|2016-03-02T19:38:01Z|3W2725225F637605K|3555709430 但我在 webhook 模拟器中提供的信息中看不到相同的种子,所以我真的不能检查发生了什么 【参考方案1】:

您可能希望使用实际的沙盒事务和 webhook 事件来测试验证。模拟器模拟数据可能不会使用沙盒算法进行更新,建议用于测试脚本的 URL 可访问性。

【讨论】:

以上是关于使用 java Event.validateReceivedEvent 验证 webhook 始终无法通过签名验证的主要内容,如果未能解决你的问题,请参考以下文章

使用Java代码和注解完成Spring配置

我应该采用 Java 12 还是坚持使用 Java 11?

java语言Class类的作用是啥以及怎么使用?

怎么使用JAVA中的包

什么是java控件?怎样使用java控件?

使用java语言,如何对一个类中的静态方法做切面编程?