PocketBill Blog
One commit, two builds, one working login. Play re-signs what you upload, so the fingerprint Google actually matches against is not the one most pages show you. Finding the real one took three certificate files and a comparison.
The dev build signed in an hour earlier. Then I installed the same commit from the Play test track, tapped Sign in with Google, and got one line back: `developer_error`. Same code, same phone, same Google account — one build that worked and one that refused to.
PocketBill is a local-first tracker for subscriptions, bills and spending. No account required, and it works with the network off, which is most of the point. The sign-in exists for exactly one reason: to carry your numbers to a second phone. So a broken login never touches the product's main promise — which is precisely why I kept telling myself it was a side feature.
My mistake was assuming a signing key is one thing with a few hashes hanging off it. Play's quantum-ready hybrid signing produces a hybrid certificate built from three: a classical ECDSA part, a post-quantum part, and a combined one. The fingerprint I had registered months earlier was the classical sub-certificate. Real, valid, and never installed on anybody's phone.
What lands on a user's device is signed by Google, not by me. I sign the bundle I upload with an upload key; Play strips that signature and re-signs the APK with its own app-signing key. Two keys, two fingerprints, and Google's matching only recognises the second. Downloading the three `.der` files and comparing their hashes is what finally pointed at the one I had never registered.
Every Play Console signing page carries a Digital Asset Links JSON at the bottom. The value under `sha256_cert_fingerprints` is always the certificate currently signing what users install — for me, `16:64:A4:6A...`. That single string was the whole answer, printed in plain sight.
What misled me was the block right above it, labelled *upload key certificate*. It is the most prominent fingerprint on the page, and it is a decoy: it only signs what you hand to Google. If you ever have to debug a Play signing problem, read the JSON and ignore the label.
The fix never touched the app. An Android OAuth client matches on package name plus SHA-1, and holds only one SHA-1 — so I added a second client with the same package name and the deployment certificate's SHA-1, then added the same pair to Firebase. Server-side, live in a minute or two, no new build, no re-upload, no reinstall. I reopened the copy already sitting on my phone and it signed in.
Which is the part worth keeping: the dev build works proves nothing about the Play build, because those are two independent signature paths. And next time someone tells you the fix is a rebuild, it is worth asking which certificate they believe is signing the file.