Why the SHA-256 on your desk is not the SHA-256 testers install

The analyzer hashes the file you dropped. Play App Signing hashes a different file. Treat a mismatch as expected until you name which certificate you are talking about.

Two keys, two jobs

With Play App Signing you keep an upload key. You sign the AAB/APK you send to the console. Play holds the app signing key and signs what devices install. Updates must match the app signing certificate users already have. Losing the upload keystore is recoverable through Play’s process. Losing the app signing key (if you opted out of Play management) is a different disaster — users cannot update; you ship a new listing.

Internal app sharing and some test tracks can re-sign again. Treat “the APK on the tester’s phone” as a third artifact unless Console says otherwise.

What DroidXP hashes

The APK Analyzer runs crypto.subtle.digest('SHA-256', buffer) on the entire file in your tab. That number is useful against CI output for the same bytes. It is not a certificate fingerprint. It will not match a Play-split APK testers sideload from “internal app sharing” after re-sign.

The APK signer guide only prints zipalign / apksigner / keytool lines. We do not create a keystore and we do not sign. Run apksigner verify --verbose --print-certs on your machine for certificate SHA-256. Paste that digest next to Console → Setup → App signing.

Worked comparison (redacted)

Local upload-signed APK (analyzer): SHA-256: 7f3c…a91b — whole-file hash.

Same artifact, apksigner cert: Signer #1 certificate SHA-256 digest: aa11… — upload cert.

Play-delivered APK (from bundle explorer or device): different whole-file hash; certificate digest matches Play’s app signing cert in Console → Setup → App signing.

If those two certificate lines are swapped in your head, you will file a “tampered build” ticket against your own Play pipeline. We have closed that ticket more than once with this table in the comment.

When a whole-file mismatch is a real bug

CI said it uploaded 7f3c… and the file on the share drive is 00aa… — someone grabbed the unsigned or the debug APK. That is the analyzer’s job. After Play processing, stop comparing whole-file hashes across sides of the console.

v1/v2/v3 signature schemes can all be present. apksigner verify will tell you which. A “signed” APK that only has v1 may install on old devices and fail Play’s current requirements. The analyzer does not parse the signing block. If verify fails, do not “fix” it by zipping META-INF by hand.

Command block we keep in the runbook

Generate it from the signer guide, then run locally:

zipalign -c -v 4 app-release-unsigned.apk
apksigner sign --ks upload.jks --out app-release-signed.apk app-aligned.apk
apksigner verify --verbose --print-certs app-release-signed.apk

Never commit the keystore. Never paste the password into the web form on a shared call. Env vars on CI only. The signer guide’s text boxes are for command shape, not for your alias password.

keytool -list -v -keystore upload.jks prints the upload cert. Compare to Play’s upload certificate line, not the app signing certificate line. Mixing those two is the other classic ticket.

Splits make hashes noisier

Each split is its own ZIP with its own whole-file hash and usually the same signer. Hash base.apk and split_config.arm64_v8a.apk separately. See what the browser can see.

Related

Publishing checklist · Browser vs apktool · versionCode vs applicationId