Issue a client certificate for mutual TLS
To issue a client certificate, generate a key and CSR as usual, then sign it with your private CA using an extensions file containing `extendedKeyUsage = clientAuth` and `basicConstraints = critical,CA:FALSE`. The `clientAuth` purpose is what makes it usable for mutual TLS — a certificate carrying only `serverAuth` is rejected by any server that checks, with an "unsupported certificate purpose" error. Use a private CA dedicated to client certificates, never a public one.
Step 1 — key and CSR #
openssl req -new -newkey rsa:2048 -nodes \
-keyout client.key -out client.csr \
-subj "/O=Example Inc/OU=Services/CN=payments-worker" Step 2 — the extensions that matter #
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer Replace these values
| Placeholder | What to put there |
|---|---|
extendedKeyUsage = clientAuth | The flag that makes this usable for mutual TLS. Without it, servers reject the certificate. |
basicConstraints = CA:FALSE | This certificate may not sign others. |
keyUsage | Restricts the key to signing and key exchange. |
Step 3 — sign it with your client CA #
openssl x509 -req -in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 90 -sha256 \
-extfile client-ext.cnf Step 4 — check the purpose took effect #
This is the check worth building into issuance, because the failure is silent until a handshake fails:
openssl x509 -in client.crt -noout -ext extendedKeyUsage -ext basicConstraints Expected output
X509v3 Extended Key Usage: TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE
openssl verify -CAfile ca.crt client.crt Expected output
client.crt: OK
Step 5 — package it for the client #
PEM works for curl and OpenSSL. Browsers, Java and Windows want PKCS#12:
openssl pkcs12 -export \
-in client.crt -inkey client.key -certfile ca.crt \
-name "payments-worker" -out client.p12 Testing against a server #
openssl s_client -connect example.com:443 -servername example.com \
-cert client.crt -key client.key </dev/null curl --cert client.crt --key client.key https://example.com/ For the server-side configuration, see mutual TLS.
Frequently asked questions
- Does a client certificate need a hostname?
- No. It identifies a client, not a server, so the subject is normally a service or machine name. Subject alternative names are optional and only matter if something on your side matches against them.
- Can one certificate serve as both server and client?
- Yes, if its extended key usage lists both
serverAuthandclientAuth. That is common for service-to-service traffic where each side both calls and is called. - How long should a client certificate last?
- Shorter than a server certificate. Revocation is the hard part of mTLS, and a short lifetime limits how long a leaked client credential stays usable. Ninety days is a reasonable starting point if issuance is automated.
- What format does the client need?
- It depends on the client. curl and OpenSSL take PEM. Browsers, Java and Windows want a PKCS#12 bundle, which is why the last step below produces one.
References
- openssl-req(1) — generating the client request
- RFC 5280: X.509 and CRL profile — the specification every field on a certificate comes from