Let users trust a server certificate Android rejects - #41
Open
chamika wants to merge 4 commits into
Open
Conversation
Android's system CA store is frozen at the OS release, so head units on Android 12-14 reject certificates chaining to newer roots. A Polestar 4 (Android 12) cannot validate Sectigo Public Server Authentication Root R46, which AOSP only added in Android 15, and Play cannot backport it because the updatable CA store landed in Android 14. Chrome works because it ships its own root store, which makes the failure look like an app bug. Sign-in now distinguishes a rejected certificate from an unreachable server, shows its SHA-256 fingerprint, and lets the user pin it. This pins the exact leaf certificate rather than disabling verification. Platform validation still runs first and still rejects everything it normally would; only a fingerprint the user explicitly approved is accepted, so a machine-in-the-middle substituting its own certificate still fails. That matters for a head unit carrying an access token across untrusted hotspots. A single OkHttpClient now backs the Jellyfin SDK, album art and playback, so an approved certificate applies to streaming, buffering and prefetch rather than only to the login request. Playback moves from DefaultHttpDataSource to OkHttpDataSource to share that client. Pinned certificates are listed in Settings and can be removed individually. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUCBnCgapXargAeGfmUHGE
Instrumented test opens the media data source against a server whose certificate Android 12 rejects, asserting it fails without a pin and succeeds with one. It builds the client through the real production provider and wraps it as DashTuneMusicService does, so it covers the wiring rather than a copy. Uses an unauthenticated Jellyfin endpoint so it needs no credentials. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUCBnCgapXargAeGfmUHGE
Connect to servers whose HTTPS certificate your car does not recognise Full release notes: - Sign in to servers your car rejects with an SSL error. DashTune now shows the certificate details and lets you approve it, instead of only saying "Could not reach server" - Approving a certificate applies everywhere — browsing, playback, buffering, offline downloads and album art — so music streams normally afterwards - Certificates are matched exactly, so an approved server stays protected against interception on public networks - Approved certificates are listed under Settings, where they can be removed at any time
pingServer returns PingResult rather than Boolean now, which broke compilation of the existing tests. Adds coverage for the two new branches: reporting the certificate when one is rejected, and falling back to unreachable when it cannot be read back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YUCBnCgapXargAeGfmUHGE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Android's system CA store is frozen at the OS release. A Polestar 4 on Android 12 cannot validate certificates chaining to Sectigo Public Server Authentication Root R46 — AOSP only added that root in Android 15, and Play can't backport it because the updatable CA store landed in Android 14.
Verified against AOSP (control root returns 200 on every tag, so the 404s are real):
Chrome works on the same device because it ships its own root store, which makes this look like an app bug. Reproduced byte-for-byte on an Android 12 emulator (zero Sectigo roots present) against the Crashlytics stack trace.
Approach
Pins the exact leaf certificate rather than disabling verification. Platform validation runs first and still rejects everything it normally would; only a SHA-256 fingerprint the user explicitly approved is accepted, so a machine-in-the-middle substituting its own certificate still fails. That matters for a head unit carrying an access token across untrusted hotspots.
Sign-in distinguishes a rejected certificate from an unreachable server, shows the fingerprint, and lets the user pin it.
A single
OkHttpClientnow backs the Jellyfin SDK, album art and playback, so an approved certificate applies to streaming, buffering and prefetch rather than only the login request. Playback moves fromDefaultHttpDataSourcetoOkHttpDataSourceto share that client — the oldHttpURLConnectionstack had no hook to pass anSSLSocketFactorythrough.Pinned certificates are listed in Settings and removable individually.
Verification on Android 12
opensslagainst the live server byte-for-byteSSLHandshakeException) and with one (succeeds), built through the real production providerKnown gaps
MockWebServerwith a self-signed cert.Includes the
v1.3.2(28)release commit.🤖 Generated with Claude Code
https://claude.ai/code/session_01YUCBnCgapXargAeGfmUHGE