Server and client certificates
Issue TLS leaves under an intermediate CA: what goes in the subject and the SANs, which key and validity to use, and how to re-issue.
Server and client leaves
server-cert and client-cert issue a key and a certificate under an intermediate CA. They differ in the extended key usage they set and the directory they write to.
$ homepki server-cert -d runlocal.dev -i bu1 -s kong-gateway
$ homepki client-cert -d runlocal.dev -i bu1 -c my-client
| Command | Extended key usage | Key usage | Written to |
|---|---|---|---|
server-cert | serverAuth | digitalSignature, plus keyEncipherment for an RSA key | <intermediate>/server-tls/<name>.crt, .key |
client-cert | clientAuth | digitalSignature | <intermediate>/client-tls/<name>.crt, .key |
The intermediate must exist. Every new certificate is verified against its chain before anything is written, so a leaf that would not verify, for example one outside a CA's name constraints, is refused and leaves nothing on disk.
Derived subject
The subject is not configurable. The organization is the root CA's literal name (the domain with dots replaced by dashes), the organizational unit is the intermediate name, and the common name is <leaf>.<intermediate>.<domain>:
O=runlocal-dev, OU=bu1, CN=kong-gateway.bu1.runlocal.dev
The common name is always the first DNS Subject Alternative Name. Most TLS clients ignore the common name and match SANs only, so if a service must answer on a name that does not fit this shape, add it with --san rather than changing the leaf name.
Subject Alternative Names
Both commands take a repeatable --san. The values are appended after the common name.
$ homepki server-cert -d runlocal.dev -i bu1 -s kong-gateway \
--san kong.local \
--san 192.168.1.10 \
--san 'DNS:*.kong.local' \
--san IP:::1
$ openssl x509 -noout -ext subjectAltName -in kong-gateway.crt
X509v3 Subject Alternative Name:
DNS:kong-gateway.bu1.runlocal.dev, DNS:kong.local, DNS:*.kong.local,
IP Address:192.168.1.10, IP Address:0:0:0:0:0:0:0:1
| Value | Becomes |
|---|---|
| a bare value that parses as an IP address | an IP SAN |
| any other bare value | a DNS SAN |
DNS:, IP:, email:, URI: prefix (any case) | that type, whatever the value looks like |
An invalid address after IP: is rejected instead of being treated as a hostname. Duplicates are dropped.
Single-quote any SAN that contains *. An unquoted --san DNS:*.kong.local is expanded by the shell before homepki receives it, and zsh fails the whole command with no matches found.
SANs on client certificates
client-cert takes --san the same way. Use a URI: SAN to give a workload a SPIFFE ID:
$ homepki client-cert -d runlocal.dev -i bu1 -c my-client \
--san URI:spiffe://runlocal.dev/ns/default/sa/my-client
Key types
Every generate command takes --key-type. Tiers are independent: an ECDSA leaf under an RSA intermediate is fine. An unknown value is rejected before anything is written.
--key-type | Key | Signature digest when this key signs |
|---|---|---|
rsa (default) | RSA 2048-bit | SHA-256 |
ecdsa, ecdsa-p256 | ECDSA P-256 | SHA-256 |
ecdsa-p384 | ECDSA P-384 | SHA-384 |
ecdsa-p521 | ECDSA P-521 | SHA-512 |
ed25519 | Ed25519 | none (Ed25519 signs the message directly) |
The digest follows the issuing CA's key, not the key being certified. A P-384 leaf under an RSA intermediate is signed with SHA-256.
$ homepki intermediate-ca -d runlocal.dev -n bu2 --key-type ecdsa
$ homepki server-cert -d runlocal.dev -i bu2 -s gw --key-type ecdsa-p384
Browsers do not accept Ed25519 certificates. Use ed25519 for service-to-service TLS between Go or OpenSSL 1.1.1+ peers, and RSA or ECDSA for anything a browser opens.
Validity
--validity sets how long a certificate stays valid, as a number of days (90d) or a Go duration (24h, 30m). There is no year suffix: write 3650d.
| Command | Default |
|---|---|
root-ca, intermediate-ca | 2190 days (about 6 years) |
server-cert, client-cert, sign | 365 days |
$ homepki server-cert -d runlocal.dev -i bu1 -s kong-gateway --validity 24h # to test rotation
A certificate never outlives its issuer. A longer request is capped at the issuer's expiry and the command prints a note:
$ homepki server-cert -d runlocal.dev -i bu1 -s long --validity 5000d
Generating server certificate long under runlocal.dev/bu1
Wrote ~/.homepki/runlocal-dev/bu1/server-tls/long.key
Wrote ~/.homepki/runlocal-dev/bu1/server-tls/long.crt
Note: validity capped at the issuer's expiry, 2032-09-24.
Server Certificate generated successfully.
NotBefore is always set 5 minutes in the past, so a new certificate is already valid in a container or VM whose clock runs slightly behind. It also means an already-expired certificate cannot be issued.
PKCS#12 bundles
--pkcs12 also writes <name>.p12 next to the .crt and .key. It holds the private key, the leaf and the CA chain (intermediate, then root).
$ homepki server-cert -d runlocal.dev -i bu1 -s kong-gateway --pkcs12
Generating server certificate kong-gateway under runlocal.dev/bu1
Wrote ~/.homepki/runlocal-dev/bu1/server-tls/kong-gateway.key
Wrote ~/.homepki/runlocal-dev/bu1/server-tls/kong-gateway.crt
Wrote ~/.homepki/runlocal-dev/bu1/server-tls/kong-gateway.p12 (key, certificate and CA chain; password "changeit")
Server Certificate generated successfully.
$ keytool -list -keystore kong-gateway.p12 -storepass changeit
- The password is
changeit, the default of mkcert and of the JDK keystores.--pkcs12-passwordsets another. - The file uses the 3DES/SHA-1 PKCS#12 encoding, which Java, macOS Keychain, Windows and OpenSSL 3 import without extra options such as
-legacy. - The file has mode
0600, like the.key.
Re-issuing a leaf
A generate command refuses to overwrite existing material. It exits 1, lists what is in the way, and changes nothing:
$ homepki server-cert -d runlocal.dev -i bu1 -s kong-gateway
Error: a server certificate named "kong-gateway" already exists under runlocal.dev/bu1:
~/.homepki/runlocal-dev/bu1/server-tls/kong-gateway.crt
~/.homepki/runlocal-dev/bu1/server-tls/kong-gateway.key
~/.homepki/runlocal-dev/bu1/server-tls/kong-gateway.p12
Re-issuing overwrites its private key, so anything already serving that pair breaks. Pass --force to replace it, or issue under another name
--force writes a new key and certificate over the old pair. There is no CA database, so nothing else needs cleaning up. The key is regenerated, so reload anything that serves or presents the old pair.
$ homepki server-cert -d runlocal.dev -i bu1 -s kong-gateway \
--san kong.local --san 10.0.0.5 --force
Re-issuing with --force but without --pkcs12 deletes the old .p12, since it holds the replaced key. A re-issued certificate gets a new serial number, so it is not affected by an earlier revocation of the same name.
In scripts, prefer a new leaf name or a fresh HOMEPKI_WORKDIR over --force. --force on a CA tier orphans every certificate below it; see Checking after --force.