Issue, renew, revoke, package, and share client certificates for mutual TLS.
The toolkit runs a pinned Smallstep CA with Docker Compose and uses the step
CLI for certificate operations.
This project manages private keys and passwords. Read these points before using it:
./stepcontains the CA state and keys. Back it up securely and never commit it.issued_passwords.csvstores PKCS#12 passwords in plaintext by design. The scripts restrict it to mode0600; anyone who can read it can unlock issued bundles.- generated ZIP files contain the encrypted PKCS#12 bundle and public certificates. They are not encrypted themselves.
- the temporary HTTP server is suitable only for a trusted local network.
- certificate revocation takes effect only when the relying service checks CRL or OCSP status.
- Docker with Compose
- Bash on Linux or macOS
step, OpenSSL,zip, andunzip- optional:
qrencodefor terminal QR codes
./init_cli.sh installs the command-line dependencies on Debian-based Linux
systems. On macOS, install them with Homebrew:
brew install smallstep zipThe scripts use atomic directory locks supported by both Linux and macOS. Windows is not a supported execution target.
git clone https://github.com/alsd4git/mtls-cli.git
cd mtls-cli
cp .env.example .envSet a strong STEP_CA_PASSWORD in .env, then start the CA:
docker compose up -dThe default bind address is 127.0.0.1:9001. Set
STEP_CA_BIND_ADDRESS=0.0.0.0 only when another host must reach the CA and a
firewall protects the port.
./issue-client-cert.shFor non-interactive use, pass the client name:
./issue-client-cert.sh aliceThe script restricts client names to a path-safe character set and creates:
certs_output/alice/
alice.crt
alice.key
alice.p12
alice-certs.zip
issued_passwords.csv
roots.pem
The raw private key remains local and is excluded from the ZIP by default. Set
INCLUDE_RAW_KEY_IN_ZIP=true only when a recipient requires it.
For a non-loopback CA_URL, set CA_FINGERPRINT. This pins the CA root during
bootstrap. Omitting the fingerprint is accepted only for the legacy loopback
setup and produces a warning.
List the local inventory:
./cert-manager.sh inventoryRenew a certificate and rebuild its bundle with the recorded password:
./cert-manager.sh renew aliceRevoke a certificate:
./cert-manager.sh revoke alice "device lost"Renewal keeps timestamped backups of the previous certificate and restores them if the operation fails.
./serve-certs.sh certs_output/alice/alice-certs.zipThe command serves only the selected file, prints a QR code when qrencode is
available, and stops after five minutes. Configure the lifetime with
EXPIRE_MINUTES, the port with PORT, and address detection with SHARE_IP.
On Android, extract the ZIP, open the .p12 file, and enter the password from
issued_passwords.csv. Android places the certificate under its credential
storage settings.
The main overrides are CA_URL, CA_FINGERPRINT, PROVISIONER,
CERT_DURATION, OUT_DIR, ROOT_CA, and PASSWORD_LOG. See the scripts and
.env.example for their defaults.
shellcheck ./*.sh ./lib/*.sh ./tests/*.sh
./tests/run.sh
STEP_CA_PASSWORD=test-only docker compose config --quietThe tests use a fake step command and temporary OpenSSL certificates. They do
not contact a live CA.
MIT. See LICENSE.