diff --git a/README.md b/README.md index 8a25748..2233542 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,37 @@ JweConfig config = JweConfigBuilder.aJweEncryptionConfig() .build(); ``` +**AES-CBC HMAC Authentication (A128CBC-HS256)** + +For enhanced security when using AES-CBC mode (A128CBC-HS256), you can enable HMAC authentication tag verification. This ensures data authenticity and integrity according to the JWE specification (RFC 7516). + +By default, HMAC verification is **disabled** for backward compatibility. To enable it: + +```java +JweConfig config = JweConfigBuilder.aJweEncryptionConfig() + .withEncryptionCertificate(encryptionCertificate) + .withDecryptionKey(decryptionKey) + .withEnableCbcHmacVerification(true) // Enable HMAC authentication + .build(); +``` + +**When to enable HMAC verification:** +- ✅ New integrations with systems that properly implement JWE A128CBC-HS256 +- ✅ When security and data authenticity are critical +- ✅ When working with compliant JWE encryption sources + +**When to keep it disabled (default):** +- ⚠️ Legacy systems that don't compute HMAC tags correctly +- ⚠️ Maintaining backward compatibility with existing deployments +- ⚠️ Encryption sources that don't fully follow the JWE specification + +**Technical Details:** +When enabled, the library: +- Splits the 256-bit Content Encryption Key (CEK) into a 128-bit HMAC key and 128-bit AES key +- Computes HMAC-SHA256 over: AAD || IV || Ciphertext || AL (AAD length in bits) +- Verifies the authentication tag (first 128 bits of HMAC output) before decryption +- Throws an `EncryptionException` if the authentication tag is invalid + ##### • Performing JWE Encryption Call `JweEncryption.encryptPayload` with a JSON request payload and a `JweConfig` instance. diff --git a/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java b/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java index 7f6a1bf..ff26d0b 100644 --- a/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java +++ b/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java @@ -53,6 +53,14 @@ public enum Scheme { Integer ivSize = 16; + /** + * Enable HMAC authentication tag verification for AES-CBC mode (A128CBC-HS256). + * When true, authentication tags are verified during decryption. + * Default is false for backward compatibility with systems that don't compute HMAC tags. + * Set to true to enable proper HMAC verification according to JWE spec. + */ + Boolean enableCbcHmacVerification = false; + /** * A list of JSON paths to encrypt in request payloads. * Example: @@ -116,4 +124,6 @@ String getEncryptedValueFieldName() { } public Integer getIVSize() { return ivSize; } + + public Boolean getEnableCbcHmacVerification() { return enableCbcHmacVerification; } } diff --git a/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java b/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java index 42c6005..7f8f022 100644 --- a/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java +++ b/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java @@ -24,6 +24,7 @@ abstract class EncryptionConfigBuilder { protected String encryptedValueFieldName; protected Integer ivSize = 16; + protected Boolean enableCbcHmacVerification = false; void computeEncryptionKeyFingerprintWhenNeeded() throws EncryptionException { try { diff --git a/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java b/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java index 59659cb..308938a 100644 --- a/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java +++ b/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java @@ -34,6 +34,7 @@ public JweConfig build() throws EncryptionException { config.encryptedValueFieldName = this.encryptedValueFieldName == null ? "encryptedData" : this.encryptedValueFieldName; config.scheme = EncryptionConfig.Scheme.JWE; config.ivSize = ivSize; + config.enableCbcHmacVerification = enableCbcHmacVerification; return config; } @@ -105,6 +106,17 @@ public JweConfigBuilder withEncryptionIVSize(Integer ivSize) { } throw new IllegalArgumentException("Supported IV Sizes are either 12 or 16!"); } + /** + * See: {@link EncryptionConfig#enableCbcHmacVerification}. + * Enable or disable HMAC authentication tag verification for AES-CBC mode (A128CBC-HS256). + * Default is false (disabled) for backward compatibility. + * Set to true to enable proper HMAC verification according to JWE spec. + */ + public JweConfigBuilder withEnableCbcHmacVerification(Boolean enableCbcHmacVerification) { + this.enableCbcHmacVerification = enableCbcHmacVerification; + return this; + } + private void checkParameterValues() { if (decryptionKey == null && encryptionCertificate == null && encryptionKey == null) { diff --git a/src/main/java/com/mastercard/developer/encryption/aes/AESCBC.java b/src/main/java/com/mastercard/developer/encryption/aes/AESCBC.java index 127a5bd..f098e02 100644 --- a/src/main/java/com/mastercard/developer/encryption/aes/AESCBC.java +++ b/src/main/java/com/mastercard/developer/encryption/aes/AESCBC.java @@ -1,13 +1,19 @@ package com.mastercard.developer.encryption.aes; +import com.mastercard.developer.encryption.EncryptionException; import com.mastercard.developer.encryption.jwe.JweObject; +import com.mastercard.developer.utils.ByteUtils; import com.mastercard.developer.utils.EncodingUtils; import javax.crypto.Cipher; +import javax.crypto.Mac; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.Key; +import java.security.MessageDigest; import java.security.spec.AlgorithmParameterSpec; public class AESCBC { @@ -15,21 +21,64 @@ public class AESCBC { private AESCBC() { } - private static final String CYPHER = "AES/CBC/PKCS5Padding"; + private static final String CIPHER = "AES/CBC/PKCS5Padding"; + private static final String HMAC_ALGORITHM = "HmacSHA256"; @java.lang.SuppressWarnings("squid:S3329") - public static byte[] decrypt(Key secretKey, JweObject object) throws GeneralSecurityException { - // First 16 bytes are the MAC key, so we only use the second 16 bytes - SecretKeySpec aesKey = new SecretKeySpec(secretKey.getEncoded(), 16, 16, "AES"); + public static byte[] decrypt(Key secretKey, JweObject object, boolean enableHmacVerification) throws GeneralSecurityException, EncryptionException { + byte[] cek = secretKey.getEncoded(); + + // For A128CBC-HS256: First 16 bytes are HMAC key, second 16 bytes are AES key + int keyLength = cek.length / 2; + SecretKeySpec aesKey = new SecretKeySpec(cek, keyLength, keyLength, "AES"); + byte[] cipherText = EncodingUtils.base64Decode(object.getCipherText()); byte[] iv = EncodingUtils.base64Decode(object.getIv()); + // Only verify authentication tag if enabled + if (enableHmacVerification) { + SecretKeySpec hmacKey = new SecretKeySpec(cek, 0, keyLength, HMAC_ALGORITHM); + byte[] authTag = EncodingUtils.base64Decode(object.getAuthTag()); + byte[] aad = object.getRawHeader().getBytes(StandardCharsets.US_ASCII); + + byte[] expectedTag = computeAuthTag(hmacKey, aad, iv, cipherText, keyLength); + if (!MessageDigest.isEqual(authTag, expectedTag)) { + throw new EncryptionException("Authentication tag verification failed"); + } + } + return cipher(aesKey, new IvParameterSpec(iv), cipherText, Cipher.DECRYPT_MODE); } public static byte[] cipher(Key key, AlgorithmParameterSpec iv, byte[] bytes, int mode) throws GeneralSecurityException { - Cipher cipher = Cipher.getInstance(CYPHER); + Cipher cipher = Cipher.getInstance(CIPHER); cipher.init(mode, key, iv); return cipher.doFinal(bytes); } + + /** + * Computes the authentication tag for AES-CBC-HMAC-SHA2 + * HMAC is computed over: AAD || IV || Ciphertext || AL + * where AL is the length of AAD in bits expressed as a 64-bit big-endian integer + */ + private static byte[] computeAuthTag(SecretKeySpec hmacKey, byte[] aad, byte[] iv, byte[] cipherText, int tagLength) + throws GeneralSecurityException { + Mac mac = Mac.getInstance(HMAC_ALGORITHM); + mac.init(hmacKey); + + // Compute AL (AAD Length in bits as 64-bit big-endian) + long aadLengthBits = (long) aad.length * 8; + byte[] al = ByteBuffer.allocate(8).putLong(aadLengthBits).array(); + + // HMAC input: AAD || IV || Ciphertext || AL + mac.update(aad); + mac.update(iv); + mac.update(cipherText); + mac.update(al); + + byte[] hmacOutput = mac.doFinal(); + + // Return first half (tagLength bytes) as the authentication tag + return ByteUtils.subArray(hmacOutput, 0, tagLength); + } } diff --git a/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java b/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java index 7e38a36..1529a91 100644 --- a/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java +++ b/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java @@ -49,7 +49,7 @@ public String decrypt(JweConfig config) throws EncryptionException, GeneralSecur if (AES_GCM_ENCRYPTION_METHODS.contains(encryptionMethod)) { plainText = AESGCM.decrypt(cek, this); } else if (encryptionMethod.equals(A128CBC_HS256)) { - plainText = AESCBC.decrypt(cek, this); + plainText = AESCBC.decrypt(cek, this, config.getEnableCbcHmacVerification()); } else { throw new EncryptionException(String.format("Encryption method %s not supported", encryptionMethod)); } diff --git a/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java b/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java index ac02937..ce443aa 100644 --- a/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java +++ b/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java @@ -193,6 +193,35 @@ public void testBuild_ShouldThrowIllegalArgumentException_WhenNotHavingWildcardO .build(); } + @Test + public void testBuild_ShouldDisableCbcHmacVerificationByDefault() throws Exception { + JweConfig config = JweConfigBuilder.aJweEncryptionConfig() + .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate()) + .withDecryptionKey(TestUtils.getTestDecryptionKey()) + .build(); + Assert.assertFalse("HMAC verification should be disabled by default for backward compatibility", config.getEnableCbcHmacVerification()); + } + + @Test + public void testBuild_ShouldAllowDisablingCbcHmacVerification() throws Exception { + JweConfig config = JweConfigBuilder.aJweEncryptionConfig() + .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate()) + .withDecryptionKey(TestUtils.getTestDecryptionKey()) + .withEnableCbcHmacVerification(false) + .build(); + Assert.assertFalse("HMAC verification should be disabled when explicitly set to false", config.getEnableCbcHmacVerification()); + } + + @Test + public void testBuild_ShouldAllowEnablingCbcHmacVerificationExplicitly() throws Exception { + JweConfig config = JweConfigBuilder.aJweEncryptionConfig() + .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate()) + .withDecryptionKey(TestUtils.getTestDecryptionKey()) + .withEnableCbcHmacVerification(true) + .build(); + Assert.assertTrue("HMAC verification should be enabled when explicitly set to true", config.getEnableCbcHmacVerification()); + } + @Test public void testBuild_ShouldThrowIllegalArgumentException_WhenMultipleWildcardsOnEncryptionPaths() throws Exception { expectedException.expect(IllegalArgumentException.class); diff --git a/src/test/java/com/mastercard/developer/encryption/aes/AESCBCTest.java b/src/test/java/com/mastercard/developer/encryption/aes/AESCBCTest.java new file mode 100644 index 0000000..3625c60 --- /dev/null +++ b/src/test/java/com/mastercard/developer/encryption/aes/AESCBCTest.java @@ -0,0 +1,218 @@ +package com.mastercard.developer.encryption.aes; + +import com.mastercard.developer.encryption.EncryptionException; +import com.mastercard.developer.encryption.jwe.JweObject; +import com.mastercard.developer.json.JsonEngine; +import com.mastercard.developer.utils.EncodingUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class AESCBCTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void testDecrypt_ShouldDecryptSuccessfully_WhenHmacVerificationIsEnabledAndTagIsValid() throws Exception { + // Given: A properly constructed JWE with correct HMAC tag + byte[] cekBytes = new byte[32]; // 256-bit key (128-bit HMAC key + 128-bit AES key) + java.security.SecureRandom random = new java.security.SecureRandom(); + random.nextBytes(cekBytes); + + SecretKeySpec cek = new SecretKeySpec(cekBytes, "AES"); + SecretKeySpec hmacKey = new SecretKeySpec(cekBytes, 0, 16, "HmacSHA256"); + SecretKeySpec aesKey = new SecretKeySpec(cekBytes, 16, 16, "AES"); + + // Encrypt data + byte[] plainText = "Valid HMAC Test Data".getBytes(StandardCharsets.UTF_8); + byte[] iv = new byte[16]; + random.nextBytes(iv); + + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, new javax.crypto.spec.IvParameterSpec(iv)); + byte[] cipherText = cipher.doFinal(plainText); + + // Compute proper HMAC according to JWE spec + String rawHeader = EncodingUtils.base64UrlEncode("{\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA-OAEP-256\"}".getBytes()); + byte[] aad = rawHeader.getBytes(StandardCharsets.US_ASCII); + + javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256"); + mac.init(hmacKey); + mac.update(aad); + mac.update(iv); + mac.update(cipherText); + + // Add AL (AAD length in bits as 64-bit big-endian) + long aadLengthBits = (long) aad.length * 8; + java.nio.ByteBuffer alBuffer = java.nio.ByteBuffer.allocate(8); + alBuffer.putLong(aadLengthBits); + mac.update(alBuffer.array()); + + byte[] hmacOutput = mac.doFinal(); + byte[] authTag = new byte[16]; // First 16 bytes (tag length = key length for A128CBC-HS256) + System.arraycopy(hmacOutput, 0, authTag, 0, 16); + + // Construct JWE string + String jweString = rawHeader + ".dummy." + EncodingUtils.base64UrlEncode(iv) + "." + + EncodingUtils.base64UrlEncode(cipherText) + "." + EncodingUtils.base64UrlEncode(authTag); + JweObject jweObject = JweObject.parse(jweString, JsonEngine.getDefault()); + + // When: Decrypt with HMAC verification enabled + byte[] result = AESCBC.decrypt(cek, jweObject, true); + + // Then: Should succeed and return correct plaintext + assertNotNull(result); + assertEquals("Valid HMAC Test Data", new String(result, StandardCharsets.UTF_8)); + } + + @Test + public void testDecrypt_ShouldThrowException_WhenHmacVerificationIsEnabledAndTagIsInvalid() throws Exception { + // Given: A JWE object with an invalid HMAC tag + String rawHeader = EncodingUtils.base64UrlEncode("{\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA-OAEP-256\"}".getBytes()); + + // Create a JWE string with intentionally wrong auth tag + String encryptedKey = "dummy_encrypted_key_base64url"; + String iv = EncodingUtils.base64UrlEncode(new byte[16]); // 16-byte IV + String cipherText = EncodingUtils.base64UrlEncode("encrypted_data".getBytes()); + String invalidAuthTag = EncodingUtils.base64UrlEncode(new byte[16]); // Wrong tag + + String jweString = rawHeader + "." + encryptedKey + "." + iv + "." + cipherText + "." + invalidAuthTag; + JweObject jweObject = JweObject.parse(jweString, JsonEngine.getDefault()); + + // When/Then: Decryption should fail with authentication tag verification error + expectedException.expect(EncryptionException.class); + expectedException.expectMessage("Authentication tag verification failed"); + + // Create a dummy CEK + byte[] cekBytes = new byte[32]; // 256-bit key + SecretKeySpec cek = new SecretKeySpec(cekBytes, "AES"); + + AESCBC.decrypt(cek, jweObject, true); + } + + @Test + public void testDecrypt_ShouldDecryptWithoutVerification_WhenHmacVerificationIsDisabled() throws Exception { + // Given: A JWE object (even with wrong HMAC tag) + String rawHeader = EncodingUtils.base64UrlEncode("{\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA-OAEP-256\"}".getBytes()); + + // Create a simple encrypted payload using AES-CBC + byte[] cekBytes = new byte[32]; // 256-bit key + SecretKeySpec cek = new SecretKeySpec(cekBytes, "AES"); + + // Encrypt some data first + byte[] plainText = "test".getBytes(StandardCharsets.UTF_8); + byte[] iv = new byte[16]; // Zero IV for testing + + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding"); + javax.crypto.spec.SecretKeySpec aesKey = new javax.crypto.spec.SecretKeySpec(cekBytes, 16, 16, "AES"); + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, new javax.crypto.spec.IvParameterSpec(iv)); + byte[] encrypted = cipher.doFinal(plainText); + + String encryptedKey = "dummy_encrypted_key_base64url"; + String ivB64 = EncodingUtils.base64UrlEncode(iv); + String cipherTextB64 = EncodingUtils.base64UrlEncode(encrypted); + String authTag = EncodingUtils.base64UrlEncode(new byte[16]); // Wrong tag, but should be ignored + + String jweString = rawHeader + "." + encryptedKey + "." + ivB64 + "." + cipherTextB64 + "." + authTag; + JweObject jweObject = JweObject.parse(jweString, JsonEngine.getDefault()); + + // When: Decrypt with HMAC verification disabled + byte[] result = AESCBC.decrypt(cek, jweObject, false); + + // Then: Should succeed and return decrypted data + assertNotNull(result); + assertEquals("test", new String(result, StandardCharsets.UTF_8)); + } + + @Test + public void testDecrypt_ShouldUseCorrectKeySplit_WhenDecrypting() throws Exception { + // Given: A 256-bit CEK that should be split into HMAC key (first 128 bits) and AES key (second 128 bits) + byte[] cekBytes = new byte[32]; + // Fill with a pattern to verify correct split + for (int i = 0; i < 16; i++) { + cekBytes[i] = (byte) 0xAA; // HMAC key part + cekBytes[i + 16] = (byte) 0xBB; // AES key part + } + SecretKeySpec cek = new SecretKeySpec(cekBytes, "AES"); + + // Create encrypted data using only the second half (AES key) + byte[] plainText = "testdata".getBytes(StandardCharsets.UTF_8); + byte[] iv = new byte[16]; + + javax.crypto.spec.SecretKeySpec aesKey = new javax.crypto.spec.SecretKeySpec(cekBytes, 16, 16, "AES"); + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, new javax.crypto.spec.IvParameterSpec(iv)); + byte[] encrypted = cipher.doFinal(plainText); + + String rawHeader = EncodingUtils.base64UrlEncode("{\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA-OAEP-256\"}".getBytes()); + String jweString = rawHeader + ".dummy." + EncodingUtils.base64UrlEncode(iv) + "." + + EncodingUtils.base64UrlEncode(encrypted) + "." + EncodingUtils.base64UrlEncode(new byte[16]); + JweObject jweObject = JweObject.parse(jweString, JsonEngine.getDefault()); + + // When: Decrypt without HMAC verification (to avoid tag mismatch) + byte[] result = AESCBC.decrypt(cek, jweObject, false); + + // Then: Should correctly use the second half of CEK for decryption + assertEquals("testdata", new String(result, StandardCharsets.UTF_8)); + } + + @Test + public void testDecrypt_ShouldComputeCorrectHmac_WhenVerificationIsEnabled() throws Exception { + // Given: A properly constructed JWE with correct HMAC + byte[] cekBytes = new byte[32]; + java.security.SecureRandom random = new java.security.SecureRandom(); + random.nextBytes(cekBytes); + + SecretKeySpec cek = new SecretKeySpec(cekBytes, "AES"); + SecretKeySpec hmacKey = new SecretKeySpec(cekBytes, 0, 16, "HmacSHA256"); + SecretKeySpec aesKey = new SecretKeySpec(cekBytes, 16, 16, "AES"); + + // Encrypt data + byte[] plainText = "Hello, World!".getBytes(StandardCharsets.UTF_8); + byte[] iv = new byte[16]; + random.nextBytes(iv); + + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, new javax.crypto.spec.IvParameterSpec(iv)); + byte[] cipherText = cipher.doFinal(plainText); + + // Compute proper HMAC + String rawHeader = EncodingUtils.base64UrlEncode("{\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA-OAEP-256\"}".getBytes()); + byte[] aad = rawHeader.getBytes(StandardCharsets.US_ASCII); + + javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256"); + mac.init(hmacKey); + mac.update(aad); + mac.update(iv); + mac.update(cipherText); + + // Add AL (AAD length in bits as 64-bit big-endian) + long aadLengthBits = (long) aad.length * 8; + java.nio.ByteBuffer alBuffer = java.nio.ByteBuffer.allocate(8); + alBuffer.putLong(aadLengthBits); + mac.update(alBuffer.array()); + + byte[] hmacOutput = mac.doFinal(); + byte[] authTag = new byte[16]; // First 16 bytes + System.arraycopy(hmacOutput, 0, authTag, 0, 16); + + // Construct JWE + String jweString = rawHeader + ".dummy." + EncodingUtils.base64UrlEncode(iv) + "." + + EncodingUtils.base64UrlEncode(cipherText) + "." + EncodingUtils.base64UrlEncode(authTag); + JweObject jweObject = JweObject.parse(jweString, JsonEngine.getDefault()); + + // When: Decrypt with HMAC verification enabled + byte[] result = AESCBC.decrypt(cek, jweObject, true); + + // Then: Should succeed and return correct plaintext + assertEquals("Hello, World!", new String(result, StandardCharsets.UTF_8)); + } +} + diff --git a/src/test/java/com/mastercard/developer/encryption/jwe/JWEObjectTest.java b/src/test/java/com/mastercard/developer/encryption/jwe/JWEObjectTest.java index 45c7bf0..ac449db 100644 --- a/src/test/java/com/mastercard/developer/encryption/jwe/JWEObjectTest.java +++ b/src/test/java/com/mastercard/developer/encryption/jwe/JWEObjectTest.java @@ -21,6 +21,26 @@ public void testDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIsCbcEncrypted() assertEquals("bar", decryptedPayload); } + @Test + public void testDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIsCbcEncryptedAndHmacVerificationDisabled() throws Exception { + JweObject jweObject = TestUtils.getTestCbcJweObject(); + String decryptedPayload = jweObject.decrypt(TestUtils.getTestJweConfigBuilder() + .withEnableCbcHmacVerification(false) + .build()); + + assertEquals("bar", decryptedPayload); + } + + @Test + public void testDecrypt_ShouldReturnDecryptedPayload_WhenPayloadIsCbcEncryptedAndHmacVerificationExplicitlyEnabled() throws Exception { + JweObject jweObject = TestUtils.getTestCbcJweObject(); + String decryptedPayload = jweObject.decrypt(TestUtils.getTestJweConfigBuilder() + .withEnableCbcHmacVerification(true) + .build()); + + assertEquals("bar", decryptedPayload); + } + private static Stream aesGcmJweObjects() { return ImmutableList.of( TestUtils.getTestAes128GcmJweObject(),