[英]Verify Paypal Restful Webhook Signature in Java
I have a Java servlet that consumes Paypal Restful Webhooks.我有一个使用 Paypal Restful Webhooks 的 Java servlet。 In verifying the request signature, I followed the instructions detailed in在验证请求签名时,我遵循了详细说明
https://developer.paypal.com/webapps/developer/docs/integration/direct/rest-webhooks-overview/#event-security https://developer.paypal.com/webapps/developer/docs/integration/direct/rest-webhooks-overview/#event-security
However, I can't seem to successfully verify the signature even while following the pseudocode in the paypal doc to the dot.但是,即使按照贝宝文档中的伪代码进行操作,我似乎也无法成功验证签名。 Here's the actual code that I use to verify (the below method always returns false):这是我用来验证的实际代码(下面的方法总是返回 false):
private static final String WEBHOOK_ID = "4HL82785RC0XXXXXX";
private boolean isValidRequest(HttpServletRequest req, String payload) throws Exception {
String transmissionId = req.getHeader("PAYPAL-TRANSMISSION-ID");
String timeStamp = req.getHeader("PAYPAL-TRANSMISSION-TIME");
String crc32 = getCrcSum(payload);
String expectedSignature = String.format("%s|%s|%s|%s", transmissionId, timeStamp, WEBHOOK_ID, crc32);
System.out.println("EXPECTED SIG:" + expectedSignature);
String actualSignatureEncoded = req.getHeader("PAYPAL-TRANSMISSION-SIG");
String certUrl = req.getHeader("PAYPAL-CERT-URL");
String algo = req.getHeader("PAYPAL-AUTH-ALGO");
Signature shaWithRsa = Signature.getInstance(algo);
byte[] certData = HttpUtils.getBytes(new URL(certUrl), null);
Certificate certificate = X509Certificate.getInstance(certData);
shaWithRsa.initVerify(certificate.getPublicKey());
shaWithRsa.update(expectedSignature.getBytes());
byte[] actualSignature = Base64.decodeBase64(actualSignatureEncoded.getBytes());
return shaWithRsa.verify(actualSignature);
}
private static String getCrcSum(final String body) {
byte[] bytes = body.getBytes();
CRC32 checkSum = new CRC32();
checkSum.update(bytes, 0, bytes.length);
return String.valueOf(checkSum.getValue());
//return Long.toHexString(checkSum.getValue());
}
HttpUtils.getBytes(new URL(certUrl), null); HttpUtils.getBytes(new URL(certUrl), null); is just a helper method for retrieving the results of a GET request.只是用于检索 GET 请求结果的辅助方法。 It returns a valid Certificate.它返回一个有效的证书。
Probable culprits that I can think of are: 1. Computation of CRC32 is somehow different from how Paypal computes it on their end.我能想到的可能的罪魁祸首是: 1. CRC32 的计算与 Paypal 最终计算它的方式有些不同。 2. Public key from the Paypal URL does not match the Private key used by Paypal. 2. Paypal URL 中的公钥与 Paypal 使用的私钥不匹配。
Here is how I get the payload from the servlet request:以下是我从 servlet 请求中获取有效负载的方法:
String payload = getString(req.getInputStream());
private static String getString(InputStream is) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
I use https://developer.paypal.com/developer/webhooksSimulator for testing.我使用https://developer.paypal.com/developer/webhooksSimulator进行测试。
It turns out that the code above is working properly.事实证明,上面的代码工作正常。 The issue was how to properly test it.问题是如何正确测试它。
Paypal's webhook simulator in https://developer.paypal.com/developer/webhooksSimulator is only meant for connectivity tests. https://developer.paypal.com/developer/webhooksSimulator 中的Paypal 网络钩子模拟器仅用于连接测试。
The webhook simulation API detailed in https://developer.paypal.com/webapps/developer/docs/api/#simulate-a-webhook-event will work for verification of the signature, but there is a catch that was NOT documented AFAIK. https://developer.paypal.com/webapps/developer/docs/api/#simulate-a-webhook-event 中详述的 webhook 模拟 API 将用于验证签名,但有一个未记录在案的问题 AFAIK . The simulate API accepts either the webhook_id or the webhook url as a parameter (or both).模拟 API 接受 webhook_id 或 webhook url 作为参数(或两者)。 As per my tests, if you only specify the url, the webhook does not verify properly.根据我的测试,如果您只指定 url,则 webhook 无法正确验证。 But if you specify the webhook_id, the webhook verification process works accordingly.但是,如果您指定 webhook_id,则 webhook 验证过程会相应地工作。
Unfortunately for me, I have been testing using the simulate API while only specifying the url.对我来说不幸的是,我一直在使用模拟 API 进行测试,而只指定了 url。 Thanks to @wpohl for giving me the idea to use the webhook_id in the simulate API.感谢@wpohl 让我想到在模拟 API 中使用 webhook_id。
The documentation stated "Webhook ID: This is the ID of the webhook resource for the destination URL on which the event is delivered".文档说明“Webhook ID:这是传递事件的目标 URL 的 Webhook 资源的 ID”。 So it is not the id of the event (in my case: "WH-36432655JG839693T-2LC486465H4712400").所以它不是事件的 id(在我的例子中:“WH-36432655JG839693T-2LC486465H4712400”)。 But if you use the webhook simulator you didn't have a persistent "webhook resource".但是如果你使用 webhook 模拟器,你就没有持久的“webhook 资源”。
I created an webhook and triggered a notification.我创建了一个 webhook 并触发了一个通知。 and with that ID (In my case "3EB298650W722680T") it works.并使用该 ID(在我的情况下为“3EB298650W722680T”)它可以工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.