home / skills / hitoshura25 / claude-devtools / android-keystore-generation
This skill helps you generate production and local development Android keystores for release signing and integrates secure credential handling.
npx playbooks add skill hitoshura25/claude-devtools --skill android-keystore-generationReview the files below or copy the command above to add this skill to your agents.
---
name: android-keystore-generation
description: Generate production and local development keystores for Android release signing
category: android
version: 1.0.0
inputs:
- project_path: Path to Android project
- organization: Organization name for certificate
- country_code: 2-letter country code (optional)
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 (`keytool` command 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**
```bash
# 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**
```bash
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:**
```bash
# 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
```bash
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**
```bash
openssl rand -base64 24 | tr -d '/+=' | head -c 24 > /tmp/prod_password.txt
```
**Step 2b: Read and display the password**
```bash
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:
```bash
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**
```bash
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**
```bash
openssl rand -base64 24 | tr -d '/+=' | head -c 24 > /tmp/local_password.txt
```
**Step 3b: Read and display the password**
```bash
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:
```bash
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**
```bash
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
```bash
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
```bash
# 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:
```bash
# 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.jks` exists
- [ ] `keystores/local-dev-release.jks` exists
- [ ] `keystores/KEYSTORE_INFO.txt` exists with passwords
- [ ] `keystores/` is in `.gitignore`
This skill generates two Android keystores for app signing: a production keystore intended for CI/CD and a local development keystore for developer testing. It automates password generation, keystore creation, credential recording, and .gitignore updates while enforcing a mandatory organization-name confirmation. The process is designed to keep the production keystore out of developer machines and to produce a clear credentials file for CI integration.
The skill detects the organization name from the Android package in app/build.gradle.kts and prompts the user to confirm or override it. It offers two password options: you can provide a secure password manually (recommended) or let the tool generate passwords and save them temporarily. It then creates a keystores/ directory, generates a production PKCS12 keystore and a local-dev PKCS12 keystore with keytool, writes a KEYSTORE_INFO.txt with base64 and password details, and ensures keystores/ and *.jks are added to .gitignore.
What if keytool or openssl is not found?
Install a JDK that provides keytool (e.g., openjdk-17) and OpenSSL or use /dev/urandom as an alternative for password generation.
Can I skip confirming the detected organization?
No. The confirmation is mandatory because the organization value appears in the certificate Distinguished Name.
Where should I store the production keystore password?
Store production passwords in your CI secret store (e.g., GitHub Secrets) and avoid keeping them on developer machines.