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 #34163 — not 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:
-
<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.
-
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.
-
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.85 — exists (1.85.2 also exists).
org.bouncycastle:bcprov-jdk15on — frozen at 1.70; there is no 1.85 under the jdk15on coordinates.
The dotSAML change was therefore an artifactId migration (jdk15on → jdk18on), 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 (jdk15on → jdk18on), 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
core — main
core — LTS (release-25.07.10_lts)
Verification
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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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
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, samlbundle25.06.3, Tika3.3.1, BouncyCastle1.84Driver CVE
bcprov1.61 → <1.85Reachability 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:
Security.addProvider(...)/BouncyCastleProviderregistrations anywhere in core.dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/cache/XmlToolCache.java:9-10(
SHA1Digest+Base64to build a Velocity cache key — not sensitive crypto, not TLS).org.bouncycastleimports and no TLS/JSSE/HostnameVerifier/SSLContextcode.
SamlInitializer.init()only runs OpenSAML'sJavaCryptoValidationInitializer, which validatesAES/CBC/ISO10126Padding — it does not register a provider.
bctlsmodule is not distributed at all, anddotCMS TLS never goes through BC's JSSE. The runtime image is a minimal
jlinkbuild with default JDKproviders — no custom
java.security, no-Djava.security.properties, no BC injection in theDockerfile or entrypoint.
bcprov 1.54, which is below the affected 1.61 → <1.85range. 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
com.dotcms.dotsaml) bundles its own copyorg.bouncycastle:bcprov-jdk15on:1.54org.bouncycastle:bcprov-jdk18on:1.85bom/application/pom.xml<bouncy-castle.version>1.84</bouncy-castle.version>1.85independent-projects/core-plugins/tika-plugin/pom.xmlosgi-base/system-bundles/pom.xmlsamlbundle reference26.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:
<tika.version>inosgi-base/system-bundles/pom.xmlis a dead property. That pom declarestika.versionandtika.plugin.bundle.versionbut references neither, and the module has noorg.apache.tikadependency at all. Bumping them is a no-op. The Tika bundle is consumed ascom.dotcms.tikaat${project.version}, built in-reactor — which is also why the tika-plugin fixships with core and needs no separate publish step, unlike the SAML bundle.
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.
TikaProxyuses stockAutoDetectParserdefaults and therepo 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.xmlimports no BOM, sobouncy-castle.versiondoes not govern whatthe Tika bundle embeds.
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.21then landedas 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.orgmetadata (thesearch.maven.orgindex is stale and under-reports):org.bouncycastle:bcprov-jdk18on:1.85— exists (1.85.2 also exists).org.bouncycastle:bcprov-jdk15on— frozen at 1.70; there is no 1.85 under thejdk15oncoordinates.The dotSAML change was therefore an
artifactIdmigration (jdk15on→jdk18on), not just a version bump.bcpkix-jdk18onhas no 1.85.2 release, and core drives bothbcprovandbcpkixfrom the single sharedbouncy-castle.versionproperty.Fourth location — documented, out of scope
There is a vendored, repackaged BouncyCastle fork in the tree: 758
.javafiles underdotCMS/src/enterprise/java/com/dotcms/enterprise/license/bouncycastle/. Because it is repackaged tocom.dotcms.enterprise.license.*, coordinate-based (GAV) scanners cannot see it — which is why thecustomer 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:
com.dotcms.dotsaml#24mainjdk15on→jdk18on), plus the requireddotcms-core26.03.17-01 → 26.03.27-01 bump. Published tolibs-release-localas samlbundle 26.08.210173f857b9release-25.07.10_ltsSamlNameIDand narrowsAttributes.nameIDObject → Serializable, plushotfix_tracking.mdentry 472914d0ec6brelease-25.07.10_ltse91619071erelease-25.07.10_ltsbbade2d35bmaind109a7cdfamain5b10eca093Why #37089 was needed before #37161: the 26.08.21 bundle calls
com.dotcms.saml.SamlNameIDandAttributes.getNameID(): Serializable, and neither existed onrelease-25.07.10_lts. Shipping the bundlewithout that backport would fail on the SAML auth and logout paths —
SamlNameIDalone is not enough,because the bundle's bytecode also calls the narrowed
Attributessignatures. Verified by patching theLTS core jar with the backported classes and re-running a binary-compat scan: zero unresolved references.
The
mainPRs (#37111, #37159) needed no enabler —SamlNameIDhas 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-support7.3.0,xmlsec2.0.5) was scanned for BouncyCastle references and eachone 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 actuallyvalidates 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 riskis behavioral and confined to certificate SAN parsing in
X509Support, which is what the QA note belowexercises.
Acceptance Criteria
dotSAML plugin
com.dotcms.dotsamldeclaresorg.bouncycastle:bcprov-jdk18on:1.85in place ofbcprov-jdk15on:1.54, exclusions preservedmain— 26.08.21, inlibs-release-localcore —
mainbom/application/pom.xmlsets<bouncy-castle.version>1.85</bouncy-castle.version>independent-projects/core-plugins/tika-plugin/pom.xmlpins bcjmail/bcpkix/bcutil/bcprov to 1.85osgi-base/system-bundles/pom.xmlpoints at samlbundle26.08.21— dropped, dead property (see Corrections)osgi-base/system-bundles/pom.xmlsets<tika.version>3.3.2</tika.version>core — LTS (
release-25.07.10_lts)25.06.3cicd_1-pr.ymlthere is filtered tomain/master, so a green PR proves nothing).install -pl :dotcms-core --amon JDK 21: greenVerification
dependency:treefiltered onorg.bouncycastleshows no artifact below 1.85 and no residualjdk15oncoordinate, on bothmainand LTS — including the tika-plugin's four embedded artifactslibs/bcprov-jdk18on-1.85.jaron itsBundle-ClassPath(checked inside the jar, not inferred from a pom)Security.getProviders()enumerated on the rebuilt image confirms no bundled dependency auto-registers BouncyCastle (baseline: the same check ondotcms/dotcms:25.07.10_lts_v17— the source predicts zero BC providers)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.
provider's login page, not to an error page.
name, email and permissions as before.
same person as before the upgrade, not created as a new duplicate user.
signed out there too. Logging in again should work without clearing cookies or restarting.
alternative names (the common real-world case). The login must still succeed.
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_ltsbut reach the customer only when the next25.07.10 patch is cut and published.
References
/_importendpoint — silent 200 OK, broken transaction, bad validation #34718 (Tika XXE)