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 -d
The 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.py
Certbox 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 --version
Global 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 --version
Create a client certificate:
certbox create <username>
Example:
certbox create alice
Output:
✓ 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 alice
Output:
✓ Certificate revoked successfully for user: alice
Serial number: 12345678901234567890
Revoked at: 2023-10-03T08:15:00.123456
Status: revoked
Get the current CRL:
certbox crl
You can redirect the output to save the CRL to a file:
certbox crl > crl.pem
View current configuration:
certbox config
Output:
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 settings
Or 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 alice
Or 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.py
The 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/alice
Response:
{
"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/pfx
Note: 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/alice
Response:
{
"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/info
Response:
{
"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 alice
certbox revoke alice
certbox info alice
Output:
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.pem
certbox config
certbox api --host 0.0.0.0 --port 8000
The 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
.pfx
file - Enter the password provided in the API response (
pfx_password
field) - Complete the import wizard
- Go to Settings → Privacy & Security → Certificates → View Certificates
- Click "Import" in the Your Certificates tab
- Select the downloaded
.pfx
file - Enter the password provided in the API response
- The certificate will be available for client authentication
- Double-click the downloaded
.pfx
file - 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.py
The 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:major
label -
Edge Cases:
- Documentation-only changes →
release:patch
or will default to patch - Internal refactoring →
refactor:
(patch) orrelease:patch
if 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.py
Create 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/certbox
Or copy and modify the example file:
cp .env.example .env
# Edit .env with your preferred values
Then simply run:
python main.py
Note: 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 config
This 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.py
Using .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.py
Using 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 config
You can view the current configuration by accessing the /config
endpoint:
curl http://localhost:8000/config
Response:
{
"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.