Skip to content

CVE‐2026‐71892

David Hook edited this page Oct 2, 2026 · 1 revision

Title: CMS key-transport recipient key-size validation never runs for RFC 9709 HKDF-derived keys.

Issue affecting: BC before 1.86 (from 1.78), BC-FJA before bcpkix-fips 2.0.13 (from 2.0.7) and 2.1.13.

Fixed versions: BC 1.86, BC-FJA bcpkix-fips 2.0.13 (from 2.0.7) and 2.1.13.

Platform affected: Java 8 and later.

JceKeyTransRecipient.setKeySizeValidation(true) is a documented, opt-in hardening control: it is the only mechanism the API offers for rejecting a recovered content-encryption key whose size does not match what the CMS structure's own content-encryption algorithm identifier declares. A caller who wants that protection has no alternative but to enable it and trust it.

Where the content-encryption algorithm is wrapped in the RFC 9709 id-alg-cek-hkdf-sha256 construction, the real algorithm identifier lives in the wrapper's parameters and has to be unwrapped before the key size means anything. The branch that should have done so tested

if (encryptedEncryptionKey.equals(CMSObjectIdentifiers.id_alg_cek_hkdf_sha256))

where encryptedEncryptionKey is the byte[] of RSA-wrapped ciphertext. A byte[] does not override Object.equals, so this compares an array reference against a singleton ASN1ObjectIdentifier and is false for every possible input — the intended branch was unreachable. Control fell to the else, which looked the key size up against the outer id-alg-cek-hkdf-sha256 OID. That OID names a key-derivation construction rather than a cipher, so DefaultSecretKeySizeProvider has no entry for it, EnvelopedDataHelper.keySizeCheck computed a non-positive expected size, and the comparison was skipped outright — for any key length whatsoever.

The result is that a sender, who controls both the declared content-encryption algorithm and the key bytes it wraps, could advertise AES-256-CBC while transporting a 128-bit key, and a recipient with setKeySizeValidation(true) accepted it in silence. Java's AES Cipher selects strength by key length rather than by the declared identifier, so nothing downstream objected either. The practical impact is the silent failure of an explicitly-requested policy control rather than direct plaintext exposure — the attacker still lacks the recipient's private key.

The recipient now dispatches on encryptedKeyAlgorithm.getAlgorithm(), matching the pattern EnvelopedDataHelper.getJceKey already used correctly a few lines earlier, so validation checks the recovered key against the inner content-encryption algorithm. Messages whose key size already matched, non-HKDF messages, and recipients that never enabled validation are unaffected.

The fix was introduced in commit be0a7d925c81.

Credit: Yu Bao from the PayPal Cyber Security Team.

Clone this wiki locally