Skip to content

Commit

Permalink
Update 4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
muhammadrizwan87 committed Nov 22, 2024
1 parent 7e932eb commit b47d9cd
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 154 deletions.
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# KeySigner: Keystore Management and APK Signing Tool

## Overview
KeySigner is a command-line python tool designed to simplify keystore management and APK signing. It integrates KeyTool and ApkSigner, allowing developers to easily generate keystores (JKS, BKS, PKCS12), manage certificates, and sign APKs with custom keystores supporting multiple signature schemes (v1, v2, v3). Whether you need to create, convert, or sign, KeySigner provides a user-friendly interface for developers of all levels and efficient workflow.
KeySigner is a command-line python tool designed to simplify keystore management and APK signing. It integrates KeyTool and ApkSigner, allowing developers to easily generate and migrate keystores (JKS, BKS, PKCS12), extract certificates and private keys, and sign APKs with custom keystores, supporting multiple signature schemes (v1, v2, v3). Whether you need to create, migrate, or sign, KeySigner provides a user-friendly interface for developers of all levels and efficient workflow.

---

## Features

- **Generate Keystores**: Easily create JKS, BKS, or PKCS12 keystores.
- **Keystore Migration**: Seamlessly convert JKS or BKS keystores to PKCS12 format.
- **Certificate Extraction**: Securely extract x509 certificates and private keys in PKCS8 format from PKCS12 keystores.
- **Keystore Info Display**: Displays detailed information about the selected keystore, including certificate fingerprints, public key algorithm, expiries and more.
- **Generate Keystores**: Easily generate JKS, BKS, and PKCS12 keystores.
- **Keystore Migration**: Seamlessly migrate JKS, BKS and PKCS12 to each other.
- **Export PKCS12 to PEM**: Securely extract x509 certificates and private keys in PKCS8 format from PKCS12 keystores.
- **Import PEM to PKCS12**: Import the certificate and private key as new entries in the PKCS12 keystore.
- **Keystore Info Display**: Displays detailed information about the selected keystore, including certificate fingerprints, public key algorithm, expiries and more. If you want a detailed analysis of your keystores we recommend our other tool SigTool. [Check out SigTool...](https://github.com/muhammadrizwan87/sigtool)
- **APK Signing**: sign APK files with custom keystores, supporting multiple signing formats (v1, v2, v3).

---
Expand Down Expand Up @@ -87,7 +88,7 @@ To build KeySigner from source:

```bash
python -m build
pip install --force-reinstall dist/keysigner-3.0-py3-none-any.whl
pip install --force-reinstall dist/keysigner-4.0-py3-none-any.whl
```

---
Expand All @@ -102,15 +103,13 @@ Example:
$ keysigner
Select an option:
1. Create new JKS keystore
2. Create new BKS keystore
3. Create new PKCS12 keystore
4. Migrate JKS to PKCS12
5. Migrate BKS to PKCS12
6. Convert PKCS12 to PEM and extract x509 certificate and private key
7. Show keystore information
8. Sign APK
9. Show Notes
1. Generate new keystore (JKS/BKS/PKCS12)
2. Migrate keystores to each other (JKS/BKS/PKCS12)
3. Convert PKCS12 to PEM and extract certificate and key
4. Convert PEM to PKCS12
5. Show keystore information
6. Sign APK
7. Show Notes
q. Quit
```

Expand Down
12 changes: 7 additions & 5 deletions keysigner/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from .keystore_manager import KeystoreManager
from .keystore_generator import KeystoreGenerator
from .keystore_migrator import KeystoreMigrator
from .pkcs12_converter import PKCS12Converter
from .pkcs12_to_pem import PKCS12ToPEM
from .pem_to_pkcs12 import PEMToPKCS12
from .keystore_info import KeystoreInfo
from .signer import APKSigner
from .apk_signer import APKSigner
from .utils import *

__all__ = [
"KeystoreManager",
"KeystoreGenerator",
"KeystoreMigrator",
"PKCS12Converter",
"PKCS12ToPEM",
"PEMToPKCS12",
"KeystoreInfo",
"APKSigner",
"color_text",
Expand Down
4 changes: 2 additions & 2 deletions keysigner/signer.py → keysigner/apk_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def sign_with_keystores(self):

def sign_with_keystore(self, keystore_type):
keystore_path = validate_input(cyan_text(f"Enter {keystore_type.upper()} keystore path: "), path=True)
store_pass = validate_input(cyan_text("Enter keystore password: "), password_ck=True)
store_pass = validate_input(cyan_text("Enter keystore password: "), password=True, min_length=6)
alias = validate_input(cyan_text("Enter alias name: "))
base_name = os.path.splitext(os.path.basename(self.apk_file))[0]
signed_apk = os.path.join(self.output_path, f"{base_name}_signed.apk")
Expand All @@ -87,7 +87,7 @@ def sign_with_keystore(self, keystore_type):
]

if keystore_type.lower() == 'jks':
key_pass = validate_input(cyan_text("Enter alias password: "), password_ck=True)
key_pass = validate_input(cyan_text("Enter alias password (default: same as keystore password): "), pass_opt=store_pass, min_length=6)
cmd.extend(['--key-pass', f'pass:{key_pass}'])

cmd.extend([self.apk_file])
Expand Down
39 changes: 29 additions & 10 deletions keysigner/keystore_manager.py → keysigner/keystore_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import subprocess
from .utils import *

class KeystoreManager:
def __init__(self, store_type):
self.store_type = store_type
class KeystoreGenerator:
def __init__(self):
self.store_type = None
self.store_name = None
self.store_pass = None
self.alias = None
Expand All @@ -20,13 +20,20 @@ def __init__(self, store_type):

def set_keystore_details(self):
print_blue("\n--- Setting Keystore Details ---")
while True:
keystore_type = validate_input(cyan_text("Enter new keystore type (JKS/BKS/PKCS12): ")).upper()
if keystore_type in ['JKS', 'BKS', 'PKCS12']:
self.store_type = keystore_type
break
else:
print_red("Invalid keystore type. Please try again.")
self.store_name = validate_input(cyan_text("Enter keystore name: "))
self.store_pass = validate_input(cyan_text("Enter keystore password: "), password=True)
self.store_pass = validate_input(cyan_text("Enter keystore password: "), password=True, min_length=8)
self.alias = validate_input(cyan_text("Enter alias name: "))
if self.store_type == 'PKCS12':
self.key_pass = self.store_pass
else:
self.key_pass = validate_input(cyan_text("Enter alias password: "), password=True)
self.key_pass = validate_input(cyan_text("Enter alias password (default: same as keystore password): "), pass_opt=self.store_pass, min_length=8)
self.validity = validate_input(cyan_text("Enter validity (days, default 36500): "), required=False) or '36500'
self.dname = self.generate_dname()
if self.store_type == 'BKS':
Expand Down Expand Up @@ -54,7 +61,19 @@ def generate_dname(self):
l = validate_input(cyan_text("Enter L (Locality): "), required=False)
st = validate_input(cyan_text("Enter ST (State): "), required=False)
c = validate_input(cyan_text("Enter C (Country Code): "), required=False)
return f"CN={cn}, OU={ou}, O={o}, L={l}, ST={st}, C={c}"

if not any([cn, ou, o, l, st, c]):
return "CN=Unknown"

dname_parts = []
if cn: dname_parts.append(f"CN={cn}")
if ou: dname_parts.append(f"OU={ou}")
if o: dname_parts.append(f"O={o}")
if l: dname_parts.append(f"L={l}")
if st: dname_parts.append(f"ST={st}")
if c: dname_parts.append(f"C={c}")

return ", ".join(dname_parts)

def generate_keytool_command(self):
cmd = [
Expand All @@ -77,20 +96,20 @@ def generate_keytool_command(self):
cmd.extend(['-providerclass', self.provider_class, '-providerpath', self.provider_path])
return cmd

def create_keystore(self):
def generate_keystore(self):
try:
self.set_keystore_details()
self.cmd = self.generate_keytool_command()
print_blue("\n--- Executing KeyTool Command ---")
result = subprocess.run(self.cmd)

if result.returncode != 0:
print_red("Keystore creation failed.")
print_red("Keystore generation failed.")
return

print_green("KeyTool command executed successfully!")
print_green(f"Keystore created at: {self.store_path}")
self.handle_and_generate_command(self.cmd, "Keystore Command")
print_green(f"Keystore {self.store_type} generated at: {self.store_path}")
self.handle_and_generate_command(self.cmd, f"Keystore command to generate new {self.store_type}")

if self.store_type != 'BKS':
self.generate_apksigner_command(self.store_path, self.store_pass, self.alias, self.key_pass)
Expand Down
4 changes: 2 additions & 2 deletions keysigner/keystore_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self):
def get_keystore_info(self):
print_blue("\n--- Gathering Keystore Information ---")
self.keystore_path = validate_input(cyan_text("Enter keystore path: "), path=True)
self.store_pass = validate_input(cyan_text("Enter keystore password: "), password_ck=True)
self.store_pass = validate_input(cyan_text("Enter keystore password: "), password=True, min_length=6)
self.determine_keystore_type()

def determine_keystore_type(self):
Expand All @@ -26,7 +26,7 @@ def determine_keystore_type(self):
self.keystore_type = 'PKCS12'
else:
while True:
keystore_type = validate_input(cyan_text("Keystore type not detected. Please enter keystore type (JKS/BKS/PKCS12): ")).upper()
keystore_type = validate_input(cyan_text("Please enter keystore type (JKS/BKS/PKCS12): ")).upper()
if keystore_type in ['JKS', 'BKS', 'PKCS12']:
self.keystore_type = keystore_type
break
Expand Down
Loading

0 comments on commit b47d9cd

Please sign in to comment.