Android Upload Key — Rotation & Recovery
App: eSHARS Mobile (Android) Related ticket: HM-8427
Overview
The eSHARS mobile app uses Google Play App Signing. Google holds the app signing key (used to re-sign the APK delivered to users) and we hold the upload key (used to sign AABs before uploading to Google Play). This split means that even if the upload keystore is compromised, only the upload key needs to be rotated — the app itself is never at risk.
GitHub Actions └─ upload-keystore.jks ─(signs AAB)─► Google Play ─(re-signs APK)─► User deviceWhere Artifacts Live
| Artifact | Location | Secret / File Name |
|---|---|---|
| Upload keystore | GitHub repo secret (base64) | ANDROID_KEYSTORE_BASE64 |
| key.properties | GitHub repo secret (base64) | ANDROID_KEY_PROPERTIES_BASE64 |
| Google Play service account JSON | GitHub repo secret | GOOGLE_PLAY_JSON_KEY |
| App signing key | Google Play Console (managed by Google) | Never leaves Google |
Secrets are stored in the eshars-mobile-ui GitHub repository: Settings → Secrets and variables → Actions.
key.properties format
storePassword=<store-password>keyPassword=<key-password>keyAlias=uploadstoreFile=upload-keystore.jksPasswords are also stored in Key Vault (kv-eshars-mgmt), secrets:
mobile-android-store-passwordmobile-android-key-password
How the CI/CD Pipeline Uses These
The build.yml GitHub Actions workflow decodes both secrets and writes them
to disk before the Flutter build:
- name: Setup Android signing env: KEY_PROPERTIES_BASE64: ${{ secrets.ANDROID_KEY_PROPERTIES_BASE64 }} KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} run: | echo "$KEY_PROPERTIES_BASE64" | base64 --decode > android/key.properties echo "$KEYSTORE_BASE64" | base64 --decode > android/app/upload-keystore.jksNeither file is committed to the repo — they exist only for the duration of the runner job.
Local Development Setup
To sign a release build locally you need both artifacts from Key Vault or a team lead who can export them from GitHub secrets.
-
Get the base64 values (from GitHub secrets or Key Vault) and decode them:
Terminal window echo "<ANDROID_KEYSTORE_BASE64 value>" | base64 --decode > android/app/upload-keystore.jksecho "<ANDROID_KEY_PROPERTIES_BASE64 value>" | base64 --decode > android/key.properties -
Confirm both files are excluded by
.gitignorebefore proceeding. -
Build a signed AAB:
Terminal window flutter build appbundle --release
Upload Key Rotation
Rotate when the upload key is compromised, expired, or on a scheduled security policy basis.
1 — Generate a new upload keystore
keytool -genkey -v \ -keystore upload-keystore-new.jks \ -alias upload \ -keyalg RSA \ -keysize 2048 \ -validity 9125Fill in the Distinguished Name fields when prompted. Store the new passwords
in Key Vault (kv-eshars-mgmt) immediately.
2 — Export the public key for Google Play
keytool -export -rfc \ -keystore upload-keystore-new.jks \ -alias upload \ -file upload-key-new.pem3 — Request key rotation via Google Play Console
- Go to Google Play Console → [eSHARS app] → Setup → App signing.
- Click Request upload key reset, upload
upload-key-new.pem. - Submit to Google support — typical turnaround is 1–3 business days.
- Google will confirm when the new key is active. The old keystore is invalid for uploads from that point on.
4 — Update GitHub secrets
- Base64-encode the new files:
Terminal window base64 -w 0 upload-keystore-new.jks # → new value for ANDROID_KEYSTORE_BASE64base64 -w 0 key.properties # → new value for ANDROID_KEY_PROPERTIES_BASE64 - In the eshars-mobile-ui GitHub repo go to Settings → Secrets and variables → Actions and update both secrets.
5 — Update Key Vault
Update mobile-android-store-password and mobile-android-key-password in
kv-eshars-mgmt with the new values (add a new version; don’t delete the old
one until the rotation is confirmed working).
6 — Verify
Trigger the Build iOS and Android workflow with Deploy Android = true and
confirm the AAB uploads successfully to Google Play.
Recovery — Lost or Corrupted Upload Keystore
Because Google holds the app signing key, losing the upload keystore does not mean losing the app. You can request a new upload key from Google.
Lost the JKS file (secret deleted or corrupted)
- Generate a new keystore (Step 1 above).
- Follow the Google Play key rotation steps (Steps 2–6 above).
Lost the passwords but file is intact
Passwords cannot be recovered from a JKS file. Treat this the same as a lost keystore — generate a new one and request rotation via Google Play.
Emergency contacts & access
| Resource | Location |
|---|---|
| Google Play Console | Check android-release@authenticasolutions.com for account owner |
| GitHub secrets | eshars-mobile-ui repo → Settings → Secrets — any repo admin |
| Key Vault | kv-eshars-mgmt in rg-eshars-mgmt — Entra ID role or PIM |
Related
- HM-8427 — Android: Configure app signing and upload key
- Google Play App Signing docs
eshars-mobile-ui/android/app/build.gradle.kts— signing config wired tokey.propertieseshars-mobile-ui/.github/workflows/build.yml— GitHub Actions build and deploy workflow