diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java index 48d5cbc8b01..092581d3fd0 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java @@ -192,6 +192,11 @@ public final class JoseConstants extends RSSecurityConstants { */ public static final String RSSEC_ENCRYPTION_PBES2_COUNT = "rs.security.encryption.pbes2.count"; + /** + * The max value for the "p2c" (PBES2 count) Header Parameter used for decryption. The default is 1_000_000. + */ + public static final String RSSEC_DECRYPTION_MAX_PBES2_COUNT = "rs.security.decryption.max.pbes2.count"; + // // JWT specific configuration // diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index cefad52f5a6..67d6cb6af31 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -568,7 +568,10 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, if (password == null) { throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE); } - keyDecryptionProvider = new PbesHmacAesWrapKeyDecryptionAlgorithm(new String(password)); + int pbes2Count = MessageUtils.getContextualInteger(m, + JoseConstants.RSSEC_DECRYPTION_MAX_PBES2_COUNT, 1_000_000); + + keyDecryptionProvider = new PbesHmacAesWrapKeyDecryptionAlgorithm(new String(password), pbes2Count); } else { PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.DECRYPT); if (keyAlgo == null) { diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java index 505049bba7d..fcf00beeb8e 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java @@ -26,10 +26,15 @@ public class PbesHmacAesWrapKeyDecryptionAlgorithm implements KeyDecryptionProvider { private final byte[] password; private final KeyAlgorithm algo; + private final int maxPbesCount; public PbesHmacAesWrapKeyDecryptionAlgorithm(String password) { this(password, KeyAlgorithm.PBES2_HS256_A128KW, false); } + public PbesHmacAesWrapKeyDecryptionAlgorithm(String password, int maxPbesCount) { + this(PbesHmacAesWrapKeyEncryptionAlgorithm.stringToBytes(password), KeyAlgorithm.PBES2_HS256_A128KW, + false, maxPbesCount); + } public PbesHmacAesWrapKeyDecryptionAlgorithm(String password, KeyAlgorithm algo, boolean hashLargePasswords) { this(PbesHmacAesWrapKeyEncryptionAlgorithm.stringToBytes(password), algo, hashLargePasswords); } @@ -43,15 +48,25 @@ public PbesHmacAesWrapKeyDecryptionAlgorithm(byte[] password) { this(password, KeyAlgorithm.PBES2_HS256_A128KW, false); } public PbesHmacAesWrapKeyDecryptionAlgorithm(byte[] password, KeyAlgorithm algo, boolean hashLargePasswords) { + this(password, algo, hashLargePasswords, 1_000_000); + } + + public PbesHmacAesWrapKeyDecryptionAlgorithm(byte[] password, KeyAlgorithm algo, boolean hashLargePasswords, + int maxPbesCount) { this.password = PbesHmacAesWrapKeyEncryptionAlgorithm.validatePassword(password, algo.getJwaName(), hashLargePasswords); this.algo = algo; + this.maxPbesCount = maxPbesCount; } + @Override public byte[] getDecryptedContentEncryptionKey(JweDecryptionInput jweDecryptionInput) { JweHeaders jweHeaders = jweDecryptionInput.getJweHeaders(); byte[] saltInput = getDecodedBytes(jweHeaders.getHeader("p2s")); int pbesCount = jweHeaders.getIntegerHeader("p2c"); + if (pbesCount > maxPbesCount) { + throw new JoseException("Too many PBES2 iterations"); + } String keyAlgoJwt = jweHeaders.getKeyEncryptionAlgorithm().getJwaName(); int keySize = PbesHmacAesWrapKeyEncryptionAlgorithm.getKeySize(keyAlgoJwt); byte[] derivedKey = PbesHmacAesWrapKeyEncryptionAlgorithm diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java index d4233adf922..6f4f3c5dfbc 100644 --- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java @@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets; import java.security.Security; +import org.apache.cxf.rs.security.jose.common.JoseException; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -30,6 +31,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public class JwePbeHmacAesWrapTest { @Before @@ -75,4 +77,36 @@ public void testEncryptDecryptPbesHmacAesWrapAesGcm() throws Exception { assertEquals(specPlainText, decryptedText); } + + @Test + public void testDecryptWithInvalidLargeP2CValue() throws Exception { + final String specPlainText = "Live long and prosper."; + JweHeaders headers = new JweHeaders(); + headers.setKeyEncryptionAlgorithm(KeyAlgorithm.PBES2_HS256_A128KW); + headers.setContentEncryptionAlgorithm(ContentAlgorithm.A128GCM); + final String password = "Thus from my lips, by yours, my sin is purged."; + KeyEncryptionProvider keyEncryption = + new PbesHmacAesWrapKeyEncryptionAlgorithm(password, 1_500_000, KeyAlgorithm.PBES2_HS256_A128KW, false); + JweEncryptionProvider encryption = new JweEncryption(keyEncryption, + new AesGcmContentEncryptionAlgorithm(ContentAlgorithm.A128GCM)); + String jweContent = encryption.encrypt(specPlainText.getBytes(StandardCharsets.UTF_8), null); + + // 1. It should fail by default + PbesHmacAesWrapKeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); + JweDecryptionProvider decryption = new JweDecryption(keyDecryption, + new AesGcmContentDecryptionAlgorithm(ContentAlgorithm.A128GCM)); + try { + decryption.decrypt(jweContent).getContentText(); + fail("Failure expected on a too large p2c value"); + } catch (JoseException ex) { + // expected + } + + // 2. Now we allow 1.5M iterations and it passes + keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password, 1_500_000); + decryption = new JweDecryption(keyDecryption, + new AesGcmContentDecryptionAlgorithm(ContentAlgorithm.A128GCM)); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + } }