gplay-signing-setup
Android app signing, keystores, and Play App Signing setup. Use when configuring signing for new apps or migrating to Play App Signing.
What this skill does
# Android App Signing Setup
Use this skill when you need to set up or manage app signing for Google Play.
## Understanding Android App Signing
Android apps must be signed with a certificate before upload. Two signing approaches:
1. **App Signing by Google Play** (Recommended) - Google manages your signing key
2. **Manual Signing** (Legacy) - You manage your signing key
## Create a New Keystore
### Generate keystore
```bash
keytool -genkey -v \
-keystore release.keystore \
-alias my-app-key \
-keyalg RSA \
-keysize 2048 \
-validity 10000
```
**You'll be prompted for:**
- Keystore password (store securely!)
- Key password (can be same as keystore password)
- Your name/organization details
### Keystore file location
- **Development**: Keep locally, never commit to git
- **Production**: Store in secure location (password manager, secrets vault)
- **CI/CD**: Use encrypted secrets
## Configure Gradle Signing
### gradle.properties (gitignored)
```properties
KEYSTORE_FILE=/path/to/release.keystore
KEYSTORE_PASSWORD=your_keystore_password
KEY_ALIAS=my-app-key
KEY_PASSWORD=your_key_password
```
### app/build.gradle
```groovy
android {
signingConfigs {
release {
storeFile file(project.property('KEYSTORE_FILE'))
storePassword project.property('KEYSTORE_PASSWORD')
keyAlias project.property('KEY_ALIAS')
keyPassword project.property('KEY_PASSWORD')
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
```
### Using environment variables (CI/CD)
```groovy
android {
signingConfigs {
release {
storeFile file(System.getenv("KEYSTORE_FILE") ?: "release.keystore")
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
}
```
## Play App Signing Setup
### Enable Play App Signing (New App)
1. **Build and sign AAB** with upload key:
```bash
./gradlew bundleRelease
```
2. **Upload AAB** to Play Console:
```bash
gplay release \
--package com.example.app \
--track internal \
--bundle app-release.aab
```
3. **Google Play generates app signing key** automatically
4. **Download upload certificate**:
- Go to Play Console → App → Setup → App signing
- Download "Upload certificate" (will be used for future uploads)
### Migrate Existing App to Play App Signing
If your app uses manual signing:
1. **Export upload key**:
```bash
keytool -export -rfc \
-keystore release.keystore \
-alias my-app-key \
-file upload_cert.pem
```
2. **Encrypt private key** (required by Google):
```bash
# Generate password for encryption
openssl rand -base64 32 > encryption_password.txt
# Export and encrypt private key
keytool -importkeystore \
-srckeystore release.keystore \
-destkeystore encrypted.p12 \
-deststoretype PKCS12 \
-srcalias my-app-key \
-deststorepass $(cat encryption_password.txt)
```
3. **Upload to Play Console**:
- Go to Play Console → App → Setup → App signing
- Choose "Export and upload a key"
- Upload encrypted.p12 and encryption_password.txt
4. **Download new upload key**:
- After migration, download new upload certificate
- Use this for all future uploads
## Verify Keystore
### List keys in keystore
```bash
keytool -list -v -keystore release.keystore
```
### Check certificate validity
```bash
keytool -list -v \
-keystore release.keystore \
-alias my-app-key \
| grep Valid
```
### Extract certificate fingerprint
```bash
# SHA-256 (for Firebase, etc.)
keytool -list -v \
-keystore release.keystore \
-alias my-app-key \
| grep SHA256
```
## Verify APK/AAB Signature
### Check AAB signature
```bash
jarsigner -verify -verbose -certs app-release.aab
```
### Check APK signature
```bash
jarsigner -verify -verbose -certs app-release.apk
```
## CI/CD Signing
### GitHub Actions Example
```yaml
- name: Decode keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > release.keystore
- name: Build signed AAB
env:
KEYSTORE_FILE: release.keystore
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: |
./gradlew bundleRelease
- name: Clean up keystore
if: always()
run: rm -f release.keystore
```
### Store keystore as base64
```bash
# Encode keystore to base64
base64 -i release.keystore -o keystore_base64.txt
# Add to GitHub Secrets as KEYSTORE_BASE64
```
## Upload Certificate Management
### Register upload certificate for existing app
If you lose your upload key:
1. **Generate new keystore** (as shown above)
2. **Export certificate**:
```bash
keytool -export -rfc \
-keystore new-upload.keystore \
-alias my-app-key \
-file new_upload_cert.pem
```
3. **Contact Google Play support** to reset upload key:
- Go to Play Console → Help
- Request upload certificate reset
- Provide new_upload_cert.pem
## Security Best Practices
### DO:
- ✅ Use strong passwords (16+ characters)
- ✅ Store keystore in multiple secure locations
- ✅ Use different keys for different apps
- ✅ Enable Play App Signing
- ✅ Keep upload key separate from production
- ✅ Document key details securely (not passwords!)
- ✅ Set calendar reminder for key expiration (10-25 years)
### DON'T:
- ❌ Commit keystores to git
- ❌ Share keystores via email/chat
- ❌ Use weak or obvious passwords
- ❌ Store passwords in code
- ❌ Forget to backup keystores
- ❌ Use same key for debug and release
## Keystore Backup Strategy
### What to backup:
1. Keystore file (`release.keystore`)
2. Key alias name
3. Keystore password (in password manager)
4. Key password (in password manager)
### Where to backup:
- Password manager (1Password, LastPass, etc.)
- Encrypted cloud storage
- Company secrets vault
- Physical secure storage (for enterprise)
### Test restore process:
```bash
# Verify you can use the backup
keytool -list -v -keystore backup/release.keystore
```
## Troubleshooting
### "jarsigner: unable to sign jar"
- Check keystore password is correct
- Verify keystore file path
- Ensure keystore alias exists
### "Failed to read key from keystore"
- Key alias might be wrong
- Key password might be wrong
- Keystore might be corrupted
### "Upload certificate doesn't match"
- You're using wrong keystore
- Keystore was regenerated (contact support)
- Using app signing key instead of upload key
### Lost keystore?
- If using Play App Signing: Contact Google to reset upload key
- If not using Play App Signing: Cannot recover, must publish new app
## Multiple Apps, Multiple Keys
### Organize multiple keystores
```
~/keystores/
├── app1-release.keystore
├── app2-release.keystore
└── app3-release.keystore
```
### Use different gradle.properties per app
```properties
# app1/gradle.properties
KEYSTORE_FILE=~/keystores/app1-release.keystore
KEY_ALIAS=app1-key
# app2/gradle.properties
KEYSTORE_FILE=~/keystores/app2-release.keystore
KEY_ALIAS=app2-key
```
## Key Rotation
Android apps are typically signed with long-lived keys (10-25 years), but if you need to rotate:
1. **Only possible with Play App Signing enabled**
2. **Contact Google Play Support** to rotate app signing key
3. **Upload new upload certificate** when prompted
4. **All future builds** must use new upload key
## Documentation Template
Keep this info in your password manager:
```
App Name: My Awesome App
Package: com.example.app
Keystore File: release.keystore (backed up in Dropbox)
Keystore Password: [IN PASSWORD MANAGER]
Key Alias: my-app-key
Key Password: [IN PASSWORD MANAGER]
Certificate Validity: Valid until 2035-02-05
SHA-256 Fingerprint: AB:CD:EF:12:...
Play App Signing: Enabled
Notes: Upload key only, Google manages app signing key
```
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.