Skip to content

fix(android): Trust bundled ISRG Root X1 on API 25 and lower - #6227

Open
sentry-junior[bot] wants to merge 11 commits into
mainfrom
fix/android-bundle-sentry-root-cas
Open

sentry-junior[bot] wants to merge 11 commits into
mainfrom
fix/android-bundle-sentry-root-cas

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

📜 Description

On Android API 25 and lower, the SDK now sets SentryOptions.sslSocketFactory to a new SentryRootCaSslSocketFactory. That factory trusts the system CAs plus a bundled ISRG Root X1 (Let's Encrypt), which is listed on docs.sentry.io/security-legal-pii/security/ssl.

How it works:

  • ISRG Root X1 is bundled as a PEM constant in SentryRootCertificates. A unit test checks its SHA-256 fingerprint against the docs page.
  • The system-trusted issuers and the bundled root go into a single KeyStore, which is passed to the platform's default TrustManagerFactory. Chain validation is still done by the platform. There is no custom X509TrustManager.
  • options.getSslSocketFactory() is only read by HttpConnection, so the change only applies to the SDK's own AsyncHttpTransport uploads. Other connections in the app keep using the platform defaults. No network security config is involved.
  • The SSLContext is built lazily on first use, on the transport thread. If building it fails, we log an error and fall back to the default factory.
  • If the user already set a custom sslSocketFactory, it is kept.
  • All new classes are package-private, so there are no public API changes.

💡 Motivation and Context

Sentry is moving its TLS certificates from DigiCert to Let's Encrypt and Google Trust Services (announcement). Here is which roots each Android version ships, based on the AOSP CA store:

  • ISRG Root X1 was only added in Android 7.1 (API 25). Let's Encrypt's default chains (RSA and ECDSA) all end at X1, so API 21–24 can't validate them without this change. The factory also applies on API 25 to cover vendor builds that lack the root.
  • GTS: Google's default chain includes GTS Root R1 cross-signed by GlobalSign Root CA, which Android 5+ already trusts. That cross-sign expires on 2028-01-28, after which API ≤28 will need the GTS roots. This is left for a follow-up and marked with a TODO(2028-01-28) in SentryRootCertificates.
  • DigiCert Global Root CA and G2 are already in every supported API level.
  • ISRG Root X2 isn't needed, because Sentry will serve Let's Encrypt's default chain, which ends at X1.

Known limitation: this only helps apps that update to an SDK version containing this fix.

💚 How did you test it?

Unit tests:

  • SentryRootCaSslSocketFactoryTest: checks the bundled root's fingerprint, that the merged trust store keeps every system-trusted CA, and that the delegate is created lazily and only once.
  • AndroidOptionsInitializerTest: checks that the factory is set on API 25, not set on API 26, and that a user-provided factory is kept.

This hasn't been verified on a real API 21–25 device or emulator against a Let's Encrypt endpoint yet.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

🔮 Next steps

  • Do a manual check on an API 21–25 emulator against an ISRG-signed endpoint.
  • Before 2028-01-28, revisit bundling the GTS roots for API ≤28 (see the TODO in SentryRootCertificates).

via roman.

--

View Junior Session [Sentry]

@sentry

sentry Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.61.0 (1) release

⚙️ sentry-android Build Distribution Settings

@sentry-junior sentry-junior Bot changed the title fix(android): Trust bundled Sentry root CAs on API 25 and lower fix(android): Trust bundled ISRG Root X1 on API 25 and lower Oct 6, 2026

@runningcode runningcode left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gave it a quick look while it is still in draft mode. feel free to re-ask for a review once it is out of draft

Comment thread CHANGELOG.md Outdated
Comment thread CHANGELOG.md Outdated
Comment thread CHANGELOG.md Outdated
@romtsn
romtsn marked this pull request as ready for review October 15, 2026 12:11

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b4ca4ff. Configure here.

sentry-junior Bot and others added 11 commits October 15, 2026 16:09
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
- Mark SentryRootCaSslSocketFactory and SentryRootCertificates as @ApiStatus.Internal
- Use StandardCharsets.UTF_8 instead of suppressing CharsetObjectCanBeUsed (minSdk 21)
- Import Certificate in the test
- Rewrite the changelog entry and link the TLS CA change announcement

Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
On API 24-25 the custom SSLSocketFactory reads the system CAs from the
Network Security Config's base config, so domain-config trust anchors
(for example a private CA for a self-hosted Sentry) would no longer apply
to SDK uploads. Only install the factory when the DSN host is sentry.io or
a subdomain, which is the only case that needs the bundled ISRG Root X1.

Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
- Catch Throwable when building the delegate SSLSocketFactory, so a
  RuntimeException from OEM trust store code falls back to the default
  factory once instead of failing every upload without retry
- Use @VisibleForTesting instead of @testonly on createTrustStore
- Read the DSN host with a shared DsnUtil.getHost helper that parses it
  like Dsn does, without creating a URI
- Test a real Let's Encrypt chain with PKIX: it fails without ISRG roots
  and validates with the bundled root
- Remove ISRG roots from the JVM's system CAs in tests, so they prove the
  bundled root is added

Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
…delegate

Errors such as OutOfMemoryError are no longer swallowed, as AGENTS.md
requires. RuntimeExceptions from OEM trust store code still fall back to
the default SSLSocketFactory.

Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
Co-Authored-By: roman <roman.zavarnitsyn@sentry.io>
@sentry-junior
sentry-junior Bot force-pushed the fix/android-bundle-sentry-root-cas branch from 985261f to 0f67e7f Compare October 15, 2026 16:20
Comment thread CHANGELOG.md

### Internal

- Measure the hostname cache TTL on the monotonic ticker the options provide, so that a device time change no longer shortens or extends it ([#6100](https://github.com/getsentry/sentry-java/pull/6100))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this in this PR too as I noticed it accidentally leaked into 8.61.0

@romtsn

romtsn commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Emulator check of 0fd7bf27e8: 4/4 pass on API 21 and API 24

Both images have no ISRG Root X1 (/system/etc/security/cacerts/6187b673.0 is missing). The test is an instrumented test in sentry-android-core, run with am instrument.

Check API 21 (5.0.2) API 24 (7.0)
Default factory to https://valid-isrgrootx1.letsencrypt.org/ ❌ SSLHandshakeException: CertPathValidatorException: Trust anchor for certification path not found ❌ same
SentryRootCaSslSocketFactory to the same host ✅ HTTP 200 ✅ HTTP 200
sentry.io, o447951.ingest.us.sentry.io, o447951.ingest.de.sentry.io (DigiCert today), default factory and PR factory ✅ handshake OK with both (200 / 404 / 404) ✅ same
SentryAndroid.init with a SaaS DSN sets SentryRootCaSslSocketFactory, and HttpConnection sends a real envelope ✅ 2xx ✅ 2xx
  • API 21 tests the pre-NSC TrustManagerImpl path. API 24 tests the Network Security Config RootTrustManagerFactorySpi path.

@romtsn romtsn added the sanity-check PR needs a lightweight review for obvious issues label Oct 15, 2026

@runningcode runningcode left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sanity-check PR needs a lightweight review for obvious issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants