7.1 KiB
CertAuth Key Vault - API & Web UI Guide
Overview
CertAuth is a self-contained Certificate Authority running on 192.168.8.248 (accessible as certauth.ms on the local network). It uses two YubiKey 5 Nano devices as hardware security modules — one for the Root CA and one for the Intermediate CA — requiring physical touch + PIN to sign certificates.
Authentication
Two auth methods are supported:
JWT Token (API)
# Login
curl -X POST https://certauth.ms/api/token \
-H "Content-Type: application/json" \
-d '{"username":"certauth","password":"CHANGE_ME_ADMIN_PASS"}'
# Response: {"access_token": "eyJ...", "token_type": "bearer"}
# Use token in subsequent requests
curl -H "Authorization: Bearer $TOKEN" https://certauth.ms/api/domains
Cookie Auth (Web UI / curl)
# Login via web endpoint (sets cookie)
curl -c cookies.txt -L https://certauth.ms/login \
-d "username=certauth&password=CHANGE_ME_ADMIN_PASS"
# Use cookie in subsequent requests
curl -b cookies.txt https://certauth.ms/api/domains/web
Or use -b "token=$TOKEN" with the token value for cookie-auth endpoints.
Admin Credentials
- Username:
certauth - Password:
CHANGE_ME_ADMIN_PASS - PFX download password:
certauth
API Endpoints
All endpoints require authentication. The server is at https://certauth.ms.
Health Check
curl -k https://certauth.ms/api/health
# → {"status":"ok"}
CA Chain Download
curl -k https://certauth.ms/api/ca-chain -o ca-chain.crt
Returns the Intermediate + Root CA chain in PEM format.
Domains
List domains:
curl -H "Authorization: Bearer $TOKEN" https://certauth.ms/api/domains
Register domain (API):
curl -X POST https://certauth.ms/api/domains \
-H "Authorization: Bearer $TOKEN" \
-d "name=example.com&description=My+website"
Register domain (web/cookie auth):
curl -b cookies.txt -X POST https://certauth.ms/api/domains/web \
-d "name=example.com&description=My+website"
Certificates
List certificates:
curl -H "Authorization: Bearer $TOKEN" https://certauth.ms/api/certs
Request certificate (API):
curl -X POST https://certauth.ms/api/certs/request \
-H "Authorization: Bearer $TOKEN" \
-d "cn=example.com&sans=example.com%2Cwww.example.com&days=365&domain_id=1"
Request certificate (web/cookie auth):
curl -b cookies.txt -X POST https://certauth.ms/api/certs/web/request \
-d "cn=example.com&sans=example.com%2Cwww.example.com&days=365&domain_id=0"
Parameters:
cn— Common Name (required)sans— Comma-separated Subject Alternative Names (URL-encoded)days— Validity in days (default: 365)domain_id— Domain ID to link to (0 = auto-match by CN)
Sign/Issue certificate:
curl -X POST https://certauth.ms/api/certs/{cert_id}/sign \
-H "Authorization: Bearer $TOKEN"
Sign/Issue certificate (web/cookie auth):
curl -b cookies.txt -X POST https://certauth.ms/api/certs/{cert_id}/sign/web
⚠️ Signing requires physical YubiKey touch + PIN — this will block until the operator touches YubiKey 2 and enters the PIN.
Download PEM (cert + chain):
curl -b cookies.txt https://certauth.ms/api/certs/{cert_id}/pem -o cert.pem
Download PFX (cert + key + chain, password: certauth):
curl -b cookies.txt https://certauth.ms/api/certs/{cert_id}/pfx -o cert.pfx
History
View certificate history:
curl -b cookies.txt https://certauth.ms/history
Setup Scripts
Bash installer (Linux/macOS):
curl -sL https://certauth.ms/setup.sh | sudo bash -s 192.168.8.248
PowerShell installer (Windows):
iwr https://certauth.ms/setup.ps1 -UseBasicParsing | iex
Complete Workflow Example
Here's a full end-to-end example to register a domain, request a cert, issue it, and download:
# 1. Login and get token
TOKEN=$(curl -s -X POST https://certauth.ms/api/token \
-H "Content-Type: application/json" \
-d '{"username":"certauth","password":"CHANGE_ME_ADMIN_PASS"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# 2. Register domain
curl -s -X POST https://certauth.ms/api/domains \
-H "Authorization: Bearer $TOKEN" \
-d "name=myapp.local&description=Internal+app"
# 3. Request certificate
CERT_ID=$(curl -s -X POST https://certauth.ms/api/certs/request \
-H "Authorization: Bearer $TOKEN" \
-d "cn=myapp.local&sans=myapp.local%2Cwww.myapp.local&days=365&domain_id=0" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Certificate ID: $CERT_ID"
# 4. Issue certificate (requires YubiKey touch!)
curl -s -X POST https://certauth.ms/api/certs/$CERT_ID/sign \
-H "Authorization: Bearer $TOKEN"
# 5. Download PEM (cert + intermediate + root)
curl -s -H "Authorization: Bearer $TOKEN" \
https://certauth.ms/api/certs/$CERT_ID/pem -o myapp.pem
# 6. Download PFX (cert + key + chain, password: certauth)
curl -s -H "Authorization: Bearer $TOKEN" \
https://certauth.ms/api/certs/$CERT_ID/pfx -o myapp.pfx
# 7. Verify
openssl verify -CAfile ca-chain.crt myapp.pem
Certificate Details
- Key type: EC P-384 (SECP384R1)
- Signature algorithm: ECDSA-SHA384
- CA chain: Intermediate CA (YubiKey 2) → Root CA (YubiKey 1)
- Extensions: Server Auth + Client Auth EKU, proper AKI/SKI
- Validity: 365 days per leaf cert (configurable)
- PFX password:
certauth
Installing CA Chain on Clients
The setup.sh endpoint serves a self-installing bash script that:
- Auto-detects the server IP (or prompts)
- Downloads the CA chain
- Installs it to the OS trust store
- Supports: Debian/Ubuntu, RHEL/CentOS/Fedora, Arch, Alpine, macOS
For macOS specifically, the CA is installed to the System Keychain with security add-trusted-cert.
For LibreWolf (which disables enterprise roots by default):
- Set
security.enterprise_roots.enabled = trueinabout:config - OR import the root CA via
certutilinto LibreWolf's NSS database
Key Files on Server
| Path | Description |
|---|---|
/etc/ssl/ca/root/root-ca.crt |
Root CA certificate |
/etc/ssl/ca/intermediate/intermediate-ca.crt |
Intermediate CA certificate |
/etc/ssl/ca/ca-chain.crt |
Full chain (intermediate + root) |
/etc/ssl/ca/issued/ |
Issued leaf certificates and keys |
/etc/ssl/certauth/tls.pem |
Server cert + chain (for Caddy) |
/etc/ssl/certauth/tls.key |
Server private key |
/opt/certauth/api/ |
API application files |
/var/lib/certauth/certauth.db |
SQLite database |
DNS
certauth.ms→192.168.8.248(served by dnsmasq on the server)- Also added to router DNS for network-wide resolution
Troubleshooting
- SSL_ERROR_BAD_CERT_DOMAIN: Make sure you're using
https://certauth.msnot the IP. Firefox/NSS has quirks with IP Address SANs. - SEC_ERROR_UNKNOWN_ISSUER: The CA chain isn't trusted. Install via
setup.shor import manually. - Signing fails: YubiKey 2 must be touched and PIN entered when signing.
- DNS not resolving: Check that
certauth.msis in your router's DNS or AdGuard Home config.