我有一个使用Paypal Restful Webhooks的Java servlet。在验证请求签名时,我按照
中的详细说明进行操作但是,即使按照paypal文档中的伪代码到达点,我似乎也无法成功验证签名。这是我用来验证的实际代码(以下方法总是返回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);只是一个帮助方法,用于检索GET请求的结果。它返回一个有效的证书。
我能想到的可能罪魁祸首是: 1.计算CRC32在某种程度上与Paypal在其结束时计算它的方式不同。 2. Paypal URL中的公钥与Paypal使用的私钥不匹配。
以下是我从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();
}
我使用https://developer.paypal.com/developer/webhooksSimulator进行测试。
答案 0 :(得分:3)
事实证明上面的代码工作正常。问题是如何正确测试它。
https://developer.paypal.com/developer/webhooksSimulator中的Paypal的webhook模拟器仅用于连接测试。
https://developer.paypal.com/webapps/developer/docs/api/#simulate-a-webhook-event中详细介绍的webhook模拟API可用于验证签名,但有一个未记录的文件AFAIK。模拟API接受webhook_id或webhook url作为参数(或两者)。根据我的测试,如果您只指定了网址,则网络链接无法正确验证。但是,如果您指定webhook_id,则webhook验证过程会相应地运行。
不幸的是,我一直在使用模拟API进行测试,只指定了网址。感谢@wpohl让我想到在模拟API中使用webhook_id。
答案 1 :(得分:1)
所述文档" Webhook ID:这是传递事件的目标URL的webhook资源的ID"。因此,它不是事件的ID(在我的情况下:" WH-36432655JG839693T-2LC486465H4712400")。但是如果你使用webhook模拟器,你就没有持久的#h; webhook资源"。
我创建了一个webhook并触发了通知。并且使用该ID(在我的情况下" 3EB298650W722680T")它可以工作。