From 871702ff0ca505d9ae4973884710f442cd7d4a4e Mon Sep 17 00:00:00 2001
From: Osiris Team <59899645+Osiris-Team@users.noreply.github.com>
Date: Sat, 20 Mar 2021 11:22:56 +0100
Subject: [PATCH] 1.5
---
pom.xml | 2 +-
src/main/java/com/osiris/payhook/PayHook.java | 15 ++++++------
.../osiris/payhook/WebhookEventHeader.java | 4 ++--
.../com/osiris/payhook/paypal/SSLUtil.java | 23 ++++++++++---------
4 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/pom.xml b/pom.xml
index a3192f1..4985aac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.osiris.payhook
PayHook
- 1.4
+ 1.5
jar
PayHook
diff --git a/src/main/java/com/osiris/payhook/PayHook.java b/src/main/java/com/osiris/payhook/PayHook.java
index e3e2042..0eafa04 100644
--- a/src/main/java/com/osiris/payhook/PayHook.java
+++ b/src/main/java/com/osiris/payhook/PayHook.java
@@ -80,14 +80,14 @@ public String validateAndGet(Map map, String key) throws ParseHe
}
/**
- * See {@link #validateWebHookEvent(WebhookEvent)} (WebHookEvent)} for details.
+ * See {@link #validateWebhookEvent(WebhookEvent)} (WebHookEvent)} for details.
* @param validId your webhooks valid id. Get it from here: https://developer.paypal.com/developer/applications/
* @param validTypes your webhooks valid types/names. Here is a full list: https://developer.paypal.com/docs/api-basics/notifications/webhooks/event-names/
* @param header the http messages header as string.
* @param body the http messages body as string.
*/
- public void validateWebHookEvent(String validId, List validTypes, WebhookEventHeader header, String body) throws ParseBodyException, WebHookValidationException, CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, SignatureException, InvalidKeyException {
- validateWebHookEvent(new WebhookEvent(validId, validTypes, header, body));
+ public void validateWebhookEvent(String validId, List validTypes, WebhookEventHeader header, String body) throws ParseBodyException, WebHookValidationException, CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, SignatureException, InvalidKeyException {
+ validateWebhookEvent(new WebhookEvent(validId, validTypes, header, body));
}
/**
@@ -102,7 +102,7 @@ public void validateWebHookEvent(String validId, List validTypes, Webhoo
* @throws WebHookValidationException if validation failed. IMPORTANT: MESSAGE MAY CONTAIN SENSITIVE INFORMATION!
* @throws ParseBodyException
*/
- public void validateWebHookEvent(WebhookEvent event) throws WebHookValidationException, ParseBodyException, IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ public void validateWebhookEvent(WebhookEvent event) throws WebHookValidationException, ParseBodyException, IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
WebhookEventHeader header = event.getHeader();
// Check if the webhook types match
@@ -149,8 +149,9 @@ public void validateWebHookEvent(WebhookEvent event) throws WebHookValidationExc
String expectedSignature = String.format("%s|%s|%s|%s", transmissionId, transmissionTime, validWebhookId, SSLUtil.crc32(bodyString));
// Compare the encoded signature with the expected signature
- String decodedSignature = SSLUtil.validateAndGetSignatureAsString(clientCerts, authAlgo, actualSignatureEncoded, expectedSignature);
- if (decodedSignature!=null){
+ String decodedSignature = SSLUtil.decodeTransmissionSignature(actualSignatureEncoded);
+ boolean isSigValid = SSLUtil.validateTransmissionSignature(clientCerts, authAlgo, actualSignatureEncoded, expectedSignature);
+ if (isSigValid){
String[] arrayDecodedSignature = decodedSignature.split("\\|"); // Split by | char
header.setWebhookId(arrayDecodedSignature[2]);
header.setCrc32(arrayDecodedSignature[3]);
@@ -160,7 +161,7 @@ public void validateWebHookEvent(WebhookEvent event) throws WebHookValidationExc
throw new WebHookValidationException("Provided webhook id("+header.getWebhookId()+") does not match the valid id("+event.getValidWebHookId()+")!");
}
else
- throw new WebHookValidationException("Transmission signature is not valid!");
+ throw new WebHookValidationException("Transmission signature is not valid! Expected: '"+expectedSignature+"' Provided: '"+decodedSignature+"'");
}
/**
diff --git a/src/main/java/com/osiris/payhook/WebhookEventHeader.java b/src/main/java/com/osiris/payhook/WebhookEventHeader.java
index f098242..2c79f93 100644
--- a/src/main/java/com/osiris/payhook/WebhookEventHeader.java
+++ b/src/main/java/com/osiris/payhook/WebhookEventHeader.java
@@ -37,7 +37,7 @@ public String getTimestamp() {
/**
* IMPORTANT: SINCE THE WEBHOOK ID IS INSIDE THE ENCRYPTED TRANSMISSION SIGNATURE, THIS RETURNS NULL
- * UNLESS YOU SUCCESSFULLY EXECUTED {@link PayHook#validateWebHookEvent(WebhookEvent)} ONCE BEFORE!
+ * UNLESS YOU SUCCESSFULLY EXECUTED {@link PayHook#validateWebhookEvent(WebhookEvent)} ONCE BEFORE!
* The ID of the webhook resource for the destination URL to which PayPal delivers the event notification.
*/
public String getWebhookId() {
@@ -50,7 +50,7 @@ public void setWebhookId(String webhookId) {
/**
* IMPORTANT: SINCE THE CRC32 IS INSIDE THE ENCRYPTED TRANSMISSION SIGNATURE, THIS RETURNS NULL
- * UNLESS YOU SUCCESSFULLY EXECUTED {@link PayHook#validateWebHookEvent(WebhookEvent)} ONCE BEFORE!
+ * UNLESS YOU SUCCESSFULLY EXECUTED {@link PayHook#validateWebhookEvent(WebhookEvent)} ONCE BEFORE!
* The Cyclic Redundancy Check (CRC32) checksum for the body of the HTTP payload.
*/
public String getCrc32() {
diff --git a/src/main/java/com/osiris/payhook/paypal/SSLUtil.java b/src/main/java/com/osiris/payhook/paypal/SSLUtil.java
index 28d542d..1ebecda 100644
--- a/src/main/java/com/osiris/payhook/paypal/SSLUtil.java
+++ b/src/main/java/com/osiris/payhook/paypal/SSLUtil.java
@@ -149,27 +149,28 @@ public static long crc32(String data) {
*
* @param clientCerts Client Certificates
* @param algo Algorithm used for signature creation by server
- * @param actualSignatureEncoded Paypal-Transmission-Sig header value passed by server
- * @param expectedSignature Signature generated by formatting data with CRC32 value of request body
- * @return the decoded signature as string if it is valid, null otherwise
+ * @param actualEncodedSignature Paypal-Transmission-Sig header value passed by server
+ * @param expectedDecodedSignature Signature generated by formatting data with CRC32 value of request body
+ * @return Returns true if signature is valid
*
* @throws NoSuchAlgorithmException
* @throws SignatureException
* @throws InvalidKeyException
*/
- public static String validateAndGetSignatureAsString(Collection clientCerts, String algo,
- String actualSignatureEncoded, String expectedSignature) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ public static boolean validateTransmissionSignature(Collection clientCerts, String algo,
+ String actualEncodedSignature, String expectedDecodedSignature) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
// Get the signatureAlgorithm from the PAYPAL-AUTH-ALGO HTTP header
Signature signatureAlgorithm = Signature.getInstance(algo);
// Get the certData from the URL provided in the HTTP headers and cache it
X509Certificate[] clientChain = clientCerts.toArray(new X509Certificate[0]);
signatureAlgorithm.initVerify(clientChain[0].getPublicKey());
- signatureAlgorithm.update(expectedSignature.getBytes());
+ signatureAlgorithm.update(expectedDecodedSignature.getBytes());
// Actual signature is base 64 encoded and available in the HTTP headers
- byte[] actualSignature = Base64.decodeBase64(actualSignatureEncoded.getBytes());
- if (signatureAlgorithm.verify(actualSignature))
- return new String(actualSignature);
- else
- return null;
+ byte[] actualSignature = Base64.decodeBase64(actualEncodedSignature.getBytes());
+ return signatureAlgorithm.verify(actualSignature);
+ }
+
+ public static String decodeTransmissionSignature(String encodedSignature){
+ return new String(Base64.decodeBase64(encodedSignature.getBytes()));
}
}