home / skills / hitoshura25 / claude-devtools / android-keystore-generation

android-keystore-generation skill

/skills/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-generation

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
8.1 KB
---
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`

Overview

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.

How this skill works

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.

When to use it

  • Preparing release signing assets for CI/CD pipelines while keeping secrets out of developer machines
  • Setting up a reproducible local development keystore for team QA and testing
  • Bootstrapping signing credentials for a new Android project
  • When you need both a CI-only keystore and a separate local keystore with documented credentials

Best practices

  • Always confirm the detected organization name when prompted; do not skip the confirmation step
  • Prefer user-provided passwords for production keystores to avoid exposing secrets to the agent
  • Treat the production keystore as CI/CD-only; never copy it to developer machines
  • Store production keystore secrets in your CI platform (GitHub Secrets) and add keystores/ to .gitignore
  • Verify keystore files and KEYSTORE_INFO.txt exist and that keystores/ is ignored before committing

Example use cases

  • Generate a CI-only production keystore and upload it to GitHub Actions secrets for Play Store signing
  • Create a local development keystore for developers to build and test release-signed APKs locally
  • Onboard a new Android repository by auto-detecting organization from build.gradle.kts and producing keystores
  • Regenerate compromised local keystore credentials while keeping the CI production keystore unchanged

FAQ

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.