SSH user certificates
SSH public-key authentication authorizes the card’s raw public key on each
server’s authorized_keys. SSH user certificates replace that with a
trust relationship: an SSH CA signs the card’s public key once, every server
trusts the CA (one line in sshd_config), and revocation/renewal happens
at the CA instead of editing authorized_keys everywhere the key was
copied. The card’s role in this is identical to the raw-key guide — it only
ever signs the connection challenge; certificate issuance is a host-side
step this guide covers, not a card capability.
Warning
9C cannot be used for SSH on the unmodified ``cryptnox-default`` profile, for the same reason as SSH public-key authentication — see Why it fails on cryptnox-default. The certificate layer doesn’t change which key actually signs the connection; if the raw-key path fails on your card, this one will too. Use 9A instead — see Fixing it: give 9A the SIGN role.
Also, do not use ssh-add to load the certificate — it cannot load a
bare certificate file (invalid format) when the matching private key
lives on a PKCS#11 token rather than on disk. Step 4 below uses
CertificateFile + PKCS11Provider instead, which is the mechanism
ssh_config(5) documents for this case.
Prerequisites
Complete SSH public-key authentication first — this guide only adds a certificate on top of that public key, it doesn’t replace any of it. That includes its contact-reader requirement — 9A’s contactless access mode is
NEVER, and the certificate layer doesn’t change what interface the underlying signature needs.An SSH CA keypair. For a self-managed CA:
ssh-keygen -t ed25519 -f ssh_ca -C "internal SSH CA"(keepssh_caoffline;ssh_ca.pubis what servers trust). Larger deployments typically run a CA service (e.g. Vault SSH secrets engine,step-ca) instead of a bare keypair — the signing command in Step 2 is the same either way, only how you invoke the signer differs.
Step 1 — export the card’s public key
Same as SSH public-key authentication Step 1:
$ ssh-keygen -D "$PKCS11_MODULE" -e > card-key.pub
Step 2 — the CA signs it
$ ssh-keygen -s ssh_ca -I "user-id" -n username -V +52w card-key.pub
This produces card-key-cert.pub. -I is a certificate identifier (shows
up in server auth logs), -n restricts which login principal(s) the
certificate is valid for, -V sets an expiry — the CA can also constrain
by IP, force-command, or source address; see man ssh-keygen.
Host certificates (signing the server’s key so clients stop trusting
first-connection TOFU) use the same command with -h, but that’s a
server-key operation, unrelated to the card.
Step 3 — trust the CA on the server
One line in /etc/ssh/sshd_config, instead of touching
authorized_keys per user:
TrustedUserCAKeys /etc/ssh/ssh_ca.pub
Copy ssh_ca.pub (never the CA private key) to the server, then
systemctl reload sshd.
Step 4 — connect with the certificate
$ ssh -o PKCS11Provider="$PKCS11_MODULE" \
-o CertificateFile=card-key-cert.pub \
username@host
Do not use ssh-add card-key-cert.pub for this — ssh-add only
loads private keys (checking file permissions as if it might be one first),
and a bare certificate file has no matching on-disk private key to pair with
when the key itself lives on a PKCS#11 token. CertificateFile is the
option ssh_config(5) documents specifically for pairing a certificate
with a key provided via PKCS11Provider (or IdentityFile/ssh-agent
for a disk-resident key). Make this permanent in ~/.ssh/config:
Host host
PKCS11Provider /usr/lib64/pkcs11/opensc-pkcs11.so
CertificateFile ~/card-key-cert.pub
Troubleshooting
Server still asks for a password / falls back — confirm
TrustedUserCAKeyspoints at the CA’s public key andsshdwas reloaded; checksshd -T | grep trustedusercakeys.“certificate invalid: not yet valid” / “expired” — re-sign with
-Vcovering the current date; certificates don’t auto-renew.Principal mismatch (“Certificate invalid: name is not a listed principal”) — the
-nvalue at signing time must match the login username (or use-nwith multiple comma-separated principals).“Error loading key …: invalid format” from ssh-add — you tried
ssh-add card-key-cert.pub, which doesn’t work for a PKCS#11-resident key (see the warning at the top): useCertificateFile+PKCS11Providerin Step 4 instead.“Permission denied (publickey)” / “agent refused operation” even with correct syntax and correct PIN, and you’re using 9C — this is the
cryptnox-default/9C always-reauth issue, identical to Why it fails on cryptnox-default. The certificate doesn’t change which key signs the connection — switch to 9A, see Fixing it: give 9A the SIGN role.Anything else from the raw-key path (module not found, other PIN/agent issues) — see SSH public-key authentication Troubleshooting first; this guide only adds the certificate layer on top.
Next steps
SSH public-key authentication — the raw-public-key path this builds on, including the 9A fix