AppMama

blog / publishing · gradle

Android App Signing: Keystores, Upload Keys and Play Signing

· 8 min read · AppMama

Android app signing proves that every version of an app comes from the same developer. Android will only install an update if it is signed with the same key as the version already on the device, so the key you choose on day one has to last the life of the app. This guide explains debug and release keys, keystores, how Play App Signing splits the job into an upload key and an app signing key, how to configure signing in Gradle without leaking passwords, and exactly what to do if a key is lost.

Why do Android apps need to be signed?

Every APK must carry a signature before Android will install it. The signature is not about trust in the browser sense: there is no certificate authority, and almost every Android signing certificate is self-signed. What matters is continuity. When an update arrives, the system compares its signing certificate with the installed app’s. If they differ, the update is refused, and with adb install you see INSTALL_FAILED_UPDATE_INCOMPATIBLE.

The same check powers signature-level permissions and lets apps signed with the same key share data. So the key is effectively your app’s identity, alongside its package name.

Debug key vs release key: what is the difference?

The first time you build, Android Studio creates a debug keystore at ~/.android/debug.keystore (C:\Users\you\.android\ on Windows). Its well-known values are store password android, key alias androiddebugkey and key password android. The certificate is valid for 30 years. Every debug build is signed with it automatically.

Because the password is public, Google Play rejects debug-signed uploads. The debug key also differs per machine, which causes a classic confusion: install a debug build from your laptop, then one from a colleague’s machine or CI, and the second install fails because the keys do not match. If that bothers your team, commit one debug keystore to the repo and point the debug config at it:

app/build.gradle.kts

android {
    signingConfigs {
        getByName("debug") {
            storeFile = rootProject.file("keystores/debug.keystore")
            storePassword = "android"
            keyAlias = "androiddebugkey"
            keyPassword = "android"
        }
    }
}

That is safe precisely because a debug key is worthless. Never do the same with a release key.

A release key is one you create yourself, protect with real passwords, and use for every build you distribute. With Google Play, the key you hold is an upload key, explained below.

What is a keystore, an alias and a certificate?

Create a key from Android Studio with Build → Generate Signed Bundle/APK → Create new, or from the command line:

keytool -genkeypair -v \
  -keystore upload-keystore.jks \
  -storetype PKCS12 \
  -alias upload \
  -keyalg RSA -keysize 2048 \
  -validity 10000

A validity of 10,000 days is about 27 years. Google recommends at least 25 years, and Play requires a validity that ends after 22 October 2033. Print the fingerprints any time with keytool -list -v -keystore upload-keystore.jks -alias upload.

What is Play App Signing?

With Play App Signing, Google holds the key that signs what users install, and you sign uploads with a separate key. It is required for every app created on Google Play since August 2021, because Android App Bundles are built into APKs, and signed, on Google’s side.

Upload keyApp signing key
Who holds itYouGoogle Play
SignsThe AAB you uploadThe APKs users install
Users ever see itNoYes, it is your app’s identity on devices
If lost or leakedRequest a reset, keep shippingCannot be downloaded; can only be upgraded through Play

For a new app, the simplest path is to do nothing: sign your first AAB with your upload key, create a release, and Play generates the app signing key. The key you used becomes the registered upload key.

Supply your own app signing key instead (Change app signing key when setting up the first release) only if you need the same signature elsewhere, for example the same app on another store or a sideloaded APK that must update over the Play version. You cannot get a Google-generated key out of Play later.

The single most useful fact about Play App Signing: the upload key is replaceable, the app signing key is not. Keep them different, so a leaked laptop or CI secret never touches your app’s real identity.

Which SHA-1 do I give Firebase, Maps or Google Sign-In?

Both the debug SHA-1 and the app signing key SHA-1 from Play Console. Apps installed from Play are signed with Google’s key, not your upload key, so if you register only the upload or debug fingerprint, Google Sign-In and similar APIs work in your own builds and fail for every Play user. The app signing page lists MD5, SHA-1 and SHA-256 for both certificates.

How do I configure release signing in Gradle?

Keep secrets out of build.gradle.kts. Put them in a keystore.properties file at the project root and add it, and the keystore, to .gitignore:

keystore.properties (not committed)

storeFile=keystores/upload-keystore.jks
storePassword=change-me
keyAlias=upload
keyPassword=change-me

app/build.gradle.kts

import java.util.Properties

val keystoreProps = Properties().apply {
    val file = rootProject.file("keystore.properties")
    if (file.exists()) file.inputStream().use { load(it) }
}

android {
    signingConfigs {
        create("release") {
            storeFile = keystoreProps.getProperty("storeFile")?.let { rootProject.file(it) }
            storePassword = keystoreProps.getProperty("storePassword")
            keyAlias = keystoreProps.getProperty("keyAlias")
            keyPassword = keystoreProps.getProperty("keyPassword")
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = true
            // Only sign when credentials exist, so contributors
            // without the keystore can still build everything else.
            if (keystoreProps.isNotEmpty()) {
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }
}

Then ./gradlew bundleRelease produces a signed AAB in app/build/outputs/bundle/release/. This differs slightly from the official sample, which loads the file unconditionally and fails for anyone who does not have it.

Signing on CI

On a CI server, store the keystore as a base64 secret and the passwords as separate secrets, decode the file at build time, and read the values from environment variables:

create("release") {
    storeFile = rootProject.file("upload-keystore.jks")
    storePassword = providers.environmentVariable("SIGNING_STORE_PASSWORD").orNull
    keyAlias = providers.environmentVariable("SIGNING_KEY_ALIAS").orNull
    keyPassword = providers.environmentVariable("SIGNING_KEY_PASSWORD").orNull
}

CI step

echo "$SIGNING_KEYSTORE_BASE64" | base64 --decode > upload-keystore.jks
./gradlew bundleRelease

Only ever put the upload key on CI. If it leaks, you reset it; if your own app signing key leaks, you have a much bigger problem.

How do I check which key signed a build?

What happens if I lose my signing key?

It depends on which key, and how you distribute.

SituationCan you recover?What to do
Lost or leaked upload key, app uses Play App Signing✓ YesRequest an upload key reset (below)
App signing key compromised, app on PlayPartlyUse key upgrade in Play Console
Lost the only key, older app not enrolled in Play App Signing✗ NoPublish a new app with a new package name
Lost the key for an APK you distribute yourself✗ NoNew key means users must uninstall and reinstall

How do I reset a lost upload key?

From Google’s Play App Signing help:

  1. Generate a new upload keystore, as shown above.
  2. Export its certificate to PEM:
keytool -export -rfc \
  -keystore upload-keystore.jks \
  -alias upload \
  -file upload_certificate.pem
  1. In Play Console, open the Play app signing page for the app. At the time of writing Google’s help lists it under Protected with Play → Play Store protection → Manage Play app signing; older docs call it Release → Setup → App signing. The menus move, so search for “app signing” if neither matches.
  2. In the Upload key certificate section, click Request upload key reset, give a reason, upload upload_certificate.pem and submit.

The new key does not work instantly. Play Console tells you when it becomes valid, typically a day or two later, and uploads signed with either key are refused until then. Users are unaffected because the app signing key does not change. Update keystore.properties and your CI secrets as soon as the new key is active.

What if the app signing key itself is compromised?

Play offers a key upgrade (the Upgrade key button in the app signing key section). It relies on Android’s key rotation support in APK Signature Scheme v3.1, so it only fully applies to newer devices: the Android developer docs describe the new key signing installs and updates on Android 13 and higher, while older versions keep receiving updates signed with the old key. Google’s help page has recently added further detail on newer Android versions, so read the current version before you rely on it. After an upgrade, register the new fingerprints with every API provider.

Outside Play, the same mechanism is available with apksigner rotate, which creates a signing lineage proving the new key is authorised by the old one. Rotation via APK Signature Scheme v3 is supported from Android 9 (API 28).

How should I store my keystore?

Signing is the last step before upload, so it is also a good moment to check that R8 and resource shrinking are on; see how to reduce Android app size. If bundleRelease fails for reasons that have nothing to do with keys, see common Android Gradle build errors.

Frequently asked questions

Is the upload key the same as the app signing key?

It can be, but it should not be. With Play App Signing you sign uploads with the upload key and Google signs what users install with the app signing key. Keeping them different means a leaked upload key can be reset without changing your app's identity.

Can I download my app signing key from Google Play?

No. Once Play App Signing is set up, you cannot retrieve the private app signing key, whether Google generated it or you uploaded it. You can download its public certificate and fingerprints from the app signing page.

Why does Google Sign-In work in my build but not for Play users?

Play re-signs your app with the app signing key, so the installed app has a different SHA-1 from your upload or debug key. Add the app signing key's SHA-1 and SHA-256 from Play Console to Firebase or Google Cloud alongside your debug fingerprint.

How long does an upload key reset take?

Google does not publish a fixed time. Play Console shows the date and time when the new upload key becomes valid, which is usually within a day or two, and you cannot upload new bundles until then.

Can I change the signing key of an app I distribute outside Google Play?

Only through key rotation, using apksigner rotate to create a signing lineage from the old key to the new one, which needs the old key and works on Android 9 and higher. If the old key is lost, users must uninstall and reinstall to move to a newly signed build.