Files
simple-ca/README.md
T

266 lines
9.1 KiB
Markdown

# Simple CA
[![test](https://gitea.koszewscy.waw.pl/slawek/simple-ca/actions/workflows/test.yaml/badge.svg)](https://gitea.koszewscy.waw.pl/slawek/simple-ca/actions?workflow=test.yaml)
Tools for creating and managing a simple Certificate Authority for testing and development. Three implementations are provided — pick whichever fits your environment:
| | `simple-ca.sh` | `simple-ca.py` | Go binary |
|---|---|---|---|
| Usage | sourced function library | CLI | compiled CLI |
| Config file | none | `simple-ca.json` | none |
| AIA base URL | `aia_base_url.txt` | `simple-ca.json` | `aia_base_url.txt` |
| Certificate history | no | yes | no |
| CRL generation | no | yes | no |
| Revocation | no | yes | no |
| PFX (`--apple-openssl`) | yes | yes | yes |
| Requires | bash/zsh + openssl | Python 3.8+ + openssl | none (native crypto) |
All variants share the same directory layout and produce interchangeable files.
## Directory layout
```
CA_DIR/
ca_cert.pem root CA certificate
ca_key.pem root CA private key
ca_bundle.pem root + all issuing CA certs concatenated
aia_base_url.txt base URL for AIA extension (shell/Go only)
{name}_cert.pem certificate issued by root CA
{name}_key.pem key for certificate issued by root CA
{issuing_ca}/
ca_cert.pem issuing CA certificate
ca_key.pem issuing CA private key
{name}_cert.pem certificate issued by issuing CA
{name}_key.pem key for certificate issued by issuing CA
```
Python additionally writes:
```
CA_DIR/
simple-ca.json config + certificate history
crl.pem root CA CRL (make-crl)
{issuing_ca}/
crl.pem issuing CA CRL (make-crl)
```
Use `ca_bundle.pem` with `openssl verify -CAfile CA_DIR/ca_bundle.pem` — this works identically on Linux, macOS, and Windows without relying on hash symlinks.
---
## Shell — `simple-ca.sh`
Source the file and call the functions directly. `SIMPLE_CA_DIR` is set by the first `--ca-dir` call and inherited by all subsequent calls in the same session. It can also be set in the environment before sourcing.
```bash
source simple-ca.sh
```
### `make_ca`
```bash
make_ca [--ca-dir DIR] [--days N] [--aia-base-url URL] [--issuing-ca NAME] CA_NAME
```
Without `--issuing-ca`: creates the root CA in `DIR`.
With `--issuing-ca NAME`: creates an issuing CA in `DIR/NAME/`, signed by the root CA.
### `make_cert`
```bash
make_cert [--ca-dir DIR] [--cert-dir DIR] [--days N] [--issuing-ca NAME] SUBJECT [SAN ...]
```
Creates a TLS certificate (serverAuth + clientAuth). `SUBJECT` is used as the CN and first SAN. Additional SANs can be DNS names or IPv4 addresses. Without `--cert-dir`, files are written to the signing CA's directory.
### `make_pfx`
```bash
make_pfx [--ca-dir DIR] [--issuing-ca NAME] [--password PASS] [--apple-openssl] CERT_PATH
```
Creates a PKCS#12 bundle from an existing certificate. `--apple-openssl` uses `/usr/bin/openssl` to produce a PFX that Apple Keychain and iOS accept (legacy RC2/3DES encryption instead of modern PBES2).
### Example — two-level CA
```bash
source simple-ca.sh
make_ca --ca-dir /tmp/ca "My Root CA"
make_ca --issuing-ca servers "My Servers CA"
make_cert --issuing-ca servers web.example.com web.example.com 192.168.1.1
make_pfx --issuing-ca servers --password s3cr3t /tmp/ca/servers/web_cert.pem
```
---
## Python — `simple-ca.py`
A standalone CLI with the same commands as the shell variant plus CRL generation and revocation. Configuration and certificate history are stored in `simple-ca.json` inside the CA directory.
```bash
python3 simple-ca.py COMMAND [OPTIONS] [ARGS]
```
### Commands
```bash
make-ca [--ca-dir DIR] [--days N] [--ca-publish-base-url URL] [--issuing-ca NAME] CA_NAME
make-cert [--ca-dir DIR] [--cert-dir DIR] [--days N] [--issuing-ca NAME] SUBJECT [SAN ...]
make-pfx [--ca-dir DIR] [--issuing-ca NAME] [--password PASS] [--apple-openssl] CERT_PATH
make-crl [--ca-dir DIR] [--issuing-ca NAME] [--days N]
revoke-cert [--ca-dir DIR] [--issuing-ca NAME] CERT_PATH
```
`--ca-publish-base-url` embeds both AIA (Authority Information Access) and CRL Distribution Point extensions in issued certificates, pointing to the published location of the CA cert and CRL file.
### Example — two-level CA with CRL
```bash
python3 simple-ca.py make-ca --ca-dir /tmp/ca "My Root CA"
python3 simple-ca.py make-ca --ca-dir /tmp/ca --issuing-ca servers "My Servers CA"
python3 simple-ca.py make-cert --ca-dir /tmp/ca --issuing-ca servers web.example.com web.example.com 192.168.1.1
python3 simple-ca.py make-cert --ca-dir /tmp/ca --issuing-ca servers alice.example.com alice.example.com
python3 simple-ca.py revoke-cert --ca-dir /tmp/ca --issuing-ca servers /tmp/ca/servers/alice_cert.pem
python3 simple-ca.py make-crl --ca-dir /tmp/ca --issuing-ca servers
```
---
## Go binary
Equivalent to the shell variant in features. Uses native Go crypto — no `openssl` subprocess required except for `--apple-openssl` PFX generation.
```bash
cd src/simple-ca && go build -o simple-ca .
```
### Commands
```bash
simple-ca make-ca [--ca-dir DIR] [--days N] [--aia-base-url URL] [--issuing-ca NAME] CA_NAME
simple-ca make-cert [--ca-dir DIR] [--cert-dir DIR] [--days N] [--issuing-ca NAME] SUBJECT [SAN ...]
simple-ca make-pfx [--ca-dir DIR] [--issuing-ca NAME] [--password PASS] [--apple-openssl] CERT_PATH
```
`SIMPLE_CA_DIR` environment variable is used as the default CA directory when `--ca-dir` is not specified.
### Example — two-level CA
```bash
export SIMPLE_CA_DIR=/tmp/ca
simple-ca make-ca "My Root CA"
simple-ca make-ca --issuing-ca servers "My Servers CA"
simple-ca make-cert --issuing-ca servers web.example.com web.example.com 192.168.1.1
simple-ca make-pfx --issuing-ca servers --password s3cr3t /tmp/ca/servers/web_cert.pem
```
---
## generate-mobileconfig.py
Generates Apple `.mobileconfig` profiles for distributing CA certificates and optionally client certificates and IKEv2 VPN configuration to Apple devices (macOS / iOS / iPadOS).
### Modes
| Arguments supplied | Profile content |
|---|---|
| `--ca-cert` only | CA trust anchor |
| `--ca-cert` + `--client-cert` + `--client-key` | CA trust anchor + PKCS#12 client certificate |
| All of the above + `--remote-address` + `--match-domains` | CA + client cert + IKEv2 VPN |
### Usage
```
generate-mobileconfig.py --ca-cert CA.pem --output profile.mobileconfig \
--identifier com.example.vpn \
[--client-cert CLIENT.pem --client-key CLIENT_KEY.pem] \
[--remote-address vpn.example.com --match-domains example.com] \
[--profile-name "My VPN"] [--ca-name "My CA"] \
[--client-name "My Cert"] [--vpn-name "My VPN Connection"] \
[--openssl /usr/bin/openssl]
```
#### Required arguments
- `--ca-cert PEM` — CA certificate PEM file to embed as a trust anchor.
- `--output FILE` — Output `.mobileconfig` path.
- `--identifier ID` — Reverse-DNS profile identifier (e.g. `com.example.vpn`). Derived automatically from `--remote-address` when a VPN profile is generated.
#### Client certificate (optional)
- `--client-cert PEM` — Client certificate PEM file.
- `--client-key PEM` — Client private key PEM file (required together with `--client-cert`).
#### VPN (requires client certificate)
- `--remote-address FQDN` — VPN gateway hostname.
- `--match-domains DOMAIN [DOMAIN …]` — Split-DNS domains routed through the VPN.
#### Display name overrides (all optional)
- `--profile-name NAME` — Profile display name (default: `VPN` or `Certificates`).
- `--ca-name NAME` — CA payload display name (default: certificate CN).
- `--client-name NAME` — Client cert payload display name (default: certificate CN).
- `--vpn-name NAME` — VPN connection display name (default: profile name).
#### Other
- `--openssl PATH` — Path to the `openssl` binary (default: `/usr/bin/openssl`).
### Examples
**CA trust profile only:**
```bash
python3 generate-mobileconfig.py \
--ca-cert ca/ca_cert.pem \
--identifier com.example.ca \
--output ca-trust.mobileconfig
```
**CA + client certificate:**
```bash
python3 generate-mobileconfig.py \
--ca-cert ca/ca_cert.pem \
--client-cert certs/alice_cert.pem \
--client-key certs/alice_key.pem \
--identifier com.example.certs \
--output alice.mobileconfig
```
**Full IKEv2 VPN profile:**
```bash
python3 generate-mobileconfig.py \
--ca-cert ca/ca_cert.pem \
--client-cert certs/alice_cert.pem \
--client-key certs/alice_key.pem \
--remote-address vpn.example.com \
--match-domains example.com internal.example.com \
--output alice-vpn.mobileconfig
```
---
## Self-signed certificate
The following command creates a full-featured self-signed certificate usable as a CA and for client/server authentication:
```bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "key.pem" \
-out "cert.pem" \
-subj "/CN=<user name>/O=<user organization>/C=<CC>" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,digitalSignature,keyEncipherment,keyCertSign,cRLSign" \
-addext "extendedKeyUsage=serverAuth,clientAuth" \
-addext "subjectAltName=DNS:<computer_name>"
```