| name | android-keystore-generation |
| description | Generate production and local development keystores for Android release signing |
| category | android |
| version | 1.0.0 |
| inputs | [object Object], [object Object], [object Object] |
| outputs | keystores/production-release.jks, keystores/local-dev-release.jks, keystores/KEYSTORE_INFO.txt |
| verify | ls keystores/*.jks && cat keystores/KEYSTORE_INFO.txt |
Android Keystore Generation
Generates dual keystores for Android release signing: production (CI/CD only) and local development.
Prerequisites
- JDK installed (
keytoolcommand available) - Write access to project directory
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
| project_path | Yes | . | Android project root |
| organization | Yes | - | Organization name for certificate DN |
| country_code | No | US | 2-letter country code |
Process
Organization Name
ALWAYS prompt the user, but provide the auto-detected value as default.
Step 1: Detect organization from package name
# Extract package name from build.gradle.kts
PACKAGE=$(grep "applicationId" app/build.gradle.kts | sed 's/.*"\(.*\)".*/\1/')
echo "Package: $PACKAGE"
# Extract second segment: com.{ORG}.app → ORG
ORG=$(echo $PACKAGE | cut -d. -f2)
echo "Detected organization: $ORG"
Step 2: MANDATORY - Ask the user for confirmation
⛔ DO NOT SKIP THIS PROMPT
Ask the user:
"The detected organization name is {ORG}. Press Enter to use this, or type a different name:"
Wait for user response. Use their input if provided, otherwise use the detected default.
Step 3: Store the confirmed organization name
ORGANIZATION="{confirmed_org_name}"
echo "Using organization: $ORGANIZATION"
Why this matters: The organization appears in the certificate's Distinguished Name. While it doesn't affect app functionality, users may want to customize it.
Password Generation Options
Choose one option for keystore passwords:
Option 1 (Recommended): User-Provided Password
Ask the user to provide a password for the production keystore:
"Please enter a password for the production keystore (minimum 12 characters, mix of letters, numbers, and symbols):"
Security benefits:
- Password never visible to the agent during the conversation
- User has complete control over password strength and storage
- Reduces risk of password exposure in logs or conversation history
Instructions for user:
# User will run keytool command manually with their chosen password
# Example:
keytool -genkeypair -v \
-keystore keystores/production-release.jks \
-storetype PKCS12 \
-alias upload \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass "YOUR_PASSWORD_HERE" \
-keypass "YOUR_PASSWORD_HERE" \
-dname "CN=Android Release, OU=Android, O={ORGANIZATION}, C=US"
Option 2: Generated Password
If the user prefers a generated password, use the automated generation steps below.
Note: The agent will have access to this password during the session. The password will be stored in temporary files and KEYSTORE_INFO.txt.
"Would you like me to generate a secure password? (The agent will see this password during generation)"
If yes, proceed with the automated generation steps.
Step 1: Create Keystores Directory
mkdir -p keystores
Step 2: Generate Production Keystore
SECURITY: This keystore is for CI/CD only. Never use locally.
⚠️ IMPORTANT: Run each command in a SEPARATE bash call. Do NOT combine commands.
Step 2a: Generate password and save to file
openssl rand -base64 24 | tr -d '/+=' | head -c 24 > /tmp/prod_password.txt
Step 2b: Read and display the password
cat /tmp/prod_password.txt
📝 Copy this password now - you'll need it for the keytool command and KEYSTORE_INFO.txt
Step 2c: Generate the keystore
Replace {PASSWORD} with the password from Step 2b, and {ORGANIZATION} with the confirmed organization name:
keytool -genkeypair -v \
-keystore keystores/production-release.jks \
-storetype PKCS12 \
-alias upload \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass "{PASSWORD}" \
-keypass "{PASSWORD}" \
-dname "CN=Android Release, OU=Android, O={ORGANIZATION}, C=US"
If keytool prompts for confirmation, type yes and press Enter.
Step 2d: Verify keystore was created
ls -la keystores/production-release.jks
Expected output:
-rw------- 1 user staff 2557 Dec 11 10:30 keystores/production-release.jks
Step 3: Generate Local Development Keystore
⚠️ IMPORTANT: Run each command in a SEPARATE bash call. Do NOT combine commands.
Step 3a: Generate password and save to file
openssl rand -base64 24 | tr -d '/+=' | head -c 24 > /tmp/local_password.txt
Step 3b: Read and display the password
cat /tmp/local_password.txt
📝 Copy this password now - you'll need it for the keytool command and KEYSTORE_INFO.txt
Step 3c: Generate the keystore
Replace {PASSWORD} with the password from Step 3b:
keytool -genkeypair -v \
-keystore keystores/local-dev-release.jks \
-storetype PKCS12 \
-alias local-dev \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass "{PASSWORD}" \
-keypass "{PASSWORD}" \
-dname "CN=Local Development, OU=Development, O=Local, C=US"
If keytool prompts for confirmation, type yes and press Enter.
Step 3d: Verify keystore was created
ls -la keystores/local-dev-release.jks
Expected output:
-rw------- 1 user staff 2557 Dec 11 10:30 keystores/local-dev-release.jks
Step 4: Create Credentials File
cat > keystores/KEYSTORE_INFO.txt << EOF
Production Keystore
===================
File: production-release.jks
Alias: upload
Store Password: $PROD_PASSWORD
Key Password: $PROD_PASSWORD (same as store - PKCS12 requirement)
⚠️ SECURITY: CI/CD ONLY - Never use on developer machines
GitHub Secrets:
SIGNING_KEY_STORE_BASE64: $(base64 -w 0 keystores/production-release.jks 2>/dev/null || base64 -i keystores/production-release.jks)
SIGNING_KEY_ALIAS: upload
SIGNING_STORE_PASSWORD: $PROD_PASSWORD
SIGNING_KEY_PASSWORD: $PROD_PASSWORD
---
Local Development Keystore
==========================
File: local-dev-release.jks
Alias: local-dev
Store Password: $LOCAL_PASSWORD
Key Password: $LOCAL_PASSWORD
Add to ~/.gradle/gradle.properties:
SIGNING_KEY_STORE_PATH=$(pwd)/keystores/local-dev-release.jks
SIGNING_KEY_ALIAS=local-dev
SIGNING_STORE_PASSWORD=$LOCAL_PASSWORD
SIGNING_KEY_PASSWORD=$LOCAL_PASSWORD
EOF
Step 5: Update .gitignore
# Add to .gitignore if not present
grep -q "keystores/" .gitignore 2>/dev/null || echo "keystores/" >> .gitignore
grep -q "*.jks" .gitignore 2>/dev/null || echo "*.jks" >> .gitignore
Verification
MANDATORY: Run these commands:
# Verify keystores exist
ls -la keystores/*.jks
# Verify credentials documented
cat keystores/KEYSTORE_INFO.txt
# Verify gitignored
grep "keystores" .gitignore
Expected output:
- Two .jks files in keystores/
- KEYSTORE_INFO.txt with passwords
- keystores/ in .gitignore
Outputs
| Output | Location | Description |
|---|---|---|
| Production keystore | keystores/production-release.jks | For CI/CD only |
| Local keystore | keystores/local-dev-release.jks | For local testing |
| Credentials | keystores/KEYSTORE_INFO.txt | Passwords and setup info |
Troubleshooting
"keytool: command not found"
Cause: JDK not installed or not in PATH
Fix: Install JDK 17: brew install openjdk@17 (macOS) or apt install openjdk-17-jdk (Linux)
"openssl: command not found"
Cause: OpenSSL not installed
Fix: Use alternative password generation: head -c 24 /dev/urandom | base64
Completion Criteria
-
keystores/production-release.jksexists -
keystores/local-dev-release.jksexists -
keystores/KEYSTORE_INFO.txtexists with passwords -
keystores/is in.gitignore