The Bouncy Castle Crypto package for Java LTS is a long term support distribution of the Bouncy
Castle Crypto APIs. It offers the same lightweight API, JCA/JCE provider, JSSE provider, CMS/PKIX
and OpenPGP APIs as the regular Java distribution, with the addition of optional native (JNI)
acceleration for AES modes, SHA-2/SHA-3, GCM-SIV and DRBG on x86_64 (AVX, VAES, VAESF) and ARM64
(NEON-LE). The jars are named bc{module}-lts8on-<version>.jar and target Java 8 and later.
This is not the FIPS distribution; release notes for that are carried with the BC-FJA distributions.
Release: 2.73.14
Date: 2026, TBD.
Lightweight API - conformance.
- ML-KEM matrix generation (FIPS 203 Algorithm 7, SampleNTT) squeezed two SHAKE128 blocks for each additional block it needed after the first three, but sampled from only the first of them, so a matrix entry needing more than one additional block was sampled from the wrong part of the XOF stream and differed from the one FIPS 203 specifies. The chance of an entry needing that is about 10^-32, so no key, ciphertext or test vector is known to be affected; each additional block is now squeezed singly, as the standard and the reference implementation do.
Lightweight API - malformed input, bounds and state.
-
Grain-128AEAD returned corrupted plaintext from a decryption driven in chunks. The AEAD stream cipher data operator splits a
processBytes()call that spans the buffered authentication tag into two output segments, and wrote the second at the caller's output offset instead of after the first, so it overwrote the head of the first and the tail of the reported output was never written. The call still returned the full byte count and the tag still verified. The second segment is now written after the first. -
The same operator wrote its output without first checking that the caller's buffer was long enough, so a short output buffer surfaced as an
ArrayIndexOutOfBoundsExceptionfrom inside the engine rather than theOutputLengthExceptionevery other AEAD engine reports. Both directions ofprocessBytes(), andprocessByte(), now check before anything is written or buffered. -
The key factories cast fields of a PrivateKeyInfo or SubjectPublicKeyInfo to the ASN.1 type they expected, or used algorithm parameters without checking they were present, so a malformed DH, ElGamal, DSA, EC or GOST R 34.10 key gave a
ClassCastExceptionorNullPointerException, andECNamedDomainParameters.lookup()built parameters from no curve for an OID it did not know. They now go throughgetInstanceand refuse absent parameters, and an unknown curve, with anIllegalArgumentException; an EC private key whose algorithm carries no parameters takes its curve from the ECPrivateKey, as RFC 5915 allows. -
PrivateKeyInfo and the CMS SignerInfo, the CMP PKIMessage and PKIHeader refused a sequence shorter than their required fields only by failing with a
NoSuchElementException, PrivateKeyInfo a version too large for an int with anArithmeticException, and an OCSP response with a status too large for an int was accepted and then failed ingetStatus(); each is now refused when parsed with anIllegalArgumentException, which OCSPResp and GeneralPKIMessage report as theIOExceptionthey declare.RSAKeyParametersrefuses a negative modulus, which passed the parity check and failed in the small factor search, and the lightweightDSASignerrefuses a key without domain parameters with anIllegalArgumentExceptionrather than aNullPointerException. -
A GeneralizedTime read without its minutes or seconds and with a fraction of the hour or minute, as X.680 allows -
9050010112.100Z, say - was written as DER with the missing digits appended after the fraction, which is not DER and grew each time the value was re-encoded. The fraction is now carried down into minutes and seconds (90500101120600Zhere), and a ',' decimal separator becomes the '.' DER requires. -
Both ML-DSA and ML-KEM private key factories decided between the raw and the DER-wrapped pre-RFC forms by whether the content parsed, and a raw expanded key is random bytes, so about one in 2^31 begins with bytes that read as a DER header covering the rest. Such a key was refused, or - through the
org.bouncycastle.pqc.crypto.mldsakey class, which sliced and padded an encoding of any length - loaded as a different key. The raw forms are now recognised by their length before anything is parsed, and that key class refuses an encoding that is neither a seed nor an expanded key. -
Ported from the BC 1.87 development line: the Base64, Base32 and Hex decoders no longer read past the range they are given, fail a truncated final Base64 block and refuse non-ASCII input;
ISO9796d2PSSSignercompares the message passed in with the one recovered from the signature, and the X9.31 and ISO 9796-2 signers reset on every failed verification;HMacDSAKCalculatorclears its copies of the private scalar and candidate nonce;CipherInputStream.mark()andreset()keep all of the read-ahead state andCipherOutputStream.close()is idempotent; theAEADBaseEnginein-place overlap check counts buffered bytes; DSTU 7624 KGCM, KGMac, KCCM andDSTU7624Macrefuse the inputs DSTU 7624:2014 excludes and KGCM re-initialises from a clean state; the GOST wrap engines takeParametersWithUKMoutsideParametersWithRandomandCryptoProWrapEnginediversifies a copy of the KEK; the TupleHash length prefix is computed in long arithmetic;Blake2bDigestandBlake2sDigestread their parameter block correctly and keep the tree parameters across copies andreset();KMAC.reset(Memoable)copies the key; Argon2 returns every block to its pool zeroised;DESedeParameters.isWeakKeychecks every part of a key at a non-zero offset;ECPoint.hashCode()hashes coordinate values, so equal points hash equally;CryptoServicesRegistrarno longer copies a global property onto the calling thread. -
CCM decryption, in the java and native CCM and in both packet ciphers, applied the payload length limit to the input including the tag, so the largest payloads that encrypt were refused on decryption; the limit now applies to the payload, and the B0 length field is exactly q bytes.
-
The ML-DSA public key (rho, t1) constructor and the ML-DSA and SLH-DSA private key component constructors reject components of the wrong length; an ML-DSA private key given a public key compares rho as well as t1 with it; and an expanded ML-DSA private key is rejected when its tr does not match the hash of its public key.
-
The LMS private key reader bounded the master secret by
InputStream.available(), which refused keys read from streams reporting nothing available; it is now bounded byorg.bouncycastle.lms.max_seed_length(default 1024). -
ASN1Enumeratednow accepts negative values, an ENUMERATED being an integer (X.680 sec. 20). -
ASN.1, from the BC 1.87 development line: the SEQUENCE parsers of the X.509, PKCS, CMS, CRMF, CMP, ESF, TSP, OCSP, EAC, DVCS, CMC, ESS, X9, GOST and qualified-certificate types take each optional field at most once, in definition order and with its tag checked, and refuse elements after the last field;
ASN1InputStream, the streaming ASN.1 parsers and the streaming CMS and time-stamped data parsers report malformed content as the exception they declare; a UTCTime or GeneralizedTime day is checked against the length of its month; and a definite-length read no longer allocates the full length from the length field before any content arrives. -
The first DER encoding of an
ASN1Setnot built in DER order cached its copy of the elements before sorting it, so another thread encoding the same SET, or comparing it withequals(), at the same time could see the copy unsorted or part way through the sort and produce a wrong DER encoding - for example of a CMS SignerInfo's signed attributes when two threads verified the sameSignerInformationat once. The copy is now cached only once it is sorted, and theASN1Setarray constructor keeps its own copy of the elements it is given. -
JceKEKAuthEnvelopedRecipient,JceKeyAgreeAuthEnvelopedRecipientandJcePasswordAuthEnvelopedRecipienthave been added: AuthEnvelopedData with authenticated attributes could not be read through the KEK, key agreement or password recipients, the enveloped-data recipients failing with aNullPointerExceptionon the missing AAD stream. -
ECPoint.multiplythrew aNullPointerExceptionfrom the WNAF precomputation for an unvalidated point of small odd order on an Fp curve in Jacobian coordinates, such as (0, 2) on BLS12-381 G1, whose table of odd multiples reaches the point at infinity:ECCurve.normalizeAllnow skips it, andWNafUtilrebuilds such a table on the curve itself rather than on the isomorphic curve, where a doubling would also have used the wrong curve coefficient. Validated points are unaffected. -
DH and ElGamal key pair generation: a private value length equal to the bit length of p is accepted again and gives an l-bit private value, as PKCS#3 sec. 7.1 specifies; a private value whose public value would fall outside [2, p - 2] is redrawn; and the private value length must be at least twice the security strength of p (SP 800-56A Appendix D: at least 304, 352 and 400 bits for 4096, 6144 and 8192-bit p), including for the DHIES ephemeral key. A shorter length is now refused; set
org.bouncycastle.dh.allow_short_private_valuetotrueto allow it. -
The AES and DES key schedules of
AESEngine,AESLightEngine,DESEngineandDESedeEngine, and the working state of the SP 800-90ACTRSP800DRBG,HashSP800DRBGandHMacSP800DRBG, are now zeroized once their owner is no longer referenced and has been collected, as well as when an engine is re-keyed: throughfinalize()on Java 8 and throughjava.lang.ref.Cleaner, from the multi-release jar, on Java 9 and later. As with the native code, material belonging to objects still in use at JVM exit is not cleared. -
The lightweight stream ciphers and stream modes (CTR/SIC, CFB, OFB, GOFB, GCFB, KCTR, the GOST R 34.13 CTR, CFB and OFB modes, Salsa20, ChaCha, RC4, HC-128, HC-256, ISAAC, VMPC, Grain and ZUC) gave wrong output from
processBytes()when the output was in the same array as the input and started after it while overlapping it, as the output overwrote input not yet read. The input is now moved first; output at or before the input, the buffered ciphers and the AEAD modes were unaffected (bc-java 21b9dfd7b0).
Provider (prov).
-
The raw JCA provider bounded the PBKDF2 iteration count taken from an encoding (
org.bouncycastle.pbe.max_iteration_count, default 10,000,000) but not the counts of the legacy PBES1 (PKCS#5 scheme 1) and PKCS#12 PBE families beside it. TheirAlgorithmParameters(PKCS12PBEand its OID aliases,PBKDF1) accepted any count, narrowing one beyond theintrange withintValue()so that 2^32 arrived as 0, and everyCipher,MacandSecretKeyFactoryderivation ran with whatever count it was given - including a count decoded by another provider'sAlgorithmParameters, as whenjavax.crypto.EncryptedPrivateKeyInfo.getKeySpec()decrypts a PKCS#12 PBE-protected key with BC. As these schemes carry the count in unauthenticated parameters and derive before anything can be checked, a supplied blob could hold a derivation for tens of minutes. The parameter parse now rejects a negative, beyond-int or over-limit count, and the derivations reject a negative or over-limit count, under the same property as PBKDF2. The PKCS#12 key store derives through the same code, so anorg.bouncycastle.pkcs12.max_it_countraised above 10,000,000 now needsorg.bouncycastle.pbe.max_iteration_countraised with it. -
The PKCS#12 key stores wrote the MAC key-derivation parameters of a file they had loaded into every file they wrote afterwards, under whatever password the caller stored with. For PKCS12-PBMAC1 that carried the loaded file's PBKDF2 salt, iteration count, key length and PRF, and one store reused a single PBKDF2 salt across every write, including writes under different passwords, with no file loaded at all; the classic store inherited the MAC salt length and digest algorithm the same way. The values were also latched before the MAC was verified. The PBKDF2 salt and the MAC salt are now generated for every write, the MAC salt at no fewer than 8 octets, and nothing is latched until the file has verified. A loaded file's PBKDF2 count is kept only where it is at least the count being written with, and raised to it otherwise; that count is the new
org.bouncycastle.pkcs12.pbkdf2_it_count, default 65,536. Reading is unaffected. -
A DSA public key without domain parameters, as a certificate may carry when it inherits them from its issuer (RFC 3279 sec. 2.3.2), made
BCDSAPublicKey.toString(), and soX509Certificate.toString(), throw aNullPointerException, and the DSA Signature classes threw one when initialised with such a key or with a null key. The key now prints by its y value alone, and initialisation refuses either with anInvalidKeyException. -
Ported from the BC 1.87 development line: the KeyAgreement services report the
IllegalStateExceptionjavax.crypto.KeyAgreementspecifies when a secret is asked for beforedoPhase, report an initialisation they cannot carry out as a parameter error, refuse aUserKeyingMaterialSpecsalt unless the KDF is HKDF, and the X25519/X448 agreements take the unified flag and curve from their own name rather than theEMULATE_ORACLEname;ConcatenationKDFGeneratortreats a missing OtherInfo as empty; the KTS Ciphers check aKTSParameterSpec's KDF at init. -
The key stores: JKS refuses a store shorter than its checksum with an
IOException, BCFKSisKeyEntry()reports PBE key entries, the BC stores empty themselves on a load with a null stream, and PKCS#12 generates the MAC salt only when a MAC is written, counts a SunJCE secret key bag as protected content, resets the MAC defaults when a file without a MAC is loaded, gives secret keys read from JDK-written files their JCA names, and writes 300,000 PBE iterations by default (600,000 for the MAC). -
The GOST R 34.10 Signatures verify only a signature of exactly the encoded length; the EC services report a key whose domain parameters are not a valid curve with
InvalidKeyException; the EC IES ciphers givegetKeySizefor any EC keyinitaccepts; the stream and wrap ciphers refuse anAEADParameterSpec; ML-KEM, ML-DSA and SLH-DSA keys from other providers are converted via their encodings, including throughjavax.crypto.KEM; the composite ML-DSA Signatures discard the buffered message on everyinitSignandinitVerify; certificate signature algorithm names no longer come from a non-BouncyCastle provider registered as "BC". -
CRL.isRevoked()no longer throwsClassCastExceptionfor an "X.509" certificate that is not anX509Certificate. -
The PKIX CertPathValidator did not fall back to CRLs when OCSP left a certificate's status undetermined: an OCSP response with a status other than successful (malformedRequest, internalError, tryLater, sigRequired, unauthorized), from a responder or supplied by the caller, and a certificate status of unknown, each failed validation outright. Each is now recoverable, so CRL checking takes over unless NO_FALLBACK is set; where the CRLs do not settle the status either, the OCSP failure is the one reported, with the CRL failure attached as a suppressed exception. An unknown status is now reported as "certificate status unknown" rather than "certificate revoked, details unknown" (github bc-lts-java #12).
CMS, PKIX and EST (pkix).
-
The RFC 9709 content-encryption
AlgorithmIdentifier, which carries the real algorithm inside the parameters of an outer id-alg-cek-hkdf-sha256, was unwrapped at only some of the points where a CMS recipient makes a decision about it. Key-size validation in the KEK, RSA-KTS and KEM recipients, and in the key-transport recipient's ORI-KEM branch, compared the recovered key against the outer identifier, which registers no key size, sosetKeySizeValidation(true)silently checked nothing there; thesetAllowedContentAlgorithmsallow-list and thesetMinimumTagSizefloor were applied to the outer identifier on every recipient. The inner algorithm is now resolved first at each of these points. -
A CMS message naming the RFC 9709 key derivation but carrying no readable inner content-encryption
AlgorithmIdentifierwas reported as aNullPointerException, or as anIllegalArgumentExceptionfrom the ASN.1 decoder, out of methods declared to throwCMSException. The places that resolve the wrapper now share one resolver, which reports an absent or unreadable inner algorithm as aCMSException. -
The CMS RFC 8418 key agreement schemes (dhSinglePass-stdDH-hkdf-sha256/384/512, used with X25519 and X448) put the user keying material in the entityUInfo of the ECC-CMS-SharedInfo but never used it as the HKDF salt, which RFC 8418 sec. 2.2 also requires, so a message carrying a ukm did not interoperate with a conforming implementation in either direction. The ukm is now passed as the salt as well, on both the generating and the receiving side. A message with a ukm written by 2.73.11 to 2.73.13 is not readable by this release and vice versa; messages without a ukm, and the X9.63-KDF key agreement schemes, are unaffected.
-
Building an evidence record was cubic in the number of data objects:
SortedHashListandSortedIndexedHashListfound each insertion point by walking aLinkedList. Each now sorts its hashes once, stably, so the order of the leaves and every root hash are unchanged; generating a time-stamp request over 8,000 data objects goes from about 210 seconds to under a tenth of a second. AnERSDataGroupnow takes its hash from theERSCachingDatacache rather than recomputing it on every request, andERSArchiveTimeStampGeneratorbuilds its reduced hash tree once rather than on every call. -
The DANE SMIMEA support in
org.bouncycastle.cert.danehad three defects.JndiDANEFetcherFactory, when asked for every entry under a bare domain name, removed the first and last characters of the owner name the JNDI DNS provider reports for each listed record, assuming a quoted name; the provider returns the name unquoted with a trailing root dot, so the first hex digit of the hash label was lost and the lookup that followed could never find the record. It now removes only the root dot.DANEEntryStorekept one entry per owner name, so an address with more than one SMIMEA record (a signing and an encryption certificate, say) only ever showed its last; every entry is now kept.DANEEntrySelectorFactorysplit an address at its first '@', mis-handling a quoted local-part containing one, and threwStringIndexOutOfBoundsExceptionrather thanDANEExceptionfor an address with none; it now splits at the last '@', removes enclosing quotes and backslash quoting from the local-part before hashing as RFC 8162 sec. 3 describes, and rejects an address with no '@'. TheDANEEntry(String, byte[])constructor also now rejects, with anIOException, a record whose flags do not describe a full certificate entry rather than parsing whatever follows the flags as a certificate. -
SignerInformation.verify(), and theisSignatureValid()methods of X509CertificateHolder (including the alternative signature), X509CRLHolder, X509AttributeCertificateHolder and PKCS10CertificationRequest and the signature checks of CertificateRequestMessage, ProtectedPKIMessage and SignedPublicKeyAndChallenge, let an uncheckedRuntimeOperatorExceptionescape when a DSA or ECDSA signature did not decode; each now reports it as the checked exception it declares, with the operator exception as the cause.JcaContentVerifierProviderBuilder, given a certificate whose key algorithm the provider does not recognise, handed a null key to the Signature; it now refuses with anOperatorCreationException. -
A CMS AuthenticatedData message using the des-ede3-cbc or rc2-cbc CBC-MAC without authenticated attributes started the MAC from the IV in the macAlgorithm parameters, which the MAC does not cover when its input is the content alone (RFC 5652 sec. 9.2), so a change to the first eight content bytes could be offset by one to the IV.
JceCMSMacCalculatorBuildernow writes these MACs with a zero IV, which earlier releases also verify, and the generators,CMSAuthenticatedData,CMSAuthenticatedDataParserandRecipientInformation.getMac()refuse a non-zero IV for a message without authenticated attributes. Messages in that form written by earlier releases carry a random IV and can be read by settingorg.bouncycastle.cms.allow_unauthenticated_cbc_mac_ivto true; messages with authenticated attributes are unaffected. The javadoc examples now use HMAC-SHA256. -
RecipientInformation.getMac()did not check the content-type authenticated attribute of a CMS AuthenticatedData message, so a message whose authenticated attributes had no content-type attribute, or one not matching the eContentType, was accepted. RFC 5652 sec. 9.2 and 9.3 require the attribute whenever authenticated attributes are present, with a value matching the eContentType;getMac()now enforces both, asSignerInformationalready does for SignedData. -
PKIXCertPathReviewerlet a delta CRL decide the status of a certificate. A delta CRL lists only the changes since its base CRL, so a certificate revoked on the base was reported as not revoked when the CRLs the reviewer was given held a current delta and only an expired base, or earlier deltas and no base at all.SignedMailValidatorgives the reviewer the CRLs carried in the signed message. As inCertPathValidator, only a complete CRL now decides the status of a certificate, whether found in the CertStores or fetched from a CRL distribution point; where no current complete CRL is available the reviewer reports that rather than a status. -
PKIXCertPathReviewerdid not refuse a CRL carrying a critical extension other than the issuing distribution point and the delta CRL indicator, as RFC 5280 sec. 5.2 requires andCertPathValidatordoes, and looked up a certificate on an indirect CRL by serial number alone. Such a CRL is now refused, and the entry is matched on the certificate issuer as well. -
The certificate path validators looked a certificate up in an indirect CRL by serial number alone and then compared the issuer of whichever entry came first, so where two issuers had each revoked the same serial number a certificate whose own entry followed the other issuer's was reported as not revoked. The provider's PKIX validator and the pkix
X509RevocationCheckernow check every entry carrying the serial number against the issuer it applies to (RFC 5280 sec. 5.3.3), and the provider'sX509CRLimplementsgetRevokedCertificate(X509Certificate)the same way.CRL.isRevoked()on the provider's CRLs stopped at the first entry carrying the serial number and now does the same. The validators also look beyond the JDK provider'sgetRevokedCertificate(BigInteger), which sees only the CRL issuer's entries, whenever that lookup cannot be conclusive (github #2464). -
JcaContentVerifierProviderBuilderand the BC provider'sX509CertificateandX509CRLverify()no longer verify legacy generic composite signatures (id_alg_composite), whose creation was removed earlier; such a signature is now refused with "legacy composite signature algorithm not supported". The standardised composite ML-DSA signatures are unaffected. -
Ported from the BC 1.87 development line, certification path processing: an empty DNS, rfc822 host or URI host label is refused under a name constraint of that type; the validators apply the RFC 5280 sec. 6.3.3 distribution point name match and reasons intersection, count only non-self-issued intermediates against the maximum path length, apply a TrustAnchor's name constraints, keep a CRL entry's reasonCode apart from the revocation status and report
BasicReason.REVOKED;PKIXCertPathReviewerapplies the CRL scope rules, rfc822Name constraints to subject DN e-mail addresses and TrustAnchor name constraints, and fetches from CRL distribution points only whenorg.bouncycastle.x509.enableCRLDPis set and no error is recorded above the certificate;BasicConstraintsValidation,CertificatePoliciesValidationandCRLValidationincert.pathcheck pathLenConstraint, policy extensions and CRL currency correctly;LDAPUtils.parseDNmatches the attribute only as an RDN type and decodes DN escapes before filter encoding. -
OCSP:
OCSPRespandOCSPReqreport a malformed encoding asCertIOException, the OCSP ASN.1 types refuse a short SEQUENCE, andorg.bouncycastle.ocsp.max_response_agebounds a response with no nextUpdate (default one hour). -
CMS and TSP: the RSA-KTS key is derived to the length the wrapping algorithm fixes; 1-pass ECMQV uses a fresh ephemeral key pair per recipient and feeds the KDF the DER-encoded ECC-CMS-SharedInfo (RFC 5753), keeping the raw-ukm form for earlier messages; the CMS decoders and streaming parsers reject short or malformed content with their declared exceptions; the algorithm protection attribute is generated from the algorithms actually used;
CMSAuthenticatedDataParserreturns its unauthenticated attributes;TimeStampTokenrejects a token whose signing-time attribute disagrees with its genTime, unlessorg.bouncycastle.tsp.allow_inconsistent_signing_timeis set. -
CRMF, EST and MIME:
PKIPublicationInforejects the combinations RFC 4211 sec. 6.3 forbids,CertificateRequestMessageBuilder.build()reports bad configurations rather than throwingNullPointerException, andESTResponseandmime.Headersparse repeated and folded headers in linear time. -
PEMParserreported a malformed trusted certificate or CRL as "problem parsing cert"; it now says "problem parsing trusted certificate" or "problem parsing CRL", and a certificate "problem parsing certificate" (bc-java 7ae5ac9f5e).
TLS (tls).
- Ported from the BC 1.87 development line: a DTLS handshake no longer deadlocks when a handshake message ahead of the peer's ChangeCipherSpec (a client's CertificateVerify, for example) is lost; the RFC 7633 TLS feature checks are satisfied by the TLS 1.3 end-entity CertificateEntry, no longer enforce status_request_v2 and no longer force a positive reading of the feature values; and TLS 1.3 Certificate entry extensions must correspond to ones in the ClientHello or CertificateRequest, an unsolicited one being refused with unsupported_extension.
OpenPGP (pg).
- Decrypting an OpenPGP message in two steps - recovering the session key from a SKESK packet and
then decrypting the SEIPD v1 body through
PGPEncryptedDataList.extractSessionKeyEncryptedData()- stopped detecting a wrong passphrase once 2.73.12 suppressed the legacy CFB quick check for every session-key decryption. For a password-derived session key the check is what identifies a wrong passphrase, and a SKESK v4 packet deriving the session key from the S2K output directly yields a well-formed session key for any passphrase, so a wrong one surfaced as a parse or integrity failure further down the stream. The newextractSessionKeyEncryptedData(boolean)states whether the session key came from a password: true restores the check and thePGPDataValidationExceptionon a wrong passphrase, while the no-argument method goes on suppressing it, so a session key recovered from a public key operation is still never quick checked. PGPObjectFactory.nextObject()let anIllegalArgumentException,IllegalStateExceptionorUnsupportedPacketVersionExceptionfrom the object built from a packet escape, although it declares onlyIOException- for example for a version 4 SKESK followed by a version 2 SEIPD, an order RFC 9580 forbids. Each is now reported as anIOExceptionwith the original exception as its cause.- Ported from the BC 1.87 development line:
PGPPublicKey.join,PGPPublicKeyRing.joinandPGPSignature.joinmerge two signatures only when they differ in no more than their unhashed subpackets, so a relabelled copy of a revocation can no longer displace the genuine one, leave the rings they are given unchanged, and index what they merge so joining stays linear;PGPSecretKeyRing.insertOrReplacePublicKey()no longer drops the ring's other secret keys; text-mode signatures and cleartext signatures hash a bare CR unaltered, with the earlier CR to CRLF form still accepted on verification, andPGPCanonicalizedDataGeneratorno longer rewrites a bare CR;BCPGOutputStreamwrites a first partial body length of at least 512 octets;PGPLiteralDataandSignerUserIDdecode a name that is not valid UTF-8 with replacement characters;PGPObjectFactoryreports an unsupported packet version as anIOException; the LibrePGP encryption mode preference is read from subpacket 34; and theKeyGripCalculatorRSA keygrip now matches GnuPG's. SignedMailValidatorcompared the certificate and From addresses lower-cased in the default locale, so under a Turkish locale an address containing "I" did not match; it now lower-cases in ASCII only.
Native code.
DumpInfono longer prints the native random source information when native support is not available.- The native AES-GCM corrupted its output, failing the tag check on decryption, when
processBytes()was given the same array for input and output with the output starting after the input and overlapping it: the native code overwrote input it had yet to read. Overlapping input is now copied first, as the java GCM does. The native AES-GCM also now reports a key of the wrong length frominit()as anIllegalArgumentException, as the java GCM and native CBC do, rather than anIllegalStateException. - The native AES CTR and CFB
processBytes(), and the native AES ECB and CBCprocessBlock()andprocessBlocks(), likewise gave wrong output when the output was ahead of overlapping input in the same array (see the stream cipher entry above; the java ECB and CBC were never affected). The input is now moved first, out of range arguments still being left to the native checks.
PGPEncryptedDataList.extractSessionKeyEncryptedData(boolean)has been added; see above.- The
org.bouncycastle.pkcs12.pbkdf2_it_countproperty (Properties.PKCS12_PBKDF2_IT_COUNT) sets the PBKDF2 iteration count for a PBMAC1-protected PKCS#12 file's MAC, default 65,536. org.bouncycastle.util.Pack.longToBigEndian()now writes the eight bytes of the long directly rather than through twointToBigEndian()calls, which the JIT of JDK 25 compiles to a single byte-swapped store. On JDK 23 and laterlongToLittleEndian()does the same, from a copy ofPackthe bcprov and bccore multi-release jars carry underMETA-INF/versions/23, since JDK 23 is the first whose JIT merges those stores and the form is slower than the existing one on earlier JDKs. Packing longs big-endian measured about 5 times as fast on JDK 25 and about 10% faster on JDK 8 to 23, and little-endian about 2.2 to 2.7 times as fast on JDK 23 and 25, with SHAKE squeezing about 6% and SHA-512 about 4% faster on JDK 25 (github #2474).ChaCha20Poly1305- and with it the provider's ChaCha20-Poly1305 cipher - now hands runs of whole 64-byte blocks to ChaCha and to Poly1305 in one call each, where it used to alternate between them one block at a time and, when decrypting, copied every byte through its buffer individually. The output is unchanged (github #2481).- Ported from the BC 1.87 development line, with the output unchanged:
Poly1305absorbs whole blocks straight from the input, 1.3 to 1.5 times as fast for 1 KB and longer (github #2477);Blake3Digestcompresses in local variables, about 2.8 times as fast (github #2471);Salsa20Engine.processBytes()XORs whole 64-byte keystream blocks, making Salsa20, XSalsa20, ChaCha and XChaCha20 1.3 to 1.75 times as fast (github #2476); and the javaGCMBlockCiphermakes fewer allocations per block. - Ported from the BC 1.87 development line: the CRMF
ProtocolEncrKeyControl,PKIPublicationInfoControlandOldCertIDControl(with JCA forms) andCertificateRequestMessage.getControlValue();Argon2BytesGenerator.getBlockCount()and a publicArgon2BytesGenerator.Block.clear();ASN1Enumerated.isNegative()andlongValueExact(); and the propertiesorg.bouncycastle.lms.max_seed_length,org.bouncycastle.ocsp.max_response_ageandorg.bouncycastle.tsp.allow_inconsistent_signing_time. - The property
org.bouncycastle.dh.allow_short_private_value, which lets DH and ElGamal key pair generation use a private value length below twice the security strength of p. - BCJSSE: the
BCSSLServerSocketextension interface, implemented by the provider's server sockets, and theBCSSLContextandBCSSLSessionContextinterfaces, obtained from a BCJSSESSLContextwithContextUtil.getBCSSLContext(); andProperties.JSSE_USE_NAMED_GROUPS_ORDERnaming theorg.bouncycastle.jsse.useNamedGroupsOrdersystem property.
Release: 2.73.13
Date: 2026, 22nd September.
Lightweight API - secret-dependent operations.
- The lightweight API had no constant-time modular arithmetic or EC scalar multiplication
primitives for secret-key operations.
BigIntegers.modAdd,modSubtract,modMultandcreateBlindedExponent, andECAlgorithms.multiplySecret/sumOfTwoMultipliesSecretwith the supportingECConstantTimeMultiplier, have been ported, and the DSA, ECDSA, ECGOST3410, ECNR, DSTU4145 and SM2 signers now use them, with exponent blinding for their secret-key operations. GOST3410Signer additionally gained the DSA/ECDSA-style nonce blinding it had been missing. - DH, ECDH, ECDHC, ECVKO, MQV, ECMQV, SM2, SRP and J-PAKE key agreement now use constant-time EC scalar multiplication, exponent blinding and constant-time evidence/MAC-tag comparison.
- The EC ElGamal transform classes and the ElGamal, SM2, Naccache-Stern and Cramer-Shoup engines, and the ECIES and RSA KEMs, have been hardened against timing side channels. RSA-KEM decapsulation is now blinded, Cramer-Shoup performs a ciphertext range check, and the invalid-point-encoding exception message is zero-padded.
- The Zuc MAC comparison is now constant-time, and
DSTU7624WrapEngineuses a constant-time checksum comparison. IESEnginecomputed its MAC over the padding buffer rather than the actual cipher-mode output length.
Lightweight API - malformed input, bounds and state.
SICBlockCipher(CTR) propagated its borrow incorrectly for counter increments carrying across 0xFF IV bytes, andskip()moved to the wrong position for backward moves smaller than the current intra-block offset. The full-block-IV counter advance is bounded at 2^64 blocks, the short-IV range check now covers theprocessBlockpath, and the skip/seekTo cascades are a constant-time counter addition. The 2^64-block bound is also enforced on the skip path for 8-byte block ciphers, and the native CTR range bound andgetPosition()were aligned with the Java path.Salsa20Engine.skip(Long.MIN_VALUE)silently moved nothing while reporting that it had, because negating the argument overflowed back to itself; the ChaCha family sharing the engine had the same defect, and ChaCha/ChaCha7539 skip carry now uses the unsigned comparison.- A dropped squeezing flag in Kangaroo broke repeated Xof
output()calls; theGOST28147,RC2,DESedeandDSTU7624wrap engines gained unwrap-length guards; andISO9796d2Signerrejects a too-short recovered block instead of raising an uncaughtArrayIndexOutOfBoundsException. TupleHashandParallelHashbind the requested output length as the NIST SP 800-185 L parameter rather than the configured digest size.- Blake2b's counter overflow handling was corrected,
OldHMac's broken constructor restored, and the digest-state-restoreCryptoServicePurposedecoupled from the enum ordinal. - LMS/HSS now reject malformed and trailing-data signatures and reused-mode signers, raise a clear
error rather than a
NullPointerExceptionfor an unknown LM-OTS type code, and the HSS key-exhaustion off-by-comparison is fixed (github #2414).ExhaustedPrivateKeyExceptionwas given a common superclass so catching the new one also catches the legacypqc.cryptoone, and the LMS/HSS tree cache adopted upstream's versioning (github #2365/#2414). HashSLHDSASignerandSLHDSASignergained uninitialised-use guards, and SLH-DSA's double hypertree-root computation was removed.- The HPKE context advances its sequence number only after Seal and Open succeed, per RFC 9180
sec. 5.2, and HPKE DHKEM uses
ECDHCRawAgreementdirectly rather than wrapping a basic agreement. - DSA and GOST3410 private keys validate that x lies in [1, q-1] at construction, ML-DSA and ML-KEM
private keys validate their encoding length, and
PublicKeyFactoryrejects an out-of-range DHpgenCounterrather than silently truncating it. - Zeroization on
destroy()was added to the RSA, EC, DSA, GOST3410, ElGamal and DH private key parameter classes, and to the LMS and HSS private key parameters.
ASN.1 and encoding.
BMPStringcontent was read by sizing achar[]from the declared length before any content had arrived, so a short crafted header could drive a 1GB allocation and anOutOfMemoryErrorout of a parse API declaringIOException. It is now read throughDefiniteLengthInputStream.- Sorting the elements of a DER SET re-derived an element's encoding every time the insertion sort shifted it, costing O(N^2) encodings; each element is now encoded once and the ordering uses a stable O(N log N) sort.
- The BER/DER generators encoded context tag numbers of 31 and above incorrectly.
- Too-short-SEQUENCE and mandatory-field-consistency guards were added to
RevAnnContent,CertRequest,EvidenceRecord,TSTInfo,PasswordRecipientInfo,KEKRecipientInfo,AuthenticatedDataandKEMRecipientInfo, so a malformed encoding raisesIllegalArgumentExceptionrather than anArrayIndexOutOfBoundsException;EvidenceRecord's always-false length check was corrected. - A copy-paste
instanceofbug in three OERgetInstance()methods (UINT32,Version,EtsiTs103097DataEncryptedUnicast) and an invertedisInstancecheck inOEROptionalwere fixed; malformed OER structures are reported asIOException, and an unknown OER extension is consumed in bounded chunks. X500Name.hashCode()guards the RDN first-element lookup, and RFC 4514 unescaping was hardened.- A wire-declared length is read through
Streams.readLenBytesFully.
Provider (prov).
- The AES, ARIA, SM4 and PBKDF2 GCM/CCM parameter handling was hardened and the raw JCA PBKDF2 iteration count capped; the PKCS#12 and BCFKS write-side iteration counts are configurable; BCFKS scrypt keys derive with p rather than the block size; and the PBKDF2 keyLength taken from a BCFKS keystore or a PBMAC1 PKCS#12 file is bounded before deriving.
- The OCSP response read is bounded, and future-dated and unbounded responses are rejected. An unreachable or failing OCSP responder is reported as a recoverable failure naming the responder rather than a definite "configuration error", so path validation can fall back to CRL checking.
- A reused
CertPathBuilderreported the previous build's failure; the builder was hardened against large self-issued candidate sets, the X.509 valid-policy-tree size is bounded, andPKIXPolicyNode.copyre-parents its children. ReasonsMask.hasNewReasonswas written as(_reasons | mask ^ _reasons) != 0, where^binds tighter than|, so it reported new reasons for almost any pair of masks. The RFC 5280 sec. 6.3.3 CRL scope rules are now single-sourced in the pure-ASN.1org.bouncycastle.asn1.x509.PKIXCRLValidator, withRFC3280CertPathUtilitiesdelegating to it.- The X509CRL implementation actually used had an indirect-CRL issuer defect; the BKS keystore
exception type, the PKCS12 alias and public-key checks and the CRL
dateOfCertGenhandling were corrected, andKeyStore.getCertificateAliason a PKCS12 keystore could return the alias of an unrelated certificate. - Composite ML-DSA registration, key derivation and the
SignatureSpi(empty-message signing,SecureRandomthreading, context bound) were fixed, as was the composite ML-KEM OID map; EC components are normalised and the composite key attributes published. - SLH-DSA
Signatureinstances are bound to their named parameter set; the ML-DSA key-confusion defect was fixed (github #2396/#2397); PQC SPINullPointerExceptions were removed; and ML-KEM encapsulation throughjavax.crypto.KEMwas fixed - the JDK 17 source set carrying those SPIs was not being compiled at all, so the shipped encapsulator threwClassCastExceptionon every call. LMSSignatureSpiresets its digest on every exit and narrows its verify catch, and an LMS key that has been destroyed is refused atinitSign.- RIPEMD-with-RSA-PSS is supported end to end, and a PSS
trailerFieldstate-corruption defect was fixed. - EC key agreement and ETSI KEM secrets are derived into a
byte[]rather than aBigInteger, so leading zero bytes are not lost;KdfUtilguards a short secret;BouncyCastleProviderconstruction is thread-safe and no longer materialises every named curve; and the DHKeyAgreementSpiblinds the exponent of its rawmodPowfinish-agreement step. - The multi-release EdEC overlays had drifted from the base implementations: the jdk1.11
KeyAgreementSpidropped the HKDF salt and the jdk1.15SignatureSpilost the encoding fallback. - KMAC
AlgorithmParametersand the RFC 8702 OIDs are registered, the LMS OTS name table is complete, andBaseAgreementSpivalidates the requested key size. - The
org.bouncycastle.jce.exceptionpackage is deprecated in favour of thejava.security.certexceptions.
CMS, PKIX and EST (pkix).
- Both copies of
PKIXCertPathReviewerdrovecheckNameConstraintsfrom a loop bound ofindex > 0, so name constraints were never applied to the target certificate (CVE-2026-71889). - A PBMAC1 MAC-key-length floor is enforced and PBMAC1's KDF algorithm OID corrected; scrypt parameters are bounded and empty or absent ASN.1 content rejected.
- CMS malformed-content handling no longer lets undeclared unchecked exceptions escape;
EnvelopedDataHelperhonours a caller-suppliedSecureRandomand gained the GCM, SM4 and KMAC content-cipher names; the RFC 9629 KEM recipient family was added for the JCE, with a KEK-length check; andCMSInputAEADDecryptoris shared between the AuthEnveloped recipients. - Definite-length CMS streaming was added and wired through the generators.
- TSP/ERS malformed-content handling was hardened and the digest-algorithm equivalence checks corrected.
- The composite ML-DSA name mappings were migrated to the IANA arc, fixing a live mismatch, and the Composite ML-KEM names added to the name finder.
- The CRL distribution point protocol allow-list is opt-in,
CrlCachehandles non-HTTP schemes, and a DANE null-guard was added. - The policy tree is null-checked before dereferencing the node to remove, a composite-key verification guard and an EST digest-lookup fix were added, and dead legacy composite-signature creation was dropped.
- RFC 7894
tls-uniqueattribute support was added to EST, and aclose()failure is no longer reported in place of an EST 204/404 status.
TLS and JSSE (tls).
- DTLS gained RFC 9147 invalid-record resilience; the reassembly gap list is bounded and its scan starts from a binary search.
- The TLS 1.3 signature-scheme and named-group coupling was corrected, along with the JSSE early-key-share handling, the ML-KEM FIPS-group classification and the curveSM2 filter.
- SRP-6a gained exponent blinding and constant-time M1/M2 comparison, and the SRP verifier generator's random wiring was fixed.
- JSSE cipher-suite-order defaults were brought to parity with the reference implementation,
org.bouncycastle.jsse.useNamedGroupsOrderis wired as a context default, and privileged file I/O gaps were closed. tls/src/main/jdk25is now compiled and shipped asMETA-INF/versions/25; it carries the RFC 5705exportKeyingMaterial*overrides and previously belonged to no source set at all, so the jar shipped without it.
OpenPGP (pg).
- The AEAD decryption streams in
BcAEADUtilandJceAEADUtilre-throw anEOFExceptionfrom the underlying packet stream as a plainIOException("truncated AEAD data"), whichnextPacketTag()does not launder, so a truncated message is reported rather than read back as a shorter well-formed one (CVE-2026-85515). - ASCII armor gained DoS caps and now rejects a second checksum line; signature subpacket bodies
are validated; a pad count past the end of the buffer is folded into the
PGPPadreject; thePGPSecretKeyParserextended-header loop terminates on end of input;KeyBoxByteBuffer.rangeOfchecks its end on its own; the encrypted session key length is checked before the index; and the SEIPDv2 AEAD chunk size is capped. PGPPublicKey.hasRevocationraised aNullPointerExceptionon an encryption-algorithm primary key.- The bcrypt round count for an encrypted OpenSSH v1 private key is capped, configurably, through
org.bouncycastle.openssh.max_rounds, and malformed OpenSSH public keys surface asInvalidKeySpecException.
S/MIME (mail, jmail).
- The depth of nested multipart content is bounded in both S/MIME canonicalisers.
- The journaling encryptor is rebuilt from its OID.
Native code.
- Every CPUID leaf read is bounded by the range the CPU reports, so a leaf the CPU lacks reads as feature-absent rather than as another leaf's bits, and RDRAND/RDSEED are gated behind CPUID.
- The ARM SHA block loads were corrected, the key-derived S-box lookup was removed from the ARM AES
key schedule, and the ARM SHAKE mixed
doOutput/doFinalcontinuation was fixed. - The native packet ciphers wrote the expanded key over the caller's output buffer when the input and output arrays aliased, so an in-place packet encryption returned the raw AES key in place of the ciphertext it was asked for (CVE-2026-71883).
- A native packet CTR request past the counter's capacity reported a full success length while producing keystream that had wrapped; such a request is now rejected (CVE-2026-71884).
memzerowas ported to hand-written assembly; SHA-224/256 no longer export processed block state; the AVX SHA-224 path mitigates a potential unaligned output store; and CTR skip is failure-atomic on a rejected move.- The native CTR counter carries into the high lane when a 16-byte IV's low lane wraps, and
getPositionreports the offset rather than the counter. - The native entropy source retried
RDSEEDandRDRANDwithout any bound, spinning for as long as the carry flag stayed clear, so a persistent failure of the instruction left the calling thread looping inside the JNI call where it could be neither interrupted nor timed out. The retry loops are now bounded - 200 attempts forRDSEEDand 20 forRDRAND, twice Intel's baselines - and on exhaustion the routine clears any partially written buffer with an un-elidablememzeroand throws (CVE-2026-8798). - RDRAND/RDSEED retries are adjustable; the one-shot hybrid no longer casts its base entropy source
to
IncrementalEntropySource; andDumpInforeports the RNG source selection without seeding a DRBG to do it.
- CBOM and SBOM generation has been added to the build.
- The JCE KEM recipient family for CMS (RFC 9629) has been added, together with definite-length CMS streaming.
- BCJSSE now implements server-side OCSP stapling (
status_request/status_request_v2) and socket-level handshake timeouts. - RFC 7250 raw public key certificates are supported (
JcaTlsRawKeyCertificate). - The JSSE provider ships an RFC 5705
exportKeyingMaterial*implementation for Java 25 and later asMETA-INF/versions/25. - EST supports the RFC 7894
tls-uniqueattribute. - New DoS-hardening
Propertiesconstants were added, together with opt-ins for zoneless UTCTime and empty-issuer certificates. - The native RNG settings moved onto the native services, with a new
native.randselector. - The build now requires
LTS_JDK25; see README.md for why this is not optional.
- The following CVEs addressed in the regular Java release 1.86 do not apply to this distribution,
as the affected components are not shipped in the LTS edition: CVE-2026-17507, CVE-2026-71885 and
CVE-2026-71890 (MLS,
org.bouncycastle.mls), CVE-2026-18036 (NTRU), CVE-2026-18040 (HQC), CVE-2026-71891 (BLS12-381), and CVE-2026-71886 and CVE-2026-71887 (the high-level OpenPGP API introduced in 1.81,org.bouncycastle.openpgp.api). For the same reason only the AEAD route of CVE-2026-85515 was reachable here; the SEIPDv1 route runs through the high-level OpenPGP API. - As in earlier LTS releases, the native paths may buffer input differently from the pure-Java
implementations, so
CipherInputStream.read(byte[])and lightweightprocessBytescalls can return short reads. Always check returned lengths, and useStreams.readFully(...)orDataInputStream.readFully(...)where a full read is required. See README.md.
Release 2.73.13 deals with the following CVEs:
- CVE-2026-8798 - Native entropy source retries the CPU entropy instructions without limit.
- CVE-2026-17508 - Password-based KDF cost parameters honoured unbounded from untrusted input across the remaining PBE entry points.
- CVE-2026-71883 - Native AES packet cipher returns the raw AES key on an alias.
- CVE-2026-71884 - Packet CTR reports a full success length after the counter is exhausted.
- CVE-2026-71888 - CMS AuthenticatedData exposes attacker-inserted authAttrs when digestAlgorithm is absent.
- CVE-2026-71889 - PKIXCertPathReviewer does not apply X.509 name constraints to the target certificate.
- CVE-2026-85515 - OpenPGP message truncation not reported, bypassing the SEIPDv1 integrity check.
Release: 2.73.12, 2.73.12.1
Date: 2026, 16th July.
Lightweight API and ASN.1.
- Upstream's hardened AEAD modes were merged while preserving the LTS native paths:
GCMBlockCipher,CCMBlockCipher(MAC verify before write, nonce-reuse guard),EAXBlockCipher(nonce-reuse guard, MAC-size validation), the OCB nonce-reuse guard,KGCMBlockCipherandKCCMBlockCipher(partial-block handling, tag verify before decrypt, nonce guard, parameter validation). The CCM nonce-reuse guard is enforced on the native path as well. IESEnginestream mode derived its MAC key from a length-dependent KDF split, which allowed a MAC forgery; the static-key path was corrected with it.- The
KCCMBlockCipherMAC did not bind the nonce when no AAD was present, permitting a cross-nonce AEAD forgery. - RSA PKCS#1 verification skipped the last two hash bytes on the NULL-omitted path, and strict DigestInfo checking was adopted.
- DH/DSA modulus sizes and Argon2 cost parameters are bounded; the MTI/A0 DH agreement no longer exponentiates an unvalidated peer value; X25519 and X448 gained null and bounds validation; and ECIES-KEM and LMS/HSS reject malformed input.
- The HSS public-key level count was unbounded, allowing a large allocation on verify, and LMS/HSS public-key parsing now rejects malformed keys.
- Lazy ASN.1 sequence forcing reset the nesting-depth guard; the depth limit is now enforced on the lazy parse path, thirteen core ASN.1 factories reject malformed and empty SEQUENCEs, explicit-tag validation was adopted, and a definite-length read no longer allocates up-front from an unbounded declared length.
- Stringifying an X.500 distinguished name escaped in quadratic time; X.500 name validation,
hashCodehardening and thePrivateKeyHashUtilprivate-keyhashCodehardening were adopted. - The OER parser recursed without a depth limit on the self-referential IEEE 1609.2 schema.
- Non-DER time enforcement was completed on both the parse and DER-write sides.
- Upstream's ARIA, GOST3412-2015, Salsa20, Camellia and ML-DSA
PolyVecrefactors were merged, along with theSkeinEngineoverflow fix,BigIntegersadditions (areSecretValuesEqual,hasAnySmallFactors) and the ECDH-C constant-time agreement refactor. - SHA-3 and SHAKE state persistence was hardened further in 2.73.12.1.
Provider, keystores and cert paths.
- BKS and UBER keystores allocated from untrusted lengths before the integrity check, BKS accepted a legacy version with a 16-bit integrity MAC key, and the BKS/PKCS12 keystore KDF iteration counts are now bounded.
- The BCFKS keystore load honoured unbounded KDF cost from the file; those parameters are now bounded before verification.
- PKCS#12 MAC and bag-decryption KDF iteration counts, and attacker-supplied PKCS12/CRMF PBE iteration counts, are bounded.
- PKCS#8 and PBES2 decryptors honoured unbounded KDF cost from their input.
- Name constraints could be bypassed with a trailing dot in an
rfc822Nameor URI; a name-constraint bypass test and a reviewer diagnostic flagging a name-constraints extension on a non-CA certificate were added. - The X.509 valid-policy-tree size is bounded (CVE-2023-0464 class DoS), and CRL revocation checking fails closed.
- A stapled OCSP response was accepted without being bound to the certificate being checked, and an exceptional-signature-rejection test was added (github #2254).
- The CRL cache gained a TTL, ML-KEM key material is zeroized in a binary-compatible way, ARIA CCM was fixed, and composite malformed-input guards were added.
id-RSASSA-PSSis routed through PSS in the Bc operator builders (github #721), the dead generic composite verify branch inJcaContentVerifierProviderBuilderwas removed, and BSI ECKA-EG algorithms are routed as EC key agreement (#790).- The prov jdk17 ML-KEM JCE SPIs compile against the backported
javax.crypto.KEM.
CMS, PKIX, TLS, OpenPGP and S/MIME.
- CMS
AuthEnvelopedDatadid not enforce the tag length on decryption; a CMS recipient content-algorithm allow-list and an AEAD tag-size floor were added and extended to KTS recipients, and the GCM ICV length is enforced with CCM AEAD parameters routed correctly. - CMS
verifySignaturesreturned true for aSignedDatacarrying no signers, andAuthenticatedDatacontent was not bound to the MAC whenauthAttrswere present. KEMRecipientIdwas ported andKEMRecipientInformationwired to the correct recipient id; the CMS ML-DSA digest defaults to SHA-512.- CRMF/CMP password-MAC honoured an unbounded iteration count.
- The DTLS handshake reassembler allocated its buffer from an unchecked 24-bit length.
- The JSSE hostname verifier's CN fallback was enabled by default despite being documented as
opt-in; it is now gated per RFC 9525. The server handshake timeout is honoured in
ProvTlsServer. - OpenPGP AEAD decryption skipped the final tag on chunk-aligned data, the CFB quick-check oracle was active on the symmetric and session-key paths, Argon2 S2K honoured attacker-chosen memory and pass counts, and a user-attribute subpacket length was bounded only by the JVM's maximum memory. OpenPGP AEAD encryption on the native GCM path failed with an output buffer too small.
- The S/MIME validator trusted the signer-asserted
signingTimefor path validation; a writer header-injection test was added and wired. - PEM header checking was added, and encrypted OpenSSH private-key support was ported.
Native code.
- A missing CCM decrypt MAC-size check caused a heap overflow on the ARM path.
- Three low-severity Intel JNI defects were fixed, and ARM variant selection was improved.
- The index reconciliation workflow against the upstream bc-java tree, and the hidden stale-source
residual scan, are documented in CLAUDE.md, with a
reconcile-residualsskill to drive them. module-infoexports dropped from bcutil and bcprov were restored for JPMS and OSGi consistency.- SM2 PKCS#7 content-type OIDs were added to
GMObjectIdentifiers, and ML-KEM private-key parsing was enabled in the PQCPrivateKeyFactory.
Release 2.73.12 deals with the following CVEs:
- CVE-2026-8763 - Name Constraints bypass via trailing dot in rfc822Name and URI.
- CVE-2026-12185 - BKS/UBER keystore allocates from untrusted lengths before integrity check.
- CVE-2026-12802 - CMS AuthEnvelopedData fails to enforce tag-length on decryption.
- CVE-2026-12803 - KCCMBlockCipher MAC does not bind nonce when AAD is absent (cross-nonce AEAD forgery).
- CVE-2026-12816 - IESEngine stream-mode MAC forgery via length-dependent KDF split.
- CVE-2026-12817 - OpenPGP AEAD decryption skips final tag on chunk-aligned data.
- CVE-2026-12860 - RSA PKCS#1 verification skips last two hash bytes in NULL-omitted path.
- CVE-2026-13506 - Lazy ASN.1 sequence forcing resets nesting-depth guard.
- CVE-2026-13586 - PKCS#12 MAC and bag-decryption KDF iteration-count bound (DoS).
- CVE-2026-14682 - Possible OOM from unbounded up-front allocation on a definite-length read.
- CVE-2026-15055 - PKCS#8 / PBES2 decryptors honour unbounded KDF cost from input.
- CVE-2026-58059 - Quadratic-time escaping when stringifying X.500 distinguished names.
- CVE-2026-58060 - HSS public-key level count unbounded, enabling huge allocation on verify.
- CVE-2026-58061 - CCM-family modes write plaintext to caller buffer before tag check.
- CVE-2026-58062 - Stapled OCSP response accepted without binding to the checked certificate.
- CVE-2026-58063 - BCFKS keystore load honours unbounded KDF cost from untrusted file.
- CVE-2026-59638 - JSSE hostname verifier CN-fallback enabled by default despite documented opt-in.
- CVE-2026-59639 - CMS verifySignatures returns true for SignedData with zero signers.
- CVE-2026-59640 - OpenPGP CFB quick-check oracle active on symmetric/session-key paths.
- CVE-2026-59641 - S/MIME validator trusts signer-asserted signingTime for path validation.
- CVE-2026-59642 - CMS AuthenticatedData content not bound to MAC when authAttrs present.
- CVE-2026-59645 - OER parser recurses without depth limit on self-referential IEEE 1609.2 schema.
- CVE-2026-59646 - DTLS handshake reassembler allocates buffer from unchecked 24-bit length.
- CVE-2026-59647 - CRMF/CMP password-MAC honours unbounded iteration count.
- CVE-2026-59648 - OpenPGP Argon2 S2K honours attacker-chosen memory and passes.
- CVE-2026-59649 - OpenPGP user-attribute subpacket length bounded only by JVM max memory.
- CVE-2026-59650 - MTI/A0 DH agreement exponentiates unvalidated peer value.
- CVE-2026-59651 - BKS keystore accepts legacy version with 16-bit integrity MAC key.