diff --git a/pom.xml b/pom.xml
index cdec1f160..75d692e91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,16 +23,45 @@
+
11
11
-
UTF-8
UTF-8
- 1.6.16
adyen
https://sonarcloud.io
+
+
+
+ 2.21.0
+ 2.11.0
+ 1.18.0
+ 1.9.0
+ 1.6.16
+ 2.2.30
+ 3.1.0
+ 2.3.1
+ 5.5
+
+
+ 3.16.4
+ 3.0
5.11.4
5.21.0
+
+
+ 3.14.1
+ 3.5.0
+ 0.8.14
+ 3.3.1
+ 3.12.0
+ 3.2.8
+ 0.7.0
+ 3.6.0
+ 5.1.9
+ 3.2.1
+ 3.5.2
+ 1.34.1
scm:git:git@github.com:Adyen/adyen-java-api-library.git
@@ -44,34 +73,22 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.14.1
+ ${maven-compiler-plugin-version}
org.apache.maven.plugins
maven-jar-plugin
- 3.5.0
+ ${maven-jar-plugin-version}
${project.build.outputDirectory}/META-INF/MANIFEST.MF
-
- org.eluder.coveralls
- coveralls-maven-plugin
- 4.3.0
-
-
- javax.xml.bind
- jaxb-api
- 2.3.1
-
-
-
org.jacoco
jacoco-maven-plugin
- 0.8.14
+ ${jacoco-maven-plugin-version}
prepare-agent
@@ -95,7 +112,7 @@
org.apache.maven.plugins
maven-source-plugin
- 3.3.1
+ ${maven-source-plugin-version}
attach-sources
@@ -108,7 +125,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 3.12.0
+ ${maven-javadoc-plugin-version}
attach-javadocs
@@ -121,7 +138,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 3.2.8
+ ${maven-gpg-plugin-version}
sign-artifacts
@@ -136,7 +153,7 @@
org.sonatype.central
central-publishing-maven-plugin
- 0.7.0
+ ${central-publishing-maven-plugin-version}
true
central
@@ -149,7 +166,7 @@
org.apache.maven.plugins
maven-checkstyle-plugin
- 3.6.0
+ ${maven-checkstyle-plugin-version}
checkstyle.xml
checkstyle-suppressions.xml
@@ -169,7 +186,7 @@
org.apache.felix
maven-bundle-plugin
- 5.1.9
+ ${maven-bundle-plugin-version}
bundle-manifest
@@ -189,7 +206,7 @@
com.diffplug.spotless
spotless-maven-plugin
- 3.2.1
+ ${spotless-maven-plugin-version}
@@ -204,11 +221,11 @@
src/test/java/**/*.java
- 1.34.1
+ ${google-java-format-version}
true
-
+
google-java-format
@@ -218,100 +235,94 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.5.2
+ ${maven-surefire-plugin-version}
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
- 2.21.0
-
+
com.fasterxml.jackson.core
jackson-databind
- 2.21.0
+ ${com.fasterxml.jackson-version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${com.fasterxml.jackson-version}
com.google.code.gson
gson
- 2.11.0
- compile
+ ${gson-version}
- org.apache.httpcomponents.client5
- httpclient5
- 5.5
- compile
+ commons-codec
+ commons-codec
+ ${commons-codec-version}
-
- org.junit.jupiter
- junit-jupiter
- ${junit-jupiter-version}
- test
+ io.gsonfire
+ gson-fire
+ ${gson-fire-version}
- org.mockito
- mockito-junit-jupiter
- ${mockito-version}
- test
+ io.swagger
+ swagger-annotations
+ ${swagger-core-version}
- org.mockito
- mockito-core
- ${mockito-version}
- test
+ io.swagger.core.v3
+ swagger-annotations
+ ${swagger-annotations-v3-version}
- org.hamcrest
- hamcrest
- 3.0
- test
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ ${jakarta.ws.rs-api-version}
javax.xml.bind
jaxb-api
- 2.3.1
- compile
+ ${jaxb-api-version}
- io.swagger.core.v3
- swagger-annotations
- 2.2.30
- compile
+ org.apache.httpcomponents.client5
+ httpclient5
+ ${httpclient5-version}
+
+
- jakarta.ws.rs
- jakarta.ws.rs-api
- 3.1.0
- compile
+ com.squareup.okio
+ okio
+ ${okio-version}
+ test
- io.gsonfire
- gson-fire
- 1.9.0
- compile
+ org.hamcrest
+ hamcrest
+ ${hamcrest-version}
+ test
- com.squareup.okio
- okio
- 3.16.4
+ org.junit.jupiter
+ junit-jupiter
+ ${junit-jupiter-version}
test
- commons-codec
- commons-codec
- 1.18.0
+ org.mockito
+ mockito-core
+ ${mockito-version}
+ test
-
- io.swagger
- swagger-annotations
- ${swagger-core-version}
- compile
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito-version}
+ test
diff --git a/src/main/java/com/adyen/util/HMACValidator.java b/src/main/java/com/adyen/util/HMACValidator.java
index 75784542c..5f783101d 100644
--- a/src/main/java/com/adyen/util/HMACValidator.java
+++ b/src/main/java/com/adyen/util/HMACValidator.java
@@ -14,7 +14,7 @@
*
* Adyen Java API Library
*
- * Copyright (c) 2017 Adyen B.V.
+ * Copyright (c) 2026 Adyen B.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*/
@@ -31,8 +31,9 @@
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
-import javax.xml.bind.DatatypeConverter;
+import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
/** Utility class for generating and validating HMAC signatures used in Adyen webhooks. */
public class HMACValidator {
@@ -48,7 +49,7 @@ public class HMACValidator {
* @param data the data to sign
* @param key the HMAC key in hexadecimal format
* @return the Base64-encoded HMAC signature
- * @throws IllegalArgumentException if the data or key is null
+ * @throws IllegalArgumentException if the data or key is null or if key is invalid
* @throws SignatureException if signature generation fails
*/
public String calculateHMAC(String data, String key)
@@ -61,7 +62,7 @@ public String calculateHMAC(String data, String key)
throw new IllegalArgumentException("HMAC key is not provided");
}
- byte[] rawKey = DatatypeConverter.parseHexBinary(key);
+ byte[] rawKey = Hex.decodeHex(key);
SecretKeySpec signingKey = new SecretKeySpec(rawKey, HMAC_SHA256_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
@@ -71,6 +72,8 @@ public String calculateHMAC(String data, String key)
return new String(Base64.encodeBase64(rawHmac));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Missing data or key: " + e.getMessage());
+ } catch (DecoderException e) {
+ throw new IllegalArgumentException("Invalid Hex HMAC key: " + key);
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC: " + e.getMessage());
}
diff --git a/src/test/java/com/adyen/util/HMACValidatorTest.java b/src/test/java/com/adyen/util/HMACValidatorTest.java
index 3070bab33..2962a8b3a 100644
--- a/src/test/java/com/adyen/util/HMACValidatorTest.java
+++ b/src/test/java/com/adyen/util/HMACValidatorTest.java
@@ -22,8 +22,8 @@
package com.adyen.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
import com.adyen.BaseTest;
import com.adyen.model.notification.NotificationRequestItem;
@@ -77,7 +77,7 @@ public void testValidateHMACWithoutEscaping() throws SignatureException {
}
@Test
- public void testValidateHMACNullHmacSignature() throws SignatureException {
+ public void testValidateHMACNullHmacSignature() {
String notificationJson =
"{\n"
+ " \"additionalData\": {\n"
@@ -94,18 +94,17 @@ public void testValidateHMACNullHmacSignature() throws SignatureException {
+ " \"reason\": \"will contain the url to the report\",\n"
+ " \"success\": \"true\"\n"
+ "}";
- try {
- NotificationRequestItem notificationRequest =
- new Gson().fromJson(notificationJson, NotificationRequestItem.class);
- boolean result = new HMACValidator().validateHMAC(notificationRequest, HMAC_KEY);
- fail();
- } catch (IllegalArgumentException e) {
- assertEquals("Missing hmacSignature", e.getMessage());
- }
+ NotificationRequestItem notificationRequest =
+ new Gson().fromJson(notificationJson, NotificationRequestItem.class);
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new HMACValidator().validateHMAC(notificationRequest, HMAC_KEY));
+ assertEquals("Missing hmacSignature", exception.getMessage());
}
@Test
- public void testValidateHMACEmptyHmacSignature() throws SignatureException {
+ public void testValidateHMACEmptyHmacSignature() {
String notificationJson =
"{\n"
+ " \"additionalData\": {\n"
@@ -123,14 +122,13 @@ public void testValidateHMACEmptyHmacSignature() throws SignatureException {
+ " \"reason\": \"will contain the url to the report\",\n"
+ " \"success\": \"true\"\n"
+ "}";
- try {
- NotificationRequestItem notificationRequest =
- new Gson().fromJson(notificationJson, NotificationRequestItem.class);
- boolean result = new HMACValidator().validateHMAC(notificationRequest, HMAC_KEY);
- fail();
- } catch (IllegalArgumentException e) {
- assertEquals("Missing hmacSignature", e.getMessage());
- }
+ NotificationRequestItem notificationRequest =
+ new Gson().fromJson(notificationJson, NotificationRequestItem.class);
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new HMACValidator().validateHMAC(notificationRequest, HMAC_KEY));
+ assertEquals("Missing hmacSignature", exception.getMessage());
}
@Test
@@ -157,32 +155,44 @@ public void testValidateHMACMissingOptionalField() throws SignatureException {
@Test
public void testValidateHMACEmptyNotificationRequest() {
- try {
- new HMACValidator().getDataToSign((NotificationRequestItem) null);
- } catch (IllegalArgumentException e) {
- assertEquals("Missing NotificationRequestItem.", e.getMessage());
- }
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new HMACValidator().getDataToSign((NotificationRequestItem) null));
+ assertEquals("Missing NotificationRequestItem.", exception.getMessage());
}
@Test
public void testCalculateHMACNullPayload() {
- try {
- new HMACValidator().calculateHMAC((String) null, HMAC_KEY);
- } catch (IllegalArgumentException e) {
- assertEquals("Missing data or key: payload data is not provided", e.getMessage());
- } catch (SignatureException e) {
- fail();
- }
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new HMACValidator().calculateHMAC((String) null, HMAC_KEY));
+ assertEquals("Missing data or key: payload data is not provided", exception.getMessage());
}
@Test
public void testCalculateHMACNullKey() {
- try {
- new HMACValidator().calculateHMAC("TestPayload", null);
- } catch (IllegalArgumentException e) {
- assertEquals("Missing data or key: HMAC key is not provided", e.getMessage());
- } catch (SignatureException e) {
- fail();
- }
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new HMACValidator().calculateHMAC("TestPayload", null));
+ assertEquals("Missing data or key: HMAC key is not provided", exception.getMessage());
+ }
+
+ @Test
+ public void testCalculateHMACInvalidKeyFormat() {
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new HMACValidator().calculateHMAC("TestPayload", "BadKey"));
+ assertEquals("Invalid Hex HMAC key: BadKey", exception.getMessage());
+ }
+
+ @Test
+ public void testCalculateHMAC() throws SignatureException {
+ var hmac = new HMACValidator();
+ var out = hmac.calculateHMAC("TestPayload", HMAC_KEY);
+ assertEquals("YA9/UDjldIO4Gq68X4ATYYoEaPYH4f4k2uzBBzEF1PA=", out);
}
}