Certbox is a lightweight REST API for managing client X.509 certificates using a custom CA. It supports issuing and revoking certificates, exporting .pfx files for browser use, and generating a CRL for mTLS setups with Nginx. Designed for simple, internal certificate workflows.
- Features
- Installation
- CLI Usage
- Directory Structure
- API Endpoints
- Usage Examples
- Browser Certificate Import
- Nginx mTLS Configuration
- Testing
- Versioning and Releases
- Configuration
- Security Notes
- License
- Certificate Authority Management: Automatically creates and manages a local CA
- Client Certificate Issuance: Create X.509 client certificates for users
- Certificate Revocation: Revoke certificates and update CRL
- PFX Export: Export certificates as .pfx files for browser installation
- Certificate Revocation List (CRL): Generate CRL for Nginx mTLS setups
- Structured Storage: Organized directory structure for certificates and keys
Certbox can be used both as a REST API service and as a command-line tool. Choose the installation method that best fits your use case.
- Clone the repository:
git clone https://github.com/gisce/certbox.git
cd certbox- Build and run with Docker:
# Build the Docker image
docker build -t certbox .
# Run the service
docker run -p 8000:8000 \
-v certbox_ca:/app/ca \
-v certbox_crts:/app/crts \
-v certbox_private:/app/private \
-v certbox_clients:/app/clients \
certbox- Or use Docker Compose:
docker compose up -dThe service will be available at http://localhost:8000.
# Clone the repository
git clone https://github.com/gisce/certbox.git
cd certbox
# Install the package
pip install -e .PyPI installation is not yet available. The package will be published to PyPI soon. Please check back for updates or use Option A to install from source in the meantime. Once installed, you can use Certbox in multiple ways:
# Check version and available commands
certbox --help
# Create a certificate
certbox create alice
# Start the API server
certbox api --host 0.0.0.0 --port 8000# Run the API server
python -m certbox api
# Use CLI commands
python -m certbox create alice# Install dependencies first
pip install -r requirements.txt
# Run the API server
python main.pyCertbox provides a comprehensive command-line interface for certificate management operations. The CLI offers the same functionality as the REST API but can be used directly from the command line.
To use the CLI, install Certbox using one of the methods above. The certbox command will be available system-wide after installation.
The CLI supports global options that affect all commands:
# Use a custom configuration file
certbox --config /path/to/custom.env create alice
# Show help
certbox --help
# Show version
certbox --versionGlobal Options:
--config PATH- Specify a custom configuration file (default: .env)--version- Show version and exit--help- Show help message
# Show help and available commands
certbox --help
# Show version
certbox --versionCreate a client certificate:
certbox create <username>Example:
certbox create aliceOutput:
✓ Certificate created successfully for user: alice
Serial number: 12345678901234567890
Valid from: 2023-10-03T08:12:29
Valid until: 2024-10-03T08:12:29
Certificate: /path/to/crts/alice.crt
Private key: /path/to/private/alice.key
PFX file: /path/to/clients/alice.pfx
Revoke a client certificate:
certbox revoke <username>Example:
certbox revoke aliceOutput:
✓ Certificate revoked successfully for user: alice
Serial number: 12345678901234567890
Revoked at: 2023-10-03T08:15:00.123456
Status: revoked
Get the current CRL:
certbox crlYou can redirect the output to save the CRL to a file:
certbox crl > crl.pemView current configuration:
certbox configOutput:
Current Certbox Configuration:
Certificate validity: 365 days
CA validity: 3650 days
Key size: 2048 bits
Country: ES
State/Province: Catalonia
Locality: Girona
Organization: GISCE-TI
CA Common Name: GISCE-TI CA
Start the API server:
certbox api [OPTIONS]Options:
--host TEXT: Host to bind the API server to (default: 0.0.0.0)--port INTEGER: Port to bind the API server to (default: 8000)
Examples:
# Start with default settings (0.0.0.0:8000)
certbox api
# Start on specific host and port
certbox api --host localhost --port 9000
# Start on all interfaces, port 8080
certbox api --host 0.0.0.0 --port 8080| Operation | CLI Command | API Endpoint |
|---|---|---|
| Create certificate | certbox create alice |
POST /certs/alice |
| Revoke certificate | certbox revoke alice |
POST /revoke/alice |
| Renew certificate | certbox renew alice |
POST /renew/alice |
| Get certificate info | certbox info alice |
GET /certs/alice/info |
| Get CRL | certbox crl |
GET /crl.pem |
| View config | certbox config |
GET /config |
| Start server | certbox api |
N/A |
The CLI respects the same environment variables as the API server. You can configure Certbox behavior using:
# Set custom configuration for CLI operations
export CERTBOX_ORGANIZATION="My Company"
export CERTBOX_LOCALITY="Barcelona"
export CERTBOX_CERT_VALIDITY_DAYS=730
export CERTBOX_ROOT_DIR="/var/lib/certbox"
# Then use CLI commands
certbox create alice
certbox config # Will show your custom settingsOr use a .env file in your working directory:
# Create .env file
echo "CERTBOX_ORGANIZATION=My Company" > .env
echo "CERTBOX_CERT_VALIDITY_DAYS=730" >> .env
echo "CERTBOX_ROOT_DIR=/var/lib/certbox" >> .env
# CLI commands will use these settings
certbox create aliceOr use a custom configuration file:
# Create a custom config file
cat > prod.env << EOF
CERTBOX_ROOT_DIR=/var/lib/certbox
CERTBOX_ORGANIZATION=Production Company
EOF
# Use it with the --config option
certbox --config prod.env create alice
certbox --config prod.env config- Clone the repository:
git clone https://github.com/gisce/certbox.git
cd certbox- Install dependencies:
pip install -r requirements.txt- Run the service:
python main.pyThe service will start on http://localhost:8000 and automatically create a CA if one doesn't exist.
Note: This method only provides the API server. For CLI functionality, use Method 2 installation options above.
The service creates and manages the following directory structure:
certbox/
├── ca/ # Certificate Authority files
│ ├── ca.crt # CA certificate
│ ├── ca.key # CA private key
│ ├── crl.pem # Certificate Revocation List
│ └── revoked_serials.txt # List of revoked certificate serials
├── crts/ # Client certificates
├── private/ # Client private keys
├── clients/ # PFX files for browser installation
└── requests/ # Certificate signing requests (future use)
When using Docker, these directories are mounted as persistent volumes:
certbox_ca- Contains the CA certificate, key and CRLcertbox_crts- Contains client certificatescertbox_private- Contains client private keyscertbox_clients- Contains PFX files for browser installationcertbox_requests- Contains certificate signing requests
These volumes ensure data persistence across container restarts.
- GET / - Service information and available endpoints
- POST /certs/{username} - Create a new client certificate
- POST /revoke/{username} - Revoke a client certificate
- POST /renew/{username} - Renew a client certificate
- GET /certs/{username}/info - Get information about a certificate
- GET /certs/{username}/pfx - Download PFX file for browser installation
- GET /crl.pem - Download the current CRL in PEM format
- GET /config - View current configuration settings
Certbox provides both REST API and CLI interfaces. Choose the method that works best for your workflow.
# Without authentication (when CERTBOX_API_TOKEN is not set)
curl -X POST http://localhost:8000/certs/alice
# With authentication (when CERTBOX_API_TOKEN is configured)
curl -X POST -H "Authorization: Bearer your-secret-token" http://localhost:8000/certs/aliceResponse:
{
"username": "alice",
"serial_number": "12345678901234567890",
"valid_from": "2023-10-03T08:12:29",
"valid_until": "2024-10-03T08:12:29",
"certificate_path": "/path/to/crts/alice.crt",
"private_key_path": "/path/to/private/alice.key",
"pfx_path": "/path/to/clients/alice.pfx",
"pfx_password": "c^016iKp6vx0"
}# Without authentication (when CERTBOX_API_TOKEN is not set)
curl -O -J http://localhost:8000/certs/alice/pfx
# With authentication (when CERTBOX_API_TOKEN is configured)
curl -O -J -H "Authorization: Bearer your-secret-token" http://localhost:8000/certs/alice/pfxNote: PFX files are password-protected for security. The password is provided in the response when creating or renewing certificates (see pfx_password field above). You'll need this password to import the PFX file into your browser.
# Without authentication (when CERTBOX_API_TOKEN is not set)
curl -X POST http://localhost:8000/revoke/alice
# With authentication (when CERTBOX_API_TOKEN is configured)
curl -X POST -H "Authorization: Bearer your-secret-token" http://localhost:8000/revoke/aliceResponse:
{
"username": "alice",
"serial_number": "12345678901234567890",
"revoked_at": "2023-10-03T08:15:00.123456",
"status": "revoked"
}curl -O http://localhost:8000/crl.pem# Without authentication (when CERTBOX_API_TOKEN is not set)
curl http://localhost:8000/certs/alice/info
# With authentication (when CERTBOX_API_TOKEN is configured)
curl -H "Authorization: Bearer your-secret-token" http://localhost:8000/certs/alice/infoResponse:
{
"username": "alice",
"serial_number": "12345678901234567890",
"status": "valid",
"valid_from": "2023-10-03T08:12:29+00:00",
"valid_until": "2024-10-03T08:12:29+00:00",
"is_revoked": false,
"subject": {
"country": "ES",
"state_province": "Catalonia",
"locality": "Girona",
"organization": "GISCE-TI",
"common_name": "alice"
},
"issuer": {
"organization": "GISCE-TI",
"common_name": "GISCE-TI CA"
},
"certificate_path": "/path/to/crts/alice.crt",
"private_key_path": "/path/to/private/alice.key",
"pfx_path": "/path/to/clients/alice.pfx",
"key_usage": {
"digital_signature": true,
"key_encipherment": true,
"key_agreement": false,
"key_cert_sign": false,
"crl_sign": false,
"content_commitment": false,
"data_encipherment": false
},
"extensions": {
"basic_constraints": {
"ca": false,
"path_length": null
},
"extended_key_usage": ["clientAuth"],
"subject_key_identifier": "a1b2c3d4e5f6...",
"authority_key_identifier": "f6e5d4c3b2a1..."
}
}The CLI provides the same functionality with a more direct interface:
certbox create alicecertbox revoke alicecertbox info aliceOutput:
Certificate Information for user: alice
Serial number: 12345678901234567890
Status: valid
Valid from: 2023-10-03T08:12:29+00:00
Valid until: 2024-10-03T08:12:29+00:00
Is revoked: False
Subject:
Country: ES
State Province: Catalonia
Locality: Girona
Organization: GISCE-TI
Common Name: alice
Issuer:
Organization: GISCE-TI
Common Name: GISCE-TI CA
File paths:
Certificate: /path/to/crts/alice.crt
Private key: /path/to/private/alice.key
PFX file: /path/to/clients/alice.pfx
Key usage:
✓ Digital Signature
✓ Key Encipherment
Extensions:
Basic Constraints:
ca: False
path_length: None
Extended Key Usage: clientAuth
Subject Key Identifier: a1b2c3d4e5f6...
Authority Key Identifier: f6e5d4c3b2a1...
certbox crl
# Save to file
certbox crl > crl.pemcertbox configcertbox api --host 0.0.0.0 --port 8000The generated PFX files are password-protected and can be imported into browsers for client certificate authentication:
- Go to Settings → Privacy and security → Security → Manage certificates
- Click "Import" in the Personal tab
- Select the downloaded
.pfxfile - Enter the password provided in the API response (
pfx_passwordfield) - Complete the import wizard
- Go to Settings → Privacy & Security → Certificates → View Certificates
- Click "Import" in the Your Certificates tab
- Select the downloaded
.pfxfile - Enter the password provided in the API response
- The certificate will be available for client authentication
- Double-click the downloaded
.pfxfile - Enter the password when prompted
- Choose the keychain to import to (usually "login")
- The certificate will be available in Keychain Access and Safari
Important: Keep the password secure as it protects your private key. The password is only provided when creating or renewing certificates.
To use the generated certificates and CRL with Nginx for mutual TLS authentication:
server {
listen 443 ssl;
server_name example.com;
# Server certificate
ssl_certificate /path/to/server.crt;
ssl_certificate_key /path/to/server.key;
# Client certificate verification
ssl_client_certificate /path/to/certbox/ca/ca.crt;
ssl_verify_client on;
ssl_crl /path/to/certbox/ca/crl.pem;
location / {
# Your application
proxy_pass http://backend;
# Pass client certificate info to backend
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-Verify $ssl_client_verify;
}
}Run the included test suite with pytest:
# Install testing dependencies
pip install -r requirements-dev.txt
# Run API tests
pytest tests/test_api.py -v
# Run CLI tests
pytest tests/test_cli.py -v
# Run all tests
pytest tests/ -v
# Or run with the original method (deprecated)
python tests/test_api.pyThe test suite includes:
- API endpoint testing - REST API functionality and responses
- CLI command testing - Command-line interface functionality
- Configuration validation - Environment and .env file configuration
- Certificate manager functionality - Core certificate operations
- Python version compatibility checks - Support for Python 3.8+
- Integration tests with mocked dependencies - Isolated testing without external dependencies
Tests are also run automatically via GitHub Actions on Python 3.8, 3.9, 3.10, 3.11, and 3.12.
Certbox follows Semantic Versioning and uses an automated hybrid versioning system that combines conventional commits with manual label overrides.
-
Automatic Version Detection: The system analyzes commit messages following Conventional Commits format:
feat:→ Minor version bump (new features)fix:→ Patch version bump (bug fixes)feat!orBREAKING CHANGE:→ Major version bump (breaking changes)
-
Manual Override: You can override automatic detection by adding exactly one release label to your PR:
release:major→ Major version bump (x.0.0)release:minor→ Minor version bump (x.y.0)release:patch→ Patch version bump (x.y.z)
-
Validation: All PRs are automatically validated to ensure consistency between commits and labels.
When a PR is merged to main, the system automatically:
- ✅ Determines the appropriate version bump
- ✅ Updates the version in
certbox/__init__.py - ✅ Generates/updates the CHANGELOG.md
- ✅ Creates a git tag
- ✅ Creates a GitHub release
- ✅ Publishes to PyPI (if configured)
When contributing to Certbox:
-
Use Conventional Commits (recommended):
feat(api): add certificate renewal endpoint fix(cli): resolve config file parsing issue docs: update installation instructions -
Or use Release Labels: Add exactly one label (
release:major,release:minor, orrelease:patch) to your PR -
Breaking Changes: Use
feat!:or includeBREAKING CHANGE:in the commit body, or addrelease:majorlabel -
Edge Cases:
- Documentation-only changes →
release:patchor will default to patch - Internal refactoring →
refactor:(patch) orrelease:patchif breaking - CI/build changes →
ci:orbuild:(patch)
- Documentation-only changes →
The PR validation system will check your changes and provide feedback on the expected version bump.
The service can be configured using environment variables or a .env file. All settings have sensible defaults:
CERTBOX_CERT_VALIDITY_DAYS- Client certificate validity in days (default: 365)CERTBOX_CA_VALIDITY_DAYS- CA certificate validity in days (default: 3650)CERTBOX_KEY_SIZE- RSA key size in bits (default: 2048)CERTBOX_PFX_PASSWORD_LENGTH- PFX file password length (default: 12)
CERTBOX_API_TOKEN- API authentication token (default: "" - authentication disabled)
When CERTBOX_API_TOKEN is set, the following endpoints require authentication:
POST /certs/{username}- Create certificatePOST /revoke/{username}- Revoke certificateGET /certs/{username}/pfx- Download PFX file
Public endpoints (no authentication required):
GET /- Service informationGET /config- Configuration viewGET /crl.pem- Certificate revocation list
CERTBOX_COUNTRY- Country code (default: "ES")CERTBOX_STATE_PROVINCE- State or province (default: "Catalonia")CERTBOX_LOCALITY- City or locality (default: "Girona")CERTBOX_ORGANIZATION- Organization name (default: "GISCE-TI")CERTBOX_CA_COMMON_NAME- CA common name (default: "GISCE-TI CA")
CERTBOX_ROOT_DIR- Root directory for all certificate files (default: project directory)
When CERTBOX_ROOT_DIR is set, all certificate directories (ca/, crts/, private/, clients/, requests/) will be created under this path instead of the project directory.
# Run with custom configuration including authentication
CERTBOX_ORGANIZATION="My Company" \
CERTBOX_LOCALITY="Barcelona" \
CERTBOX_CERT_VALIDITY_DAYS=730 \
CERTBOX_ROOT_DIR="/var/lib/certbox" \
CERTBOX_API_TOKEN="my-secret-api-token" \
python main.pyCreate a .env file in the project root directory:
# .env file
CERTBOX_CERT_VALIDITY_DAYS=730
CERTBOX_CA_VALIDITY_DAYS=7300
CERTBOX_COUNTRY=ES
CERTBOX_STATE_PROVINCE=Catalonia
CERTBOX_LOCALITY=Barcelona
CERTBOX_ORGANIZATION=My Company
CERTBOX_CA_COMMON_NAME=My Company CA
CERTBOX_KEY_SIZE=4096
CERTBOX_ROOT_DIR=/var/lib/certboxOr copy and modify the example file:
cp .env.example .env
# Edit .env with your preferred valuesThen simply run:
python main.pyNote: Environment variables take precedence over .env file values.
You can specify a custom configuration file using the CLI --config option:
# Create a custom config file
cat > /etc/certbox/production.env << EOF
CERTBOX_ROOT_DIR=/var/lib/certbox
CERTBOX_ORGANIZATION=Production Company
CERTBOX_CERT_VALIDITY_DAYS=365
CERTBOX_CA_VALIDITY_DAYS=3650
EOF
# Use the custom config file with CLI
certbox --config /etc/certbox/production.env create alice
certbox --config /etc/certbox/production.env configThis method is particularly useful for:
- Production deployments with specific configurations
- Multiple environments (dev, staging, production)
- Shared team configurations
- System-wide installations
Using environment variables:
# Run with custom configuration including custom storage location
CERTBOX_ORGANIZATION="My Company" \
CERTBOX_LOCALITY="Barcelona" \
CERTBOX_CERT_VALIDITY_DAYS=730 \
CERTBOX_ROOT_DIR="/var/lib/certbox" \
python main.pyUsing .env file:
# Create .env file with your configuration
echo "CERTBOX_ORGANIZATION=My Company" > .env
echo "CERTBOX_LOCALITY=Barcelona" >> .env
echo "CERTBOX_CERT_VALIDITY_DAYS=730" >> .env
echo "CERTBOX_ROOT_DIR=/var/lib/certbox" >> .env
# Run the service
python main.pyUsing custom config file with CLI:
# Create a custom configuration file
cat > custom.env << EOF
CERTBOX_ROOT_DIR=/tmp/test_certs
CERTBOX_ORGANIZATION=Test Company
EOF
# Use custom config with CLI commands
certbox --config custom.env create testuser
certbox --config custom.env configYou can view the current configuration by accessing the /config endpoint:
curl http://localhost:8000/configResponse:
{
"cert_validity_days": 365,
"ca_validity_days": 3650,
"key_size": 2048,
"country": "ES",
"state_province": "Catalonia",
"locality": "Girona",
"organization": "GISCE-TI",
"ca_common_name": "GISCE-TI CA"
}Note: When CERTBOX_ROOT_DIR is configured, the response will also include:
{
"cert_validity_days": 365,
"ca_validity_days": 3650,
"key_size": 2048,
"country": "ES",
"state_province": "Catalonia",
"locality": "Girona",
"organization": "GISCE-TI",
"ca_common_name": "GISCE-TI CA",
"root_dir": "/var/lib/certbox"
}- The CA private key is stored unencrypted for simplicity
- This service is designed for internal use cases
- For production use, consider implementing proper authentication and authorization
- Regularly backup the CA directory
MIT License - see LICENSE file for details.
