Skip to content

Upgrade BouncyCastle to 1.85 across all three bundled locations (CVE-2026-59638) #37085

Description

@fabrizzio-dotCMS

Description

A customer vulnerability scanner flags BouncyCastle as vulnerable in three locations of a dotCMS
deployment and asks for all of them to be raised to 1.85.

Reported by: Support ticket #38700 (severity: Medium)
Customer build: core release-25.07.10_lts_v17, samlbundle 25.06.3, Tika 3.3.1, BouncyCastle 1.84

Status: code complete. All six PRs are merged and the new SAML bundle is published. What is left is
a manual SAML login/logout check and the next LTS patch cut. See Resolution below.

Driver CVE

CVE Detail Relevance
CVE-2026-59638 BC JSSE hostname verifier falls back to CN matching. CVSS 9.3. Affects bcprov 1.61 → <1.85 This is the reason for the 1.85 bump
CVE-2025-14813 Older BC Already remediated in core (1.70 → 1.84) via #35896
CVE-2025-66516 Tika XXE Already fixed separately via #34163not the driver here

Reachability assessment — this is scanner hygiene, not an active vulnerability

Reviewed by Dean Gonzalez (2026-08-12): every hit is a bundling / version flag, not reachable code.
Evidence gathered from the source tree:

  • Zero Security.addProvider(...) / BouncyCastleProvider registrations anywhere in core.
  • The only file importing real BouncyCastle is
    dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/cache/XmlToolCache.java:9-10
    (SHA1Digest + Base64 to build a Velocity cache key — not sensitive crypto, not TLS).
  • dotSAML has zero direct org.bouncycastle imports and no TLS/JSSE/HostnameVerifier/SSLContext
    code. SamlInitializer.init() only runs OpenSAML's JavaCryptoValidationInitializer, which validates
    AES/CBC/ISO10126Padding — it does not register a provider.
  • CVE-2026-59638 specifically is not reachable: the bctls module is not distributed at all, and
    dotCMS TLS never goes through BC's JSSE. The runtime image is a minimal jlink build with default JDK
    providers — no custom java.security, no -Djava.security.properties, no BC injection in the
    Dockerfile or entrypoint.
  • On the LTS line the SAML bundle shipped bcprov 1.54, which is below the affected 1.61 → <1.85
    range. That location was never exposed to this CVE at all.

We are doing this to clear the customer's scanner, not to close an exploitable path.

Scope — three locations

# Location Before After
1 dotSAML plugin (com.dotcms.dotsaml) bundles its own copy org.bouncycastle:bcprov-jdk15on:1.54 org.bouncycastle:bcprov-jdk18on:1.85
2 bom/application/pom.xml <bouncy-castle.version>1.84</bouncy-castle.version> 1.85
3 independent-projects/core-plugins/tika-plugin/pom.xml BC 1.84, resolved transitively through Tika BC 1.85, pinned locally
osgi-base/system-bundles/pom.xml samlbundle reference 26.03.17 (main) / 25.06.3 (LTS) 26.08.21 (both)

Corrections to the original plan

Three things in the first version of this issue turned out to be wrong. Recording them so the acceptance
criteria and the PRs read consistently:

  1. <tika.version> in osgi-base/system-bundles/pom.xml is a dead property. That pom declares
    tika.version and tika.plugin.bundle.version but references neither, and the module has no
    org.apache.tika dependency at all. Bumping them is a no-op. The Tika bundle is consumed as
    com.dotcms.tika at ${project.version}, built in-reactor — which is also why the tika-plugin fix
    ships with core and needs no separate publish step, unlike the SAML bundle.

  2. We did not take Tika 3.3.2. It does pin BC 1.85 upstream, but that release also ports the 4.x
    SAX-based OOXML parsers to 3.x and makes them the default, changing text extraction for every
    docx/pptx/xlsx/vsdx in a patch release. TikaProxy uses stock AutoDetectParser defaults and the
    repo has no tika-config, so nothing would hold the old behavior. Instead the tika-plugin pom pins
    bcjmail/bcpkix/bcutil/bcprov to 1.85 directly. Note this is required regardless of approach:
    independent-projects/pom.xml imports no BOM, so bouncy-castle.version does not govern what
    the Tika bundle embeds.

  3. dotSAML was not the long pole. The core BC bumps were deliberately decoupled from the samlbundle
    reference, so they shipped without waiting on the plugin; the reference bump to 26.08.21 then landed
    as its own one-line PR once the bundle was published. Nothing had to be cherry-picked — the bundle was
    cut from dotSAML main, as predicted.

Verified pre-conditions

Checked against repo1.maven.org metadata (the search.maven.org index is stale and under-reports):

  • org.bouncycastle:bcprov-jdk18on:1.85exists (1.85.2 also exists).
  • org.bouncycastle:bcprov-jdk15onfrozen at 1.70; there is no 1.85 under the jdk15on coordinates.
    The dotSAML change was therefore an artifactId migration (jdk15onjdk18on), not just a version bump.
  • Use 1.85, not 1.85.2: bcpkix-jdk18on has no 1.85.2 release, and core drives both bcprov and
    bcpkix from the single shared bouncy-castle.version property.

Fourth location — documented, out of scope

There is a vendored, repackaged BouncyCastle fork in the tree: 758 .java files under
dotCMS/src/enterprise/java/com/dotcms/enterprise/license/bouncycastle/. Because it is repackaged to
com.dotcms.enterprise.license.*, coordinate-based (GAV) scanners cannot see it — which is why the
customer reports three locations and not four. A source-composition scanner could flag it in the future.

Recording it here for traceability only. Removing or refreshing that fork is a separate, much larger
piece of work and was not part of this issue.

Resolution

Six PRs, all merged 2026-08-21, listed in the order they landed:

# PR Repo / branch What Merge commit
1 com.dotcms.dotsaml#24 dotSAML main BC 1.54 → 1.85 (jdk15onjdk18on), plus the required dotcms-core 26.03.17-01 → 26.03.27-01 bump. Published to libs-release-local as samlbundle 26.08.21 0173f857b9
2 #37089 core release-25.07.10_lts Enabler. Backports SamlNameID and narrows Attributes.nameID Object → Serializable, plus hotfix_tracking.md entry 47 2914d0ec6b
3 #37161 core release-25.07.10_lts samlbundle reference 25.06.3 → 26.08.21 e91619071e
4 #37160 core release-25.07.10_lts BOM 1.84 → 1.85 + BC pinned in the tika-plugin bbade2d35b
5 #37111 core main BOM 1.84 → 1.85 + BC pinned in the tika-plugin d109a7cdfa
6 #37159 core main samlbundle reference 26.03.17 → 26.08.21 5b10eca093

Why #37089 was needed before #37161: the 26.08.21 bundle calls com.dotcms.saml.SamlNameID and
Attributes.getNameID(): Serializable, and neither existed on release-25.07.10_lts. Shipping the bundle
without that backport would fail on the SAML auth and logout paths — SamlNameID alone is not enough,
because the bundle's bytecode also calls the narrowed Attributes signatures. Verified by patching the
LTS core jar with the backported classes and re-running a binary-compat scan: zero unresolved references.

The main PRs (#37111, #37159) needed no enabler — SamlNameID has been in core since 2026-03-26 (#35122).

Risk review before merging. The constant pool of every class in the jars the SAML bundle embeds
(OpenSAML 3.3.1, java-support 7.3.0, xmlsec 2.0.5) was scanned for BouncyCastle references and each
one resolved against both bcprov 1.54 and 1.85. Result: only 3 classes in the whole stack touch BC,
all 23 resolvable members resolve identically on both versions, and xmlsec — the code that actually
validates SAML signatures — references BC zero times. Two class-shape changes exist (AESEngine,
GCMBlockCipher) and both remain source- and binary-compatible for the callers involved. Residual risk
is behavioral and confined to certificate SAN parsing in X509Support, which is what the QA note below
exercises.

Acceptance Criteria

dotSAML plugin

  • com.dotcms.dotsaml declares org.bouncycastle:bcprov-jdk18on:1.85 in place of bcprov-jdk15on:1.54, exclusions preserved
  • dotSAML builds and its existing test suite passes on the bumped dependency (21 tests)
  • A new samlbundle version is released from main26.08.21, in libs-release-local

core — main

  • bom/application/pom.xml sets <bouncy-castle.version>1.85</bouncy-castle.version>
  • independent-projects/core-plugins/tika-plugin/pom.xml pins bcjmail/bcpkix/bcutil/bcprov to 1.85
  • osgi-base/system-bundles/pom.xml points at samlbundle 26.08.21
  • Full CI green on both PRs
  • osgi-base/system-bundles/pom.xml sets <tika.version>3.3.2</tika.version> — dropped, dead property (see Corrections)

core — LTS (release-25.07.10_lts)

  • The same edits applied, samlbundle reference replacing 25.06.3
  • Verified by hand — this base runs no build and no tests (cicd_1-pr.yml there is filtered to main/master, so a green PR proves nothing). install -pl :dotcms-core --am on JDK 21: green

Verification

  • dependency:tree filtered on org.bouncycastle shows no artifact below 1.85 and no residual jdk15on coordinate, on both main and LTS — including the tika-plugin's four embedded artifacts
  • The published samlbundle jar carries libs/bcprov-jdk18on-1.85.jar on its Bundle-ClassPath (checked inside the jar, not inferred from a pom)
  • SAML login + logout verified against a real IdP (see QA note)
  • Security.getProviders() enumerated on the rebuilt image confirms no bundled dependency auto-registers BouncyCastle (baseline: the same check on dotcms/dotcms:25.07.10_lts_v17 — the source predicts zero BC providers)
  • Customer scanner re-run reports zero BouncyCastle findings

QA Note

The only behavior that can realistically break is logging in and out through a SAML identity provider,
because the SAML plugin now carries a much newer crypto library. Everything else in this change is a
version number in a build file.

Setup: a dotCMS build that includes these changes, configured against a working SAML identity provider
(the standard test IdP is fine), with at least one user who can log in through it.

  1. Go to the dotCMS back end and pick the SAML login option. You should be redirected to the identity
    provider's login page, not to an error page.
  2. Log in there with a valid user. You should land inside dotCMS, logged in as that user, with the same
    name, email and permissions as before.
  3. Confirm the user's details on screen are complete — specifically that the account is recognized as the
    same person as before the upgrade, not created as a new duplicate user.
  4. Log out from dotCMS. You should be returned to a logged-out state and, if the IdP is configured for it,
    signed out there too. Logging in again should work without clearing cookies or restarting.
  5. Repeat step 1–2 once with an identity provider certificate that lists the host in its subject
    alternative names (the common real-world case). The login must still succeed.
  6. Watch the dotCMS log during all of the above. There should be no new errors mentioning security,
    certificates, signatures or missing methods.

If step 2 or 4 fails, capture the full stack trace from the log — that is the signal that the newer
crypto library disagrees with something in the identity provider's certificate or assertion, and it is the
one risk this change carries.

Not needed: re-testing regular username/password login, content, or document upload. Document text
extraction (Tika) is untouched by design — the parser change that would have affected it was deliberately
avoided.

Priority

Medium

Additional Context

Submit the PRs from a dotCMS account

Both core PRs must come from a real dotCMS account rather than a third-party fork, so that the full
CI pipeline runs. Lesson learned on #36115#36139, where a third-party PR ran a reduced pipeline.

Delivery to the customer

The LTS changes are merged into release-25.07.10_lts but reach the customer only when the next
25.07.10 patch is cut and published.

References

Metadata

Metadata

Type

Projects

Status
New

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions