[improve][build] Upgrade BouncyCastle to 1.85 and BouncyCastle FIPS to 2.1.x - #26349
[improve][build] Upgrade BouncyCastle to 1.85 and BouncyCastle FIPS to 2.1.x#26349lhotari wants to merge 2 commits into
Conversation
…o 2.1.x Non-FIPS: - bcpkix-jdk18on / bctls-jdk18on 1.84 -> 1.85 - bcprov-jdk18on 1.84 -> 1.85.2 bcprov now uses its own 'bouncycastle-bcprov' version reference. BouncyCastle published a provider-only patch release 1.85.2 that was not released for bcpkix-jdk18on or bctls-jdk18on, so a single shared version reference can no longer cover all three. FIPS: - bc-fips 2.0.1 -> 2.1.3 - bcpkix-fips 2.0.11 -> 2.1.12 - bcutil-fips 2.0.6 -> 2.1.7 Note for operators running Pulsar in FIPS mode: BC-FJA 2.1.x is a different FIPS 140-3 certified module than 2.0.x (Certificate apache#4943), certified for Java 8, 11, 17 and 21. This upgrade therefore swaps the certified cryptographic module, which may require re-validation depending on your compliance process. BouncyCastle publishes no formal 1.85 release notes; 1.85 is feature-additive over 1.84 (post-quantum signature algorithms, BLS12-381, BIP-340 Taproot, hybrid X.509 certificates). Assisted-by: Claude Code (Opus 5)
david-streamlio
left a comment
There was a problem hiding this comment.
The mechanical half of this checks out completely — I verified the resolution and the license bookkeeping rather than reading past them. The compliance half has one thing I think is worth resolving before this merges, because the PR adds an explicit certification claim and the certificates do not quite say what it says.
Verified rather than taken on trust:
- All seven coordinates resolve.
bcprov-jdk18on:1.85.2,bcpkix-jdk18on:1.85,bctls-jdk18on:1.85,bcutil-jdk18on:1.85,bc-fips:2.1.3,bcpkix-fips:2.1.12,bcutil-fips:2.1.7all return their POM from Maven Central. - The separate
bouncycastle-bcprovreference is justified exactly as described.1.85.2is published forbcprov-jdk18onand for nothing else —bcpkix-jdk18on,bctls-jdk18onandbcutil-jdk18onall 404 at1.85.2. The catalog could not express this with one reference, so the split is right rather than merely convenient. - The version skew is safe and the LICENSE entries match what actually resolves. Both
bcpkix-jdk18on:1.85andbctls-jdk18on:1.85declarebcutil-jdk18on:1.85, andbcprovis a direct catalog entry forced to1.85.2, so the assembled set is bcpkix 1.85 / bcutil 1.85 / bcprov 1.85.2 — precisely what bothLICENSE.bin.txtfiles now list. Build and License check passing confirms that against the real distribution rather than against my reading. - Nothing is left behind. No
1.84or2.0.xBouncyCastle reference survives anywhere in the tree, and the only two files that name BC jars are the two updated here.bcprov-ext-jdk18onappears solely as a defensiveexcludein the bcfips test module, so it needs no bump. - The FIPS suite did run.
:tests:pulsar-client-test-bcfips:testcompiled and executed inside CI - Unit - Other, which passed — it is not excluded fromtest_group_otherinpulsar-build/run_unit_group_gradle.sh. So the end-to-end FIPS exercise happened against 2.1.3 in CI, not only locally.
1. The pinned bc-fips version is not the version on the certificate — before or after this PR.
I read both CMVP entries rather than going from the release notes:
| Pinned in Pulsar | Certificate | Version(s) on the certificate | |
|---|---|---|---|
| today | bc-fips 2.0.1 |
#4743 | 2.0.0 |
| this PR | bc-fips 2.1.3 |
#4943 | 2.1.1 |
So this is not a regression — we already ship a patch release that is not the validated one, and I want to be clear I am not accusing this PR of introducing the problem. But it does newly add a FIPS: please note for operators section asserting that this "swaps out the certified cryptographic module," and that framing implies 2.1.3 is the certified module. An operator who reads that, upgrades, and then has to evidence their module against #4943 finds a version they are not running.
I could not resolve from BouncyCastle's own download page which jar corresponds to the validated module, so I am raising this as a question rather than asserting a defect. Three honest outcomes and I do not know which holds:
- 2.1.3 is covered by #4943 via a non-security-relevant maintenance update. Then the description should cite it, because the certificate page alone does not show it.
- Only 2.1.1 is validated. Then pinning
2.1.1— on Maven Central today — would both land the upgrade and fix the pre-existing mismatch in one go. - Jar version and module version genuinely differ. Then that mapping is the single most useful sentence this PR could give an operator.
This is the same shape we have been chasing across PIP-478: a control that is shaped like compliance without being compliance. Getting BCFIPS onto the classpath is not the claim — running the validated module is. Since you are in here anyway and 2.1.1 is available, this looks cheap to settle.
2. This upgrade shortens the certificate horizon by about two and a half years.
Worth stating plainly because it cuts against the intuition that newer is safer:
- #4743 (the 2.0.x line we are on) — Active, sunset 2029-07-28
- #4943 (the 2.1.x line this moves to) — interim validation, sunset 2027-01-16
That is inside the plausible support life of a 5.0.x line, and it is the kind of thing an operator planning a FIPS deployment on Pulsar 5.0 needs told. It does not make the upgrade wrong — 2.1.x presumably carries fixes worth having, and BC will re-validate — but the tradeoff should be in the description rather than discovered later.
3. The tested Java ceiling is 21 on both certificates; our default CI JDK is 25.
#4943's tested configurations are Java SE 8, 11, 17 and 21 (Ubuntu 22.04, 11th-gen Intel), and #4743's are the same four. So the JDK ceiling is unchanged by this upgrade — flat, not improved.
pulsar-ci.yaml defaults jdk_major_version to 25, so the FIPS run I confirmed above executed outside the tested set. Not overstating it: FIPS 140-3 permits a software module ported to a different operational environment to remain validated under vendor affirmation, so Java 25 is not automatically disqualifying — but whether that suffices is the operator's compliance programme's call, and some read it strictly. Since this PR already tells operators the module changed underneath them, one more sentence — the tested environments top out at Java 21; FIPS mode on a newer JDK relies on vendor affirmation — turns a surprise into a decision. Non-blocking.
On the red CI: none of it is this change. Six of the seven failures are the runner failing to fetch actions/setup-java or actions/download-artifact from codeload.github.com with 429/502/503 after three retries; the seventh (java-test-image) got further and then had its bitshuffle-0.3.4.tar.gz download return a 199-byte error body that tar rejected with gzip: invalid magic — the same throttling wearing a different hat. Every job died in setup and no test executed. Nearly every PR pushed in that 15:22–15:29 UTC window has an identically failed run, so the burst rate-limited itself. A rerun should clear it; no change needed here.
Nothing above touches the non-FIPS 1.85 bump, which looks fine as it stands. Point 1 is the only one I would want settled before merge, and it may well be settled by a link.
… not missing machinery The first gap bullet claimed the BC/BC-FIPS swap "lost its packaging story in the Gradle migration", which reads as though the Maven-era swappable bouncy-castle-bc / bouncy-castle-bcfips NAR modules need resurrecting. They do not, and this PIP never proposed to: BC and BC-FIPS are mutually exclusive on a classpath, so the swap is a matter of shipping one set of jars instead of the other, and SecurityUtility's reflective loading already accepts either flavor. Raised on the DISCUSS thread. The bullet now concedes that mechanics up front, states explicitly that the NAR modules are not coming back, and locates the actual gap where it belongs: no supported route for the FIPS artifacts to reach a server runtime (the server distribution excludes bc-fips outright), no server-side coverage (the only consumer is a client-side JCA-swap test), and no documentation telling an operator which jars to swap. Also drops the pinned FIPS version numbers and the libs.versions.toml line reference, both of which go stale when the BouncyCastle upgrade in apache#26349 lands; the surviving citation is the distribution/server exclusion, which is the load-bearing evidence.
…the version catalog The FIPS 140-3 cryptographic boundary for BC-FJA is the bc-fips jar file name: the security policy for CMVP certificate apache#4943 names bc-fips-2.1.1.jar, and apache#4743 names bc-fips-2.0.0.jar. Exactly one published jar per line is therefore the validated module, and BouncyCastle ships security fixes only in the later "Provider (patched)" releases, certifying them afterwards. The consequence, which is easy to get wrong when bumping these versions, is that no certified bc-fips jar is free of known CVEs: 2.1.1 has 13 and 2.0.0 has 11, two CVSS 9.3 in each. Record the mapping and that reasoning next to the versions so the trade-off does not have to be re-derived. Also note that bc-fips is test-only in this build. No version changes. Assisted-by: Claude Code (Opus 5)
|
Thanks @david-streamlio — this was worth blocking on. You were right that the description made a certification claim the certificates don't support, and chasing it down turned up something that changes the shape of the answer. The description is rewritten; summary here. Your question: outcome (b), and there is no ambiguity to resolveI went to the security policy rather than the certificate page, and it removes the guesswork — the boundary is the jar file, by name. 140sp4943.pdf (BC-FJA, Software Version 2.1.1, 2025-07-18) §2:
So your outcome (c) is out — there is no jar-version/module-version indirection to map. Exactly one published jar per line is the validated module, and it is named in the policy. Outcome (a) is out too: 2.1.2 and 2.1.3 appear nowhere in the 58-page document, and there is no maintenance-coverage language. BouncyCastle's own download page shows the vendor side of the same structure, listing two provider jars per line —
The roadmap fills in the middle: 2.1.1 is a "Bug fix release for 2.1.0 (GC related issues)", "Superseded by 2.1.2"; 2.1.2 is "Under submission". (BC's pages still name 2.1.0 as certified — CMVP rolled #4943 forward to 2.1.1 in its 2025-11-14 update.) So 2.1.3's status is pending submission: not certified, not rejected. Why the PR keeps 2.1.3 rather than taking your suggested fixYour suggestion was the obvious cheap fix, and I expected to take it. It doesn't survive the CVE data. Because BouncyCastle ships security fixes only in the patch releases and certifies them afterwards, no certified
The two criticals are certificate-validation bypasses: CVE-2026-8763 (Name Constraints bypass via a trailing dot in One caution if you go to reproduce this: OSV and the GitHub Advisory Database are currently stale here. An OSV query for And the point that decides it for this PR: the existing Your point 2 — confirmed, with a mitigating factThe interim horizon is real: #4943 sunsets 2027-01-16 vs #4743's 2029-07-28. What takes the sting out is something I should have checked before writing that operator note: No Your point 3 — confirmed, and the policy is more specific than the certificateBeyond the tested environments (Table 2), the policy lists 78 Vendor Affirmed environments (Table 3) — and every one tops out at Java SE Runtime Environment v21. Java 25 is on neither list. The porting terms:
Worth noting condition (1) also independently rules out reading vendor affirmation as covering a different jar — it varies the operational environment only. Given the test-only scoping above, for this repo the Java-25 point affects which JDK Also confirmed on the other two: On the red CIAgreed with your diagnosis, and it cleared on rerun — all checks are green now, no change needed. Separately: your review surfaced that the non-FIPS half matters more than either of us framed it. 1.85 is the fixed version for BouncyCastle's 2026-08-03 batch, and the maintenance branches ship 1.84, so that bump is going to branch-4.2 in its own PR with the CVEs enumerated. |
Motivation
BouncyCastle and BouncyCastle FIPS are both behind. This brings the non-FIPS provider stack to 1.85
and the FIPS stack to the 2.1.x line.
Modifications
gradle/libs.versions.toml:Plus the corresponding jar names in the server and shell distribution
LICENSE.bin.txtfiles, and acomment in the version catalog recording the FIPS certificate mapping described below, so the next
person bumping these does not have to re-derive it.
A new
bouncycastle-bcprovversion reference is introduced. BouncyCastle published aprovider-only patch release, 1.85.2, that was not released for
bcpkix-jdk18onorbctls-jdk18on— those remain at 1.85 on Maven Central.bcprov-jdk18onpreviously shared thesingle
bouncycastlereference, which can no longer express this, so it now has its own.FIPS: which jar is the validated module
Thanks @david-streamlio — you were right to push on this, and the original description overstated
things. Of your three outcomes, (b) holds: only 2.1.1 is validated. But the remedy does not
follow, and the reason is worth spelling out because it applies to the 2.0.x line just as much.
The cryptographic boundary is the jar file, by name. The
security policy for certificate 4943 (BC-FJA, Software Version 2.1.1, 2025-07-18) says so
in §2:
So there is no jar-version/module-version indirection to map — exactly one published jar per line is
the validated module, and it is named in the policy:
bc-fips-2.1.1.jarbc-fips-2.0.0.jarBouncyCastle's download page shows the same shape from the vendor side, listing two provider
jars per line —
bc-fips-2.1.0.jarunder "Provider" andbc-fips-2.1.3.jarunder"Provider (patched)":
The roadmap adds that 2.1.1 is a "Bug fix release for 2.1.0 (GC related issues)",
"Superseded by 2.1.2", and 2.1.2 is "Under submission". (BouncyCastle's pages still name 2.1.0 as
the certified jar; CMVP rolled #4943 forward to 2.1.1 in its 2025-11-14 update.) So 2.1.3's status
is "pending submission" — not certified, not rejected.
Why this PR keeps 2.1.3
Because BouncyCastle ships security fixes only in the patch releases and certifies them afterwards,
no certified bc-fips jar is free of known CVEs — on either line:
The two criticals are certificate-validation bypasses: CVE-2026-8763 (Name Constraints bypass via a
trailing dot in
rfc822Name/URI) and CVE-2026-58062 (stapled OCSP response accepted without bindingto the checked certificate). Pinning 2.1.1 to match the certificate would knowingly ship both.
The key point for this PR: Pulsar's existing
bc-fips 2.0.1pin is not a certified versioneither — certificate #4743 names 2.0.0 only. So 2.1.3 is no worse than the status quo from a
certification perspective, and strictly better on CVEs, which is why the upgrade to the 2.1.x line
is the right move rather than a downgrade to a certified-but-vulnerable jar. The same reasoning
rules out the 2.0.x alternative:
2.0.2would also be uncertified.The other two bumps are the vendor's minimum fixed versions as well, and neither is inside the
certified boundary — the security policy states "This module is the only software component within
the Cryptographic Boundary" — so they carry no certification consequence:
bcpkix-fips 2.0.11 → 2.1.12fixes CVE-2026-59639, CVE-2026-12802, CVE-2026-59642 (all 8.7),CVE-2026-59647, CVE-2026-15055
bcutil-fips 2.0.6 → 2.1.7fixes CVE-2026-59645 (8.7)The two secondary points
Certificate horizon (your point 2) — confirmed: #4943 is an interim validation sunsetting
2027-01-16, ~2.5 years earlier than #4743's 2029-07-28. What takes the sting out of it here is that
bc-fipsis test-only in this build: it is declared once, astestImplementationintests/pulsar-client-test-bcfips; the server distribution explicitly excludes it and ships thenon-FIPS provider instead; it appears in no
LICENSE.bin.txt, no published POM, and not inpulsar-bom. Resolving:distribution:pulsar-server-distributionyields onlybcprov-jdk18on:1.85.2,bcpkix-jdk18on:1.85andbcutil-jdk18on:1.85— nobc-fipsat anyversion. A FIPS deployment assembles its own provider classpath today (as PIP-478 notes), so this
pin does not commit any operator to the shorter horizon; it determines what Pulsar's FIPS test module
exercises. That also means the "swaps out the certified cryptographic module underneath anyone
running Pulsar in FIPS mode" framing in the original description was wrong on both halves, and it
has been removed.
Tested Java ceiling (your point 3) — confirmed, and the security policy is more specific than the
certificate page. Beyond the tested environments (Table 2) it lists 78 Vendor Affirmed environments
(Table 3), and every one tops out at Java SE Runtime Environment v21; Java 25 is on neither list. The
policy's porting terms:
Whether vendor affirmation suffices is the operator's compliance programme's call. For this
repository it affects only which JDK
pulsar-client-test-bcfipshappens to run on in CI.On 1.85 release notes
BouncyCastle publishes no formal release notes for 1.85 (see bcgit/bc-java#2355). Based on the
project's release announcement, 1.85 is feature-additive over 1.84: post-quantum signature
algorithms from recent NIST and Korean standardisation work, BLS12-381 signatures, BIP-340 Taproot
Schnorr signatures, and hybrid X.509 certificate support. No API removals are announced.
Class file versions were checked in the published jars: all artifacts keep Java 8 roots with
multi-release overlays, so the Java 17 baseline is unaffected.
Verifying this change
This change is already covered by existing tests, in particular the
tests/pulsar-client-test-bcfipsmodule, which exercises the FIPS provider end to end.
Verified locally with:
./gradlew sanityCheckand./gradlew checkBinaryLicense./gradlew :tests:pulsar-client-test-bcfips:test— passes against bc-fips 2.1.3 / bcpkix-fips2.1.12 / bcutil-fips 2.1.7
./gradlew :distribution:pulsar-server-distribution:dependenciesand:tests:pulsar-client-test-bcfips:dependencies— confirm the resolved BouncyCastle sets quotedabove
Does this pull request potentially affect one of the following parts:
The non-FIPS BouncyCastle jars bundled in the server and shell distributions change version.
bc-fipsis in no distribution, so the FIPS bump affects only the in-tree FIPS test module.