Signing feels like release ceremony until the day a bundle is rejected—or an API suddenly stops authenticating the production app. The cure is to name the two identities clearly. Your team signs the upload with an upload key. Google Play verifies that upload, builds device APKs, and signs those delivered APKs with the app-signing key.
The two-key model
Upload private key: held by your organization and used to sign AAB/APK uploads.
Upload certificate: public identity Play registers to accept those uploads; safe to share where required.
App-signing private key: held by Google under Play App Signing (or by you in legacy/self-managed distribution) and used to sign delivered APKs.
App-signing certificate: public identity Android and API providers see on the installed application.
Keystore: encrypted container that can hold one or more private keys/certificates; its path, store password, aliases, and key passwords are operational secrets/configuration.
Certificate fingerprint: SHA-256/SHA-1 digest used by services to identify a certificate; it is not the private key.
Choose ownership and recovery before generation
Use an organization-controlled Play developer account with enforced 2-Step Verification and least-privilege release access.
Assign at least two accountable custodians while preventing casual key access.
Store the keystore in an approved encrypted secrets/vault/HSM-backed workflow with access logging.
Keep tested encrypted backups in separate failure domains; document restore without writing passwords into the runbook.
Record app package name, Play app, key purpose, alias, creation/expiry, certificate SHA-256, custodians, and reset/rotation procedure.
Decide whether the same installed-app signing identity must work across Play and other stores before enrolling.
Generate an upload keystore in Android Studio
Open the application project in Android Studio.
Select Build → Generate Signed Bundle / APK.
Choose Android App Bundle for a Play release (or APK only for a workflow that explicitly requires it), then select Next.
Select the application module.
Beside Key store path, select Create new.
Choose an approved secure path and a filename ending in
.jks; do not place it inside the project/worktree.Create a strong unique keystore password and store it in the approved secret system.
Choose an unambiguous alias such as
play-upload-2026and a strong key password.Set validity long enough for the app lifecycle; Android’s guide says at least 25 years.
Enter accurate certificate owner/organization/location fields according to your policy, then select OK.
Return to signing only after recording the alias, certificate fingerprint, storage, backup, and owner.
The form creates private state
The keystore file contains a private key; copying it into
app/or a cloud drive without controls is a security defect.The alias identifies the key entry inside the container.
Store and key passwords protect access but do not make broad distribution safe.
Certificate subject fields are embedded in the public certificate and should not contain secrets.
Do not reuse a debug keystore or personal developer key for production uploads.
Command-line generation with keytool
keytool -genkeypair -v -keystore /secure/path/play-upload.jks -alias play-upload-2026 -keyalg RSA -keysize 2048 -validity 10000What keytool will ask and create
-genkeypaircreates a private/public key pair and self-signed certificate entry.The absolute keystore destination must be replaced with an approved path you control.
RSA 2048 meets Google Play’s current minimum for an upload key; organizational policy may require stronger parameters.
10000days is roughly 27 years and exceeds Android’s documented 25-year guidance.Omitting password flags keeps literal secrets out of the command line; enter prompts through a trusted terminal.
Modern JDK defaults and keystore formats can vary—record the actual type shown by
keytool -list, and test it with the project toolchain.
Inspect the key before using it
keytool -list -v -keystore /secure/path/play-upload.jks -alias play-upload-2026Record public facts, protect private material
Confirm alias, entry type, owner/issuer, serial number, validity, algorithm, and SHA-256 fingerprint.
The command reads the keystore and prompts for its password; it does not export the private key.
A
PrivateKeyEntryis expected for signing, not merely a trusted certificate entry.Fingerprints can be shared for verification, but avoid publishing internal paths, aliases, and operational metadata unnecessarily.
Store the fingerprint in the release runbook and compare it in CI/Play investigations.
Export the upload certificate
keytool -export -rfc -keystore /secure/path/play-upload.jks -alias play-upload-2026 -file upload_certificate.pemThe PEM is public, the keystore is not
-export -rfcwrites the X.509 certificate in PEM text form.This output contains the public certificate, not the signing private key.
Play requests this certificate when registering/resetting an upload key.
Verify its fingerprint before submission and label/archive it so it cannot be confused with the app-signing certificate.
Do not upload the
.jksfile when a service asks only for a certificate.
Sign an Android App Bundle in Studio
Open Build → Generate Signed Bundle / APK and select Android App Bundle.
Choose the application module.
Select the upload keystore, alias, and retrieve passwords from the approved secret system.
Choose the release build variant and any destination options shown by the current Android Studio version.
Generate the bundle.
Locate the output reported by Studio, then verify its certificate and version/package before upload.
Do not send the AAB or keystore through informal chat/email simply because both are “release files.”
Configure Gradle without committing secrets
For local automation, keep signing values in a permission-restricted file outside the repository or supply them through environment/secret-provider integration. The exact approach should fit your CI security model. This Kotlin DSL example reads environment variables and deliberately fails when a release secret is absent.
android {
signingConfigs {
create("release") {
storeFile = file(requireNotNull(System.getenv("ANDROID_UPLOAD_STORE")))
storePassword = requireNotNull(System.getenv("ANDROID_UPLOAD_STORE_PASSWORD"))
keyAlias = requireNotNull(System.getenv("ANDROID_UPLOAD_ALIAS"))
keyPassword = requireNotNull(System.getenv("ANDROID_UPLOAD_KEY_PASSWORD"))
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("release")
}
}
}This is plumbing, not a secret store
Environment variables avoid literal credentials in versioned Gradle files but can still leak through process inspection, debug output, or careless CI configuration.
Provision the keystore as a short-lived protected workspace file, then remove it through the CI platform’s cleanup mechanism.
Never print secret variables or encode a keystore in logs.
Restrict release-signing jobs to protected branches/environments and approved actors.
Prefer CI workload identity/OIDC and managed secret systems where supported; rotate access credentials independently from the Android key.
Ensure pull requests from forks cannot access production signing secrets.
Build the signed release bundle
./gradlew :app:clean :app:bundleRelease
jarsigner -verify -verbose -certs app/build/outputs/bundle/release/app-release.aabVerify before uploading
bundleReleaseproduces the release AAB for the selected module/variant and uses its configured signing config.The output path can differ by module, flavor, or plugin version; use the build output as authority.
jarsigner -verifychecks the AAB signature and prints certificate information; compare the expected upload fingerprint.Also verify application ID/package, version code, version name, build provenance, tests, mapping/native symbols, and data-safety/release notes.
A valid upload signature does not prove that installed Play APKs use the same certificate—they normally use the app-signing certificate.
Enroll the new app in Play App Signing
Create/select the correct app in Play Console and confirm package ownership.
Prepare a release in the intended internal/closed/production track.
Upload the AAB signed with the upload key.
For a normal new app, accept/use Play App Signing and the recommended Google-generated app-signing key unless a reviewed cross-store/legacy requirement dictates another option.
Open the Play App Signing/App integrity area and record both upload and app-signing certificate fingerprints.
Register installed-app certificate fingerprints with affected API providers and Android App Links.
Publish first to a controlled testing track and install through Google Play to verify the delivered artifact.
Which fingerprint goes where
Play upload verification → upload certificate fingerprint.
Google Maps/OAuth/Firebase or another API checking installed package + certificate → Play app-signing certificate fingerprint for Play installs.
Android App Links
assetlinks.json→ SHA-256 fingerprint(s) of certificate(s) signing installed variants/distribution channels.CI pre-upload check → expected upload certificate fingerprint.
Sideloaded/self-distributed APK → fingerprint of the key that actually signed that APK.
During approved signing-key upgrades or multiple stores, services may temporarily need multiple valid fingerprints.
Verify APK signatures when distributing APKs
$ANDROID_HOME/build-tools/<version>/apksigner verify --verbose --print-certs app-release.apkInspect the artifact users will install
Replace
<version>with installed Android SDK Build Tools.apksignerverifies APK signature schemes and prints signer certificate digests.For Play App Bundles, download/test Play-generated APKs or install through a test track to inspect the delivered identity.
Do not use
jarsigneras a complete APK signature-scheme verification substitute; Android Build Tools providesapksignerfor APKs.Compare results with the intended distribution channel’s registered certificate.
Protect local and CI signing material
Encrypt at rest and in transit; restrict read permissions to the release process/custodians.
Keep passwords in a managed secret store, never next to the keystore in Git.
Use separate upload keys for organizational boundaries when policy calls for it; avoid one personal key across unrelated apps/clients.
Audit access, downloads, signing jobs, reset requests, and Play permission changes.
Test backup restoration periodically without exposing production material to ordinary developer machines.
Run release signing on hardened, patched workers; prevent untrusted code from executing in the secret-bearing job.
Document incident response for suspected exposure, malicious upload, Play account compromise, and lost access.
Lost or compromised upload key
Stop release attempts and determine whether the key is lost, password is lost, or compromise is suspected.
Confirm the app is enrolled in Play App Signing and identify authorized Play account roles.
Generate a new upload key through the same controlled process.
Export its public certificate as
upload_certificate.pemand verify the fingerprint.In Play’s current app-signing management area, request an upload-key reset and provide the certificate/reason.
Observe the effective time/status communicated by Play; do not assume immediate activation.
Update CI/vault/backups/runbook, invalidate access to the old material, then test a controlled-track upload.
Investigate and report compromise according to organizational and legal requirements.
Resetting upload does not change installed identity
Under Play App Signing, the app-signing key remains the signer of delivered updates.
API-provider app-signing fingerprints normally do not change just because the upload key resets.
The old upload key can no longer authenticate future uploads after the reset becomes effective.
Without Play App Signing, losing a self-managed app-signing key is a fundamentally different and potentially unrecoverable update problem, subject to platform-supported upgrade mechanisms and eligibility.
App-signing key upgrades are different
An upload-key reset changes who may upload; an app-signing-key upgrade changes the certificate used on eligible installed updates.
Play supports signing-key upgrade paths with platform/version behavior that must be reviewed in current Console documentation.
API providers, App Links, anti-abuse systems, device management, and other stores may need new fingerprints.
Do not request an app-signing upgrade merely because an upload password was forgotten.
Plan rollout, backward compatibility, recovery, and cross-store identity before confirming an upgrade.
Existing apps and migration to Play App Signing
Inventory the original app-signing key, upload process, other stores, API fingerprints, App Links, shared UID/signature permissions, and OEM/preload relationships.
Follow the Play Console’s current PEPK instructions to encrypt and transfer a copy when supplying an existing private signing key.
Download PEPK and encryption material from the relevant Play flow; do not reuse a random command from an old article.
Create/register a separate upload key after enrollment for reduced exposure.
Verify test-track installed certificates and every signature-dependent integration before broad rollout.
Publishing outside Google Play
Android accepts an update only when signing/version/package rules are satisfied; users need a compatible signing identity for the channel they installed from.
If the same app moves between stores, decide whether all stores must deliver artifacts signed with the same app-signing key.
Google documents options such as providing your own app-signing key to Play or using Play-signed universal artifacts for alternative distribution; validate current capabilities and store policies.
Never use the upload key as a casual universal distribution key simply because it is available locally.
Track every channel’s signer fingerprints and test update paths on real prior installations.
Common release errors
“Signed with the wrong key” → compare the AAB signer to Play’s registered upload certificate; do not compare only filenames/aliases.
Keystore was tampered with or password incorrect → verify path, store type, password source, alias, key password, file integrity, and CI line endings/base64 handling without logging secrets.
Alias does not exist → list the keystore securely and use the exact entry alias.
Bundle unsigned → ensure the actual release variant has the intended signing config and inspect the artifact.
OAuth works locally but fails from Play → register the Play app-signing fingerprint and exact production package name.
Update rejected → also verify package/application ID and monotonically greater version code.
Certificate expired/near expiry → follow approved Play/key-upgrade guidance early; do not create a new key and expect updates to accept it automatically.
Release handoff checklist
Play App Signing state and key strategy approved.
Upload keystore outside Git, encrypted, backed up, and restore-tested.
Alias/validity/algorithm/fingerprint recorded without passwords.
Android Studio and protected CI can create the same signed release variant.
AAB signature verifies against the registered upload certificate.
Play app-signing fingerprints registered with APIs/App Links and tested from a Play-installed build.
Play roles, 2-Step Verification, release approvals, and secret access audited.
Version/package, tests, mapping/native symbols, SBOM/provenance, and release notes ready.
Upload reset and app-signing upgrade procedures distinguish the two key types.
Other-store/update compatibility documented.
Primary references
Android’s official app signing guide covers upload/app-signing keys, Studio generation, release signing, certificates, Gradle, upgrades, and Play enrollment.
Google Play’s current Play App Signing documentation defines key ownership/requirements, new/existing app setup, fingerprints, reset, other stores, and testing.
The Android SDK `apksigner` documentation describes APK signature verification and certificate output.
Use the exact PEPK tool/instructions supplied inside the current Play Console flow when transferring an existing app-signing key.
Comments and corrections