CVE summaries
Following are summaries of the 32 CVEs addressed in BC 1.85 (most also back-ported to BC-LTS 2.73.12). These are ordered by CVSS 4.0 base severity, most serious first. Descriptions are condensed from the per-CVE records in this directory; follow the CVE id for the full record and the fix commit.
Critical severity
-
CVE-2026-58062 — Critical (9.3) · CWE-295
Stapled OCSP response accepted without binding to the checked certificate
-
CVE-2026-59638 — Critical (9.3) · CWE-297
JSSE hostname verifier CN-fallback enabled by default despite documented opt-in
-
CVE-2026-59650 — Critical (9.3) · CWE-20
MTI/A0 DH agreement exponentiates unvalidated peer value
-
CVE-2026-8763 — Critical (9.3) · CWE-295
Name Constraints bypass via trailing dot in rfc822Name and URI
High severity
-
CVE-2026-12802 — High (8.7) · CWE-354
CMS AuthEnvelopedData fails to enforce tag-length on decryption
-
CVE-2026-12803 — High (8.7) · CWE-354
KCCMBlockCipher MAC does not bind nonce when AAD is absent (cross-nonce AEAD forgery)
-
CVE-2026-12816 — High (8.7) · CWE-354
IESEngine stream-mode MAC forgery via length-dependent KDF split
-
CVE-2026-12817 — High (8.7) · CWE-354
OpenPGP AEAD decryption skips final tag on chunk-aligned data
-
CVE-2026-12852 — High (8.7) · CWE-789
MLS wire decoder allocates attacker-declared opaque length before bounds check
-
CVE-2026-12860 — High (8.7) · CWE-347
RSA PKCS#1 verification skips last two hash bytes in NULL-omitted path
-
CVE-2026-13506 — High (8.7) · CWE-674
Lazy ASN.1 sequence forcing resets nesting-depth guard
-
CVE-2026-14682 — High (8.7) · CWE-789
Possible OOM from unbounded up-front allocation on a definite-length read
-
CVE-2026-58059 — High (8.7) · CWE-407
Quadratic-time escaping when stringifying X.500 distinguished names
-
CVE-2026-58060 — High (8.7) · CWE-789
HSS public-key level count unbounded, enabling huge allocation on verify
-
CVE-2026-58061 — High (8.7) · CWE-354
CCM-family modes write plaintext to caller buffer before tag check
-
CVE-2026-59639 — High (8.7) · CWE-347
CMS verifySignatures returns true for SignedData with zero signers
-
CVE-2026-59640 — High (8.7) · CWE-203
OpenPGP CFB quick-check oracle active on symmetric/session-key paths
-
CVE-2026-59641 — High (8.7) · CWE-345
S/MIME validator trusts signer-asserted signingTime for path validation
-
CVE-2026-59642 — High (8.7) · CWE-354
CMS AuthenticatedData content not bound to MAC when authAttrs present
-
CVE-2026-59643 — High (8.7) · CWE-347
OpenPGP inline-signature policy failures silently ignored
-
CVE-2026-59644 — High (8.7) · CWE-834
MLS hash-ratchet honours arbitrary 32-bit generation counter from sender
-
CVE-2026-59645 — High (8.7) · CWE-674
OER parser recurses without depth limit on self-referential IEEE 1609.2 schema
-
CVE-2026-59646 — High (8.7) · CWE-789
DTLS handshake reassembler allocates buffer from unchecked 24-bit length
-
CVE-2026-59649 — High (8.7) · CWE-789
OpenPGP user-attribute subpacket length bounded only by JVM max memory
-
CVE-2026-12185 — High (7.1) · CWE-789
BKS/UBER keystore allocates from untrusted lengths before integrity check
-
CVE-2026-59651 — High (7.1) · CWE-326
BKS keystore accepts legacy version with 16-bit integrity MAC key
Medium severity
-
CVE-2026-59647 — Medium (6.9) · CWE-770
CRMF/CMP password-MAC honours unbounded iteration count
-
CVE-2026-59648 — Medium (6.9) · CWE-770
OpenPGP Argon2 S2K honours attacker-chosen memory and passes
-
CVE-2026-59652 — Medium (6.9) · CWE-90
LDAP filter injection in legacy jdk1.4 LDAPStoreHelper
-
CVE-2026-13586 — Medium (5.3) · CWE-770
PKCS#12 MAC and bag-decryption KDF iteration-count bound (DoS)
-
CVE-2026-15055 — Medium (5.3) · CWE-770
PKCS#8 / PBES2 decryptors honour unbounded KDF cost from input
-
CVE-2026-58063 — Medium (5.3) · CWE-770
BCFKS keystore load honours unbounded KDF cost from untrusted file
CVE details
Following are the full details of each CVE, ordered by base severity.
Critical
CVE-2026-58062 — Stapled OCSP response accepted without binding to the checked certificate
CVSS 9.3 Critical · CWE-295 · fixed in BC 1.85, BC-LTS 2.73.12 · fix add5f822660f
In Bouncy Castle's ProvOcspRevocationChecker, when an OCSP response is supplied via PKIXRevocationChecker.setOcspResponses() (the stapled path), check() verifies the response signature and then loops over SingleResponse entries looking for a CertID match. If no entry matches the certificate's serial and issuer hashes, the loop simply completes and the method returns without throwing, so the certificate is treated as not revoked and no CRL fallback occurs. The network-fetch path enforces this binding via OcspCache.isCertIDFoundAndCurrent(), but the stapled path omits it. A TLS server with a revoked certificate can staple any legitimately-signed OCSP response from the same CA (e.g., a 'good' response for a different serial) and pass revocation checking on a BC-based client.
CVE-2026-59638 — JSSE hostname verifier CN-fallback enabled by default despite documented opt-in
CVSS 9.3 Critical · CWE-297 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 799bd15320a6
HostnameUtil gates the 'match CN when no dNSName SAN exists' fallback on Properties.isOverrideSet(JSSE_HOSTNAME_CHECK_CN_FALLBACK, true), and the two-argument isOverrideSet returns the supplied default (true) when the property is unset. The javadoc for the property says the unset default must DISABLE the fallback, but the code ships with it active in every deployment that doesn't explicitly set the property to false. RFC 5280 name-constraint processing only constrains SAN entries of the constrained type, so a leaf with no dNSName SAN passes a dNSName-constrained chain regardless of its CN. HostnameUtil then falls through to matching the attacker-chosen CN against the target hostname.
CVE-2026-59650 — MTI/A0 DH agreement exponentiates unvalidated peer value
CVSS 9.3 Critical · CWE-20 · fixed in BC 1.85, BC-LTS 2.73.12 · fix daeaae9d7075
In Bouncy Castle's DHAgreement (MTI/A0 two-pass variant), calculateAgreement(pub, message) raises the raw peer-supplied message to the power of the local static private key x without any range or subgroup-membership check. While the pub argument is validated by DHPublicKeyParameters, message is a bare BigInteger taken straight from the wire. A malicious peer can repeatedly send small-order elements (0, 1, p−1, or elements of small subgroups of (Z/pZ)*), and each exchange leaks x mod r for some small prime r; combining these via CRT recovers the full static private key (Lim–Lee 1997). DHBasicAgreement is unaffected because its single input passes through DHPublicKeyParameters validation.
CVE-2026-8763 — Name Constraints bypass via trailing dot in rfc822Name and URI
CVSS 9.3 Critical · CWE-295 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 2c28b253a446
In PKIXNameConstraintValidator, the dNSName path strips trailing dots before comparison, but the rfc822Name (isEmailConstrained) and URI (isURIConstrained) paths compare the extracted host with a bare equalsIgnoreCase. A leaf cert with SAN rfc822Name "ceo@bank.com." therefore does not match an excludedSubtrees entry of "bank.com", so checkExcludedEmail never throws and the path validates. This is reachable from the production PKIXCertPathValidatorSpi_8 → RFC3280CertPathUtilities.processCertBC code path. An attacker who controls a name-constrained intermediate CA can issue certificates for email/URI hosts that the constraints were meant to exclude.
High
CVE-2026-12802 — CMS AuthEnvelopedData fails to enforce tag-length on decryption
CVSS 8.7 High · CWE-354 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 0fefa539e6ac
In Bouncy Castle for Java before 1.85, CMS AuthEnvelopedData fails to enforce tag-length on decryption. This issue also affects Bouncy Castle for Java LTS before 2.73.12.
CVE-2026-12803 — KCCMBlockCipher MAC does not bind nonce when AAD is absent (cross-nonce AEAD forgery)
CVSS 8.7 High · CWE-354 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 697794413ebf
In Bouncy Castle's DSTU 7624 CCM mode, processPacket() only calls processAAD() when associatedText.size() > 0, but processAAD() is the sole place where the G1 block (nonce || length || flag) is folded into macBlock. With no AAD, macBlock starts at all-zeros and CalculateMac() computes a plain zero-IV CBC-MAC over the plaintext, so the tag depends only on (key, plaintext) and not on the nonce. A chosen-plaintext attacker with access to an encryption oracle can use three queries to derive the data- and tag-keystream for two nonces and assemble a valid ciphertext+tag for any never-queried (plaintext, nonce) pair. The decryptor recomputes the same nonce-independent MAC and the constant-time tag check at line 359 passes, fully breaking AEAD authenticity.
CVE-2026-12816 — IESEngine stream-mode MAC forgery via length-dependent KDF split
CVSS 8.7 High · CWE-354 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 6d9e4bbaee94
When IESEngine is initialised via the 4-arg init(forEncryption, priv, pub, params) with no block cipher, V is empty and the KDF is seeded only with the static ECDH shared secret Z. The derived keystream is split as K1 (inLen bytes) followed by K2 (MAC key), so K2's offset depends on message length. Because K1 is used as an XOR keystream, C⊕M reveals KDF[0..L], and KDF2BytesGenerator is prefix-stable, so the attacker now knows the MAC key for any message of length L' ≤ L − macKeyBytes. A passive observer with one known plaintext can therefore forge arbitrary authenticated ciphertexts accepted by the recipient, escalating from passive reader to active forger without either private key.
CVE-2026-12817 — OpenPGP AEAD decryption skips final tag on chunk-aligned data
CVSS 8.7 High · CWE-354 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 4b712819846e
In PGPAeadInputStream.readBlock() (BcAEADUtil.java:438 and the mirrored JceAEADUtil.java), the final AEAD tag is verified only when the last chunk is shorter than chunkLength. When dataLen == chunkLength, the code pre-reads the next 16 bytes but ignores the readFully return value, and the following call sees dataLen == 0 and returns clean EOF without ever calling c.doFinal() on the final tag. RFC 9580 §5.13.2 relies on this final tag (which authenticates total byte count) to detect truncation, and PGPEncryptedData.verify() unconditionally returns true for AEAD, so there is no backstop. A network attacker who controls the ciphertext can strip trailing chunks plus the final tag, fix up the unauthenticated outer packet length, and have the recipient accept the truncated plaintext as authentic.
CVE-2026-12852 — MLS wire decoder allocates attacker-declared opaque length before bounds check
CVSS 8.7 High · CWE-789 · fixed in BC 1.85 · fix a747038bb5bb
In bc-java's MLS codec, MLSInputStream.readOpaque() decodes a Varint length (max 0x3FFFFFFF ≈ 1 GiB) and immediately calls stream.readAll(size), which performs new byte[size] before checking that enough bytes remain. readOpaque() is hit on the very first fields of unauthenticated wire structures (PublicMessage FramedContent.group_id, KeyPackage.init_key, Welcome.encrypted_group_info, PrivateMessage.group_id), so the allocation occurs before any signature or MAC verification. An attacker who can deliver raw MLS bytes sends a ~6-byte message whose first opaque length varint is 0xBFFFFFFF; each such message forces a ~1 GiB allocation. A handful of concurrent messages exhausts the JVM heap and crashes the process.
CVE-2026-12860 — RSA PKCS#1 verification skips last two hash bytes in NULL-omitted path
CVSS 8.7 High · CWE-347 · fixed in BC 1.85, BC-LTS 2.73.12 · fix ea5970ea9b2f
In org.bouncycastle.crypto.signers.RSADigestSigner.verifySignature(), the legacy branch that handles DigestInfo encoded without NULL AlgorithmIdentifier parameters computes comparison offsets as length - hash.length - 2, causing the two comparison loops to jointly cover bytes [0..sig.length-2) while leaving the last two bytes of the hash unverified. An attacker who can present a signature produced with the NULL-less encoding therefore only needs a message whose digest matches the signed digest in all but the final two bytes, shaving 2^16 off the second-preimage work factor. This path is reachable by default (unless org.bouncycastle.pkcs1.strict_digestinfo is set) from BC-lightweight TLS rsa_pkcs1_* verification, PKIX/CMS verification, and OpenPGP RSA verification. The sibling JCA DigestSignatureSpi has the corrected loop bound, confirming the defect.
CVE-2026-13506 — Lazy ASN.1 sequence forcing resets nesting-depth guard
CVSS 8.7 High · CWE-674 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 77454da9b3dc
In Bouncy Castle's ASN.1 layer, LazyEncodedSequence.force() creates a fresh ASN1InputStream with a re-initialised nesting-depth counter rather than inheriting the remaining depth from the parse that produced it, and the lazy SEQUENCE branch never calls createSubStream()/decrementDepth(). X509CRLHolder and the BC CertificateFactory CRL path enable lazy evaluation, and TBSCertList stores revokedCertificates as an unforced lazy sequence. An attacker who can supply a ~40-50KB DER CRL whose revokedCertificates is a chain of ~10,000 nested SEQUENCEs causes hashCode()/equals()/toDERObject()/getEncoded()/isSignatureValid() to recurse one Java frame per level with a fresh depth budget each time. The result is a StackOverflowError that is typically uncaught and kills the handling thread, yielding a denial of service.
CVE-2026-14682 — Possible OOM from unbounded up-front allocation on a definite-length read
CVSS 8.7 High · CWE-789 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 37094e504ef5
In Bouncy Castle for Java before 1.85, Possible OOM from unbounded up-front allocation on a definite-length read. This issue also affects Bouncy Castle for Java LTS before 2.73.12.
CVE-2026-58059 — Quadratic-time escaping when stringifying X.500 distinguished names
CVSS 8.7 High · CWE-407 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 7bf20eea8c1b
IETFUtils.valueToString(), reached from X500Name.toString()/equals()/hashCode() via AbstractX500NameStyle, escapes special characters by calling vBuf.insert(index, "\") for each match while iterating the same buffer. Because insert shifts all trailing characters, a value of N escapable bytes costs roughly N²/2 char moves. An attacker who can supply an X.509 certificate, CSR, CRL, or CMS structure with a single ~60 KB RDN of commas or plus signs controls N. Any code path that logs, pretty-prints, compares, or hashes the resulting X500Name — including JCA X509Certificate.getSubjectX500Principal().toString() and BC certificate selectors/caches — will pin a CPU core for seconds to minutes, and a few parallel submissions saturate the server.
CVE-2026-58060 — HSS public-key level count unbounded, enabling huge allocation on verify
CVSS 8.7 High · CWE-789 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 311cabbb6fce
In Bouncy Castle's HSS/LMS post-quantum signature code, HSSPublicKeyParameters.getInstance() reads the level count L from the encoded public key with no upper bound, and HSSSignature.getInstance() then reads lminus from the signature, checks it only against attacker-controlled L-1, and allocates new LMSSignedPubKey[lminus]. RFC 8554 caps HSS depth at 8, but that check is applied only in key generation, not on the parse/verify path. The code is reachable from untrusted SubjectPublicKeyInfo via PublicKeyFactory.LMSConverter, the JCA LMSKeyFactorySpi/LMSSignatureSpi, and the CMS RFC 9708 BcHssLmsContentVerifierProviderBuilder. An attacker who supplies both the HSS public key and signature can force a ~17 GB array allocation, causing OutOfMemoryError and denial of service on the verifier.
CVE-2026-58061 — CCM-family modes write plaintext to caller buffer before tag check
CVSS 8.7 High · CWE-354 · fixed in BC 1.85, BC-LTS 2.73.12 · fix cd4a5ab3ad61
CCMBlockCipher.processPacket() (and the equivalent paths in KCCMBlockCipher and KGCMBlockCipher) decrypts ciphertext directly into the caller-supplied output array and only then checks the authentication tag. When the tag check fails, InvalidCipherTextException is thrown but the output buffer already holds the full unverified plaintext and is never zeroed; the JCE BaseBlockCipher doFinal wrapper does not wipe it either. An attacker who can submit forged ciphertexts and observe the output buffer on the failure path (via pooled-buffer reuse, logging, or memory inspection) obtains AES-CTR(key, nonce) XOR attacker-ciphertext, effectively turning CCM into an unauthenticated CTR decryption oracle. Because these modes buffer the full ciphertext internally, there is no API reason to emit plaintext before verification — GCMSIVBlockCipher shows the correct decrypt-to-private-buffer-then-copy pattern.
CVE-2026-59639 — CMS verifySignatures returns true for SignedData with zero signers
CVSS 8.7 High · CWE-347 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 99ddc6dcc678
In Bouncy Castle's CMS implementation, CMSSignedData.verifySignatures(SignerInformationVerifierProvider, boolean) loops over getSignerInfos().getSigners() and returns true after the loop completes. RFC 5652 permits a 'degenerate' SignedData whose signerInfos SET is empty, and neither the ASN.1 layer nor SignerInformationStore rejects an empty collection. An attacker can therefore submit a CMS/PKCS#7 blob with arbitrary eContent and no signers, and verifySignatures() will return true. Applications that treat this return value as proof of authenticity, without separately asserting that an expected signer is present, will accept completely unsigned attacker-controlled content.
CVE-2026-59640 — OpenPGP CFB quick-check oracle active on symmetric/session-key paths
CVSS 8.7 High · CWE-203 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 6b94b1c146ce
In Bouncy Castle's OpenPGP implementation, PGPSymmetricKeyEncryptedData.getDataStream() (reached via PGPPBEEncryptedData, PGPSessionKeyEncryptedData, and the high-level OpenPGPMessageProcessor even for PKESK-unwrapped keys) still performs the CFB 'quick check' on the two repeated IV bytes and throws PGPDataValidationException before the MDC is verified. PGPPublicKeyEncryptedData already skips this check to avoid the Mister-Zuccherato oracle, but the symmetric/session-key path does not, and no system property gates it. An attacker with adaptive chosen-ciphertext access to a decryption endpoint can distinguish the early quick-check failure from later MDC/parse failures. This oracle lets them recover two plaintext bytes per roughly 2^15 queries against SEIPDv1 messages.
CVE-2026-59641 — S/MIME validator trusts signer-asserted signingTime for path validation
CVSS 8.7 High · CWE-345 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 2f81b22d559b
In Bouncy Castle's S/MIME validator, getSignatureTime() returns the CMS signingTime signed attribute and validateSignatures() passes it to PKIXCertPathReviewer via usedParameters.setDate(). Because this timestamp is chosen by the signer, certificate expiry and CRL revocation checks are performed at an attacker-selected instant. If that instant precedes the revocation date, the reviewer emits only a revokedAfterValidation notification rather than an error, so isValidCertPath() and ValidationResult.isValidSignature() both return true. No RFC 3161 timestamp token is required and the asserted time is never compared against the current time, so an attacker holding an expired or revoked key can back-date signatures and have them accepted.
CVE-2026-59642 — CMS AuthenticatedData content not bound to MAC when authAttrs present
CVSS 8.7 High · CWE-354 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 2117f316a5a4
In RecipientInformation.getContentStream(), the content is tee'd through an unkeyed digest calculator, and getMac() then MACs only DER(authAttrs). RFC 5652 §9.3 requires the recipient to compare the computed content digest against the messageDigest attribute inside authAttrs, but the library never does this automatically — getContentDigest() merely exposes the value. BC's own test suite and javadoc example compare only ad.getMac() to recipient.getMac(), so callers following the documented pattern never bind the content to the MAC. An attacker who can modify the message in transit can swap encapContentInfo.eContent for arbitrary bytes while leaving authAttrs and mac untouched, and verification still succeeds.
CVE-2026-59643 — OpenPGP inline-signature policy failures silently ignored
CVSS 8.7 High · CWE-347 · fixed in BC 1.85 · fix d3f8cc408b4a
In OpenPGPMessageInputStream.OnePassSignatures.verify(), each signature is passed to dataSignature.sanitize(key, policy) to enforce OpenPGPPolicy rules (reject MD5/SHA-1/RIPEMD160 hashes, unacceptable keys, unknown critical subpackets, pre-dated signatures). The surrounding try/catch catches PGPSignatureException but the catch body contains only a '// continue' comment with no actual continue statement, so execution falls through to dataSignature.verify(ops). If cryptographic verification succeeds, the signature is added to results with isTested=true/isCorrect=true. An attacker who can obtain or collision-forge a trusted-key signature using a policy-rejected hash can have it accepted via the isTestedCorrect()/getSignatures() API.
CVE-2026-59644 — MLS hash-ratchet honours arbitrary 32-bit generation counter from sender
CVSS 8.7 High · CWE-834 · fixed in BC 1.85 · fix 610d8757d855
In Bouncy Castle's MLS implementation, GroupKeySet.HashRatchet.get(int generation) loops 'while (nextGeneration < generation) next();' and caches every intermediate KeyGeneration in an unbounded HashMap. The generation value comes directly from the SenderData of an incoming PrivateMessage, a 32-bit integer fully controlled by the sender and encrypted only under the group-shared sender_data_secret, so any authenticated member can set it. PrivateMessage.unprotect() passes this value to keys.get() after only a hasLeaf() check and before any per-message signature verification. A single malicious member can thus send generation = 0x7FFFFFFF and force every other member into ~2 billion HKDF iterations and multi-gigabyte cache growth.
CVE-2026-59645 — OER parser recurses without depth limit on self-referential IEEE 1609.2 schema
CVSS 8.7 High · CWE-674 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 822b2478b131
Bouncy Castle's OERInputStream.parse(Element) walks SEQ/CHOICE/Supplier children recursively with no depth counter. The bundled IEEE 1609.2 schema defines a cycle Ieee1609Dot2Data -> Ieee1609Dot2Content(signedData) -> SignedData -> ToBeSignedData -> SignedDataPayload -> Ieee1609Dot2Data via deferred(...).mayRecurse(true), and the mayRecurse flag is only checked by test tooling, not the runtime parser. Public entry points ETSISignedData(byte[]) and ETSIEncryptedData(byte[]) invoke this parser on untrusted bytes before any signature verification. An attacker can craft a ~2-4 KB message with hundreds of nested signedData payloads; each cycle costs ~4-5 input bytes but ~5 Java stack frames, so parsing throws StackOverflowError and crashes the thread/JVM.
CVE-2026-59646 — DTLS handshake reassembler allocates buffer from unchecked 24-bit length
CVSS 8.7 High · CWE-789 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 2ea38942c791
In Bouncy Castle's DTLS stack, DTLSReliableHandshake.processRecord() reads the 24-bit handshake 'length' field from incoming records and constructs a new DTLSReassembler for each in-window message_seq, whose constructor immediately allocates a byte[length] buffer. Unlike the stream-TLS path, no check against peer.getMaxHandshakeMessageSize() is applied. Because up to 16 message_seq values may be buffered per connection, a single ~205-byte epoch-0 record containing 16 fragment headers with length=0xFFFFFF and fragment_length=0 forces ~256MiB of allocation before any authentication. An unauthenticated remote attacker can drive the JVM to OutOfMemoryError with a handful of tiny UDP datagrams.
CVE-2026-59649 — OpenPGP user-attribute subpacket length bounded only by JVM max memory
CVSS 8.7 High · CWE-789 · fixed in BC 1.85, BC-LTS 2.73.12 · fix a43c40dc12c3
UserAttributeSubpacketInputStream.readPacket() reads an attacker-controlled 5-octet subpacket length (up to Integer.MAX_VALUE) and allocates new byte[bodyLen-1] after checking only against 'limit'. Because UserAttributePacket constructs the stream with the single-arg constructor, 'limit' is computed by StreamUtil.findLimit(in), which for the wrapped BCPGInputStream/BufferedInputStream/PartialInputStream falls through to Runtime.getRuntime().maxMemory(). The actual enclosing packet body length known to PartialInputStream is never consulted. An attacker who can get a victim to import a public key (keyserver, WKD, attachment) can therefore force allocation of a byte array near the JVM heap size from a few dozen bytes of input, throwing OutOfMemoryError before readFully notices the short stream.
CVE-2026-12185 — BKS/UBER keystore allocates from untrusted lengths before integrity check
CVSS 7.1 High · CWE-789 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 7bbd7fe5f441
BcKeyStoreSpi.engineLoad() calls loadStore() to parse all keystore entries before validating the trailing HMAC. During parsing, multiple allocations — the certificate chain array (line 722), encoded certificate buffer (line 366), encoded key buffer (line 425), entry byte buffer (line 743), and the PBE salt (line 837) — are sized directly from attacker-controlled readInt() values with no upper bound; the UBER variant similarly allocates the salt before checking its size. An attacker who can supply or tamper with a BKS/UBER keystore file can set any of these length fields to 0x7FFFFFFF, causing the JVM to attempt a ~2 GiB allocation and throw OutOfMemoryError before the integrity check would reject the file. The result is a denial of service in the process that loads the keystore.
CVE-2026-59651 — BKS keystore accepts legacy version with 16-bit integrity MAC key
CVSS 7.1 High · CWE-326 · fixed in BC 1.85, BC-LTS 2.73.12 · fix faf5daa6e9b8
BcKeyStoreSpi.engineLoad() reads the keystore version from the file and accepts 0, 1, or 2. For versions other than 2 it calls generateDerivedMacParameters(hMac.getMacSize()), but getMacSize() returns bytes (20 for SHA-1) and PKCS12ParametersGenerator divides by 8, yielding a 2-byte (16-bit) MAC key. The default BKS type (BcKeyStoreSpi$Std) does not override engineLoad, and the org.bouncycastle.bks.enable_v1 property only gates the separate BKS-V1 alias, not this load path. Because the version integer is attacker-controlled, anyone who can supply or tamper with a .bks file can force the weak-key branch, brute-force the 2^16 keys offline, and produce a valid MAC over a modified store (e.g., injecting a rogue trusted CA).
Medium
CVE-2026-59647 — CRMF/CMP password-MAC honours unbounded iteration count
CVSS 6.9 Medium · CWE-770 · fixed in BC 1.85, BC-LTS 2.73.12 · fix c99d6427d681
PKMACBuilder's default constructor leaves maxIterations = 0, which disables the iteration-count ceiling check. When a CA/RA verifies a PBM-protected CMP/CRMF message via PKMACValueVerifier.isValid() or ProtectedPKIMessage.verify(), the iteration count is taken directly from the attacker's PBMParameter and fed into a do { K = digest(K); } while (--iter > 0); loop. An attacker can set iterationCount to 2^31-1 and tie up a server thread for hours per request; a handful of requests exhaust the thread pool. The same unbounded-iteration pattern exists for CMS PasswordRecipientInfo in EnvelopedDataHelper.java and BcPasswordRecipient.java, where the PBKDF2 iteration count is attacker-controlled.
CVE-2026-59648 — OpenPGP Argon2 S2K honours attacker-chosen memory and passes
CVSS 6.9 Medium · CWE-770 · fixed in BC 1.85, BC-LTS 2.73.12 · fix c915cc3f7a8d
When parsing S2K type 4 (Argon2, RFC 9580 §3.7.1.4) from an OpenPGP packet, S2K.Argon2Params reads the one-byte passes, parallelism, and memorySizeExponent fields with only a default cap of memorySizeExponent ≤ 30 (1 TiB). PGPUtil.makeKeyFromPassPhrase feeds these directly into Argon2BytesGenerator via .withMemoryAsKB(1 << memExp).withIterations(passes).withParallelism(parallelism). An attacker controls all three parameters in a tiny SKESK or secret-key packet. A victim who merely enters a passphrase to attempt decryption triggers an attacker-chosen memory allocation (up to ~1 TiB) and CPU work before any authenticity check can occur, yielding OOM or a long hang from a few dozen bytes of input.
CVE-2026-59652 — LDAP filter injection in legacy jdk1.4 LDAPStoreHelper
CVSS 6.9 Medium · CWE-90 · fixed in BC 1.85 · fix 27c468af54ee
The CVE-2023-33201 / CVE-2026-0636 fix added filterEncode() escaping to the main-java X509LDAPCertStoreSpi, main-java LDAPStoreHelper, and jdk1.4 X509LDAPCertStoreSpi, but missed the jdk1.4 LDAPStoreHelper. Its parseDN() returns the raw DN component, which callers concatenate directly into '(' + attr + '=' + value + ')' and pass to DirContext.search(). Because ant/jdk14.xml overwrites the patched main-java file with this variant when building bcprov-jdk14, the advertised fix never reaches the jdk14 distribution. An attacker who can present a certificate with a crafted Subject/Issuer CN (e.g.
CVE-2026-13586 — PKCS#12 MAC and bag-decryption KDF iteration-count bound (DoS)
CVSS 5.3 Medium · CWE-770 · fixed in BC 1.85, BC-LTS 2.73.12 · fix fa59cc23502f
In Bouncy Castle for Java before 1.85, PKCS#12 MAC and bag-decryption KDF iteration-count bound (DoS). This issue also affects Bouncy Castle for Java LTS before 2.73.12.
CVE-2026-15055 — PKCS#8 / PBES2 decryptors honour unbounded KDF cost from input
CVSS 5.3 Medium · CWE-770 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 7ab4ee67a013
JcePKCSPBEInputDecryptorProviderBuilder.build(password).get(algId) parses PBES2Parameters from an attacker-supplied EncryptedPrivateKeyInfo/PKCS#12/CMS structure and passes the scrypt N/r and PBKDF2 iteration count straight into key derivation (lines 128, 139/143, 184). The same unbounded pattern exists in JceOpenSSLPKCS8DecryptorProviderBuilder, CMS PasswordRecipient handling, and PKCS12PfxPdu MAC verification. Because the KDF executes before any integrity or authenticity check, an attacker who can submit such a structure can force ≈128·N·r bytes of scrypt memory or up to 2^31−1 PBKDF2 HMAC iterations. The result is JVM OOM or a worker thread pinned for hours from a tiny input file.
CVE-2026-58063 — BCFKS keystore load honours unbounded KDF cost from untrusted file
CVSS 5.3 Medium · CWE-770 · fixed in BC 1.85, BC-LTS 2.73.12 · fix 81737a56ef44
In BcFKSKeyStoreSpi.engineLoad(), the integrity-check structure is parsed from the untrusted keystore bytes and hmacPkbdAlgorithm is set from it before verifyMac() runs. verifyMac() → calculateMac() → generateKey() then feeds the file-supplied KDF parameters directly into PBKDF2 (iterationCount at lines 876/884) or scrypt (N/r at lines 855-857) with no upper bound; PKCS5S2ParametersGenerator only rejects c==0 and SCrypt.generate() enforces no cost cap. An attacker who controls or can modify a .bcfks file can set iterationCount = 2^31-1 or choose scrypt N/r so 128Nr approaches available RAM. The JVM then spends minutes-to-hours of CPU or allocates gigabytes before the MAC mismatch is ever detected.
Articles in this section
- Bouncy Castle Java FIPS 2.1.X release notes
- CVEs fixed in Bouncy Castle 1.85
- New releases: Bouncy Castle Java 1.84 and Bouncy Castle Java LTS 2.73.11
- New Bouncy Castle Java LTS 2.73.10 release
- New Bouncy Castle Java 1.83 release
- New Bouncy Castle Java FIPS 2.1.2 release and update about Bouncy Castle Java LTS 2.73.8
- New releases – Bouncy Castle Java 1.82 and Bouncy Castle Java LTS 2.73.9
- Update about release – Bouncy Castle Java LTS 2.73.8
- Update about release – Bouncy Castle Java FIPS 2.1.1
- New Release: Bouncy Castle Java FIPS 2.0.1
Add comment
Article is closed for comments.