Skip to content

Commit

Permalink
Put a max default configurable limit on the Jose P2C parameter (#1793)
Browse files Browse the repository at this point in the history
(cherry picked from commit d1d77c3)
(cherry picked from commit 2d2baa3)
  • Loading branch information
coheigea authored and reta committed Apr 5, 2024
1 parent 34eb237 commit 20793d3
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +31,7 @@
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class JwePbeHmacAesWrapTest {
@Before
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 20793d3

Please sign in to comment.