Mutual TLS and operations
Issue a client certificate
Create a separate client identity with client-auth usage instead of reusing the server certificate.
10 minute lesson
Until now, only the server proved its identity. Mutual TLS flips the second half on: the client also presents a certificate, and the server gets a certificate-backed client identity instead of a password or an API key. It’s common between backend services, for admin endpoints, and anywhere a stolen bearer token would hurt.
A client identity is its own credential. Issue it under a policy and key separate from the HTTPS server — never reuse the server certificate for a client, because the two roles have different lifetimes, different holders, and different revocation stories.
Create a client key and CSR:
openssl req -new -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj '/CN=lab-client'
Note what’s missing compared to the server CSR: no DNS names in a SAN. A client certificate identifies a party, not a hostname, so a common name like lab-client is the identity the server will see.
Now sign it with a client-auth extension. The Extended Key Usage extension declares what a certificate may be used for, and for a client it must say client authentication:
printf 'extendedKeyUsage = clientAuth\n' > client-ext.cnf
openssl x509 -req -in client.csr -CA lab-ca.crt -CAkey lab-ca.key -CAcreateserial -out client.crt -days 14 -sha256 -extfile client-ext.cnf
The -extfile option injects the extension during signing. Without it you would get a generic certificate, and strict servers reject client certificates that lack the clientAuth usage.
Inspect Extended Key Usage before using it:
openssl x509 -in client.crt -noout -subject -ext extendedKeyUsage
# subject=CN = lab-client
# X509v3 Extended Key Usage:
# TLS Web Client Authentication
TLS Web Client Authentication is the line that makes this a client certificate. If you see TLS Web Server Authentication instead, you signed with the wrong extension file — reissue before touching the server.
A verification failure you can predict: sign without the extension, then wonder why a picky server rejects an otherwise valid certificate. Decode the EKU first, every time; it costs one command.
Treat the result like what it is. A client private key authenticates its holder. Protect, scope, rotate, and revoke it like any credential — one certificate per client, short lifetimes, and a plan for what happens when a laptop carrying client.key goes missing.
Lesson completed