blog / publishing · gradle
Android App Signing: Keystores, Upload Keys and Play Signing
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?
- A keystore is a password-protected file (
.jksor PKCS12, often named.keystoreor.p12) that can hold several keys. - Each key inside has an alias (a name) and, in JKS files, its own key password. In PKCS12 stores, the default for
keytoolsince JDK 9, the store and key passwords are the same, which is why Android Studio asks you to use the same one. - The certificate is the public half: it contains the public key and the name you entered. You can share it freely; API providers ask for its SHA-1 or SHA-256 fingerprint.
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 key | App signing key | |
|---|---|---|
| Who holds it | You | Google Play |
| Signs | The AAB you upload | The APKs users install |
| Users ever see it | No | Yes, it is your app’s identity on devices |
| If lost or leaked | Request a reset, keep shipping | Cannot 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?
./gradlew signingReportprints the keystore, alias and fingerprints for every variant.apksigner verify --print-certs app-release.apk(inbuild-tools) shows the certificate on an APK.keytool -printcert -jarfile app-release.aabshows the certificate on an AAB, which uses JAR signing.- For an app installed from Play, compare against the app signing certificate in Play Console, not your upload key.
What happens if I lose my signing key?
It depends on which key, and how you distribute.
| Situation | Can you recover? | What to do |
|---|---|---|
| Lost or leaked upload key, app uses Play App Signing | ✓ Yes | Request an upload key reset (below) |
| App signing key compromised, app on Play | Partly | Use key upgrade in Play Console |
| Lost the only key, older app not enrolled in Play App Signing | ✗ No | Publish a new app with a new package name |
| Lost the key for an APK you distribute yourself | ✗ No | New key means users must uninstall and reinstall |
How do I reset a lost upload key?
From Google’s Play App Signing help:
- Generate a new upload keystore, as shown above.
- Export its certificate to PEM:
keytool -export -rfc \
-keystore upload-keystore.jks \
-alias upload \
-file upload_certificate.pem
- 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.
- In the Upload key certificate section, click Request upload key reset, give a reason, upload
upload_certificate.pemand 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?
- Keep the keystore and both passwords in a password manager, plus one offline copy somewhere physically separate.
- Write down the alias. A keystore without the alias name is surprisingly hard to use under pressure;
keytool -list -keystore file.jkswill tell you, but only if you have the password. - Never commit release keystores or
keystore.properties. Checkgit logfor a stray one if you open-source the repo. - Back up before every machine wipe. Most lost keys are not stolen; they vanish with an old laptop.
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.