Respuesta breve. Con authorized_keys, cada servidor enumera todas las claves públicas con las que se puede iniciar sesión, y el acceso dura hasta que alguien borra la línea. Con los certificados OpenSSH, los servidores confían en una sola CA de usuarios (TrustedUserCAKeys). La clave de cada usuario se firma con ssh-keygen -s, que le añade una identidad (-I), las cuentas en las que puede iniciar sesión (principals, -n) y un período de validez (-V). Los clientes confían en una sola CA de hosts (@cert-authority en known_hosts) en lugar de aceptar la huella digital de cada host en la primera conexión. Los certificados de corta duración hacen que las bajas de personal sean automáticas, y las listas de revocación de claves (RevokedKeys) se encargan del resto. El precio es que ahora tienes que operar una CA y proteger su clave.
Lo que authorized_keys no hace
~/.ssh/authorized_keys es sencillo y no tiene piezas móviles, y por eso está en todas partes. Sus límites aparecen cuando hay más personas y más servidores:
- El acceso nunca caduca. Una clave añadida para un contratista en 2023 sigue funcionando en 2026, en todos los servidores a los que se copió, salvo que alguien la quite. (Existe
expiry-time=por línea, pero alguien tiene que ponerlo.) - No hay identidad. La línea es una clave y un comentario. A qué persona, equipo o tarea pertenece consta en una hoja de cálculo, si es que consta en algún sitio.
- La distribución es cosa tuya. Añadir o quitar a alguien obliga a tocar cada servidor, con herramientas de gestión de configuración o a mano.
- Las claves de host se basan en la confianza en el primer uso. Cada cliente acepta la huella digital de cada servidor la primera vez, y reconstruir un servidor provoca
REMOTE HOST IDENTIFICATION HAS CHANGEDen todas partes.
Los certificados OpenSSH, disponibles desde OpenSSH 5.4 (2010), resuelven los cuatro problemas. No son X.509: el formato es propio de OpenSSH, una clave pública más una identidad y unas restricciones, firmada por una clave de CA que, a su vez, es una clave SSH corriente. En 2025, la descripción del formato pasó del archivo PROTOCOL.certkeys del código fuente de OpenSSH a un Internet-Draft, hoy draft-ietf-sshm-cert, en el grupo de trabajo SSHM del IETF.
Dos CA, dos sentidos
Necesitas dos claves de firma separadas, porque responden a preguntas distintas:
- La CA de usuarios responde a la pregunta «¿puede esta clave iniciar sesión con esta cuenta?». Los servidores confían en ella.
- La CA de hosts responde a la pregunta «¿de verdad este host es
web01?». Los clientes confían en ella.
$ ssh-keygen -q -t ed25519 -f user_ca -C "Example user CA" -N ""
$ ssh-keygen -q -t ed25519 -f host_ca -C "Example host CA" -N ""
Con -N "" el laboratorio es más sencillo. Una clave de CA real debe estar en un HSM, en un token de hardware o en un servicio de firma (así funcionan el motor de secretos SSH de HashiCorp Vault, smallstep, Teleport y sus equivalentes en los proveedores de nube). Quien tenga la clave de la CA de usuarios puede iniciar sesión como cualquier usuario que admitan los servidores.
Certificados de usuario
Firma la clave pública que Alice ya tiene, con validez de 8 horas, para las cuentas alice y deploy:
$ ssh-keygen -s user_ca -I alice@example.test -n alice,deploy -V -5m:+8h -z 1001 id_ed25519.pub
Signed user key /home/alice-laptop/id_ed25519-cert.pub: id "alice@example.test" serial 1001 for alice,deploy valid from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
-Ies el ID de clave. sshd lo anota en el registro en cada inicio de sesión, así que pon ahí una persona o una tarea, no un nombre de host.-nenumera los principals, los nombres con los que puede actuar este certificado. El manual de ssh-keygen dice que los certificados sin principals son «válidos para todos los usuarios o hosts», pero sshd 10.0 rechaza un certificado de usuario así cuando la confianza viene deTrustedUserCAKeys(Certificate lacks principal list). Indica siempre-n.-V -5m:+8hfija la validez desde hace 5 minutos (por el desfase de reloj) hasta dentro de 8 horas.-zasigna un número de serie, al que puede hacer referencia una revocación.
ssh-keygen -L muestra lo firmado:
/home/alice-laptop/id_ed25519-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s
Signing CA: ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60 (using ssh-ed25519)
Key ID: "alice@example.test"
Serial: 1001
Valid: from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
Principals:
alice
deploy
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
De forma predeterminada, un certificado de usuario concede las cinco extensiones. -O clear las quita, y después puedes volver a añadir solo lo que necesites, por ejemplo -O clear -O permit-pty. Las opciones críticas restringen el certificado. force-command sustituye cualquier comando que pida el cliente, y source-address limita desde dónde se puede usar. Con source-address=10.0.0.0/8, se rechaza un inicio de sesión desde 127.0.0.1. Desde un origen permitido, el comando forzado se ejecuta pida lo que pida el cliente:
Critical Options:
force-command echo backup only
source-address 10.0.0.0/8
$ ssh deploy@web01.example.test
deploy@web01.example.test: Permission denied (publickey).
cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).
Refused by certificate options
...
$ ssh deploy@web01.example.test "rm -rf /tmp/anything"
backup only
Starting session: forced-command (key-option) 'echo backup only' for deploy from 127.0.0.1 port 48976 id 0
El cliente ssh encuentra el certificado automáticamente cuando está junto a la clave privada como id_ed25519-cert.pub. Si no, indícalo con CertificateFile. ssh-add carga la clave y el certificado en el agente.
Configuración de sshd
Todo lo que hay que hacer del lado del servidor cabe en unas pocas líneas. Este archivo de configuración adicional superó sshd -t y se usó en todas las pruebas de esta página:
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
RevokedKeys /etc/ssh/revoked_keys
PasswordAuthentication no
LogLevel VERBOSE
TrustedUserCAKeyscontiene una o varias claves públicas de CA. Durante una rotación de la CA, incluye las dos.AuthorizedPrincipalsFileasocia cuentas con principals. Aquí/etc/ssh/auth_principals/deploycontiene una sola línea,deploy, así que solo los certificados que incluyen el principaldeploypueden iniciar sesión comodeploy. Sin esta opción, sshd exige que el propio nombre de la cuenta figure entre los principals. La asociación es más flexible: un principal comooncallpuede admitirse en muchas cuentas, yAuthorizedPrincipalsCommandpuede buscarlo en un servicio de directorio.RevokedKeysapunta a una lista de revocación (consulta más abajo).LogLevel VERBOSEregistra el ID de clave y el número de serie del certificado en cada inicio de sesión. Es el registro de auditoría queauthorized_keysnunca tuvo.
Un inicio de sesión con el certificado de Alice, visto desde los dos lados:
Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:IuzvI4QsLmSx1+6p3yrvlRx2wdl+LUCY1zXqSzssLAc, serial 0 ID "web01" CA ssh-ed25519 SHA256:+iie/YHPulJReHN2GQ8UvrorBVrtayD2T1MGxeg/QCQ valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Host 'web01.example.test' is known and matches the ED25519-CERT host certificate.
Offering public key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Server accepts key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Authenticated to web01.example.test ([127.0.0.1]:22) using "publickey".
Accepted publickey for deploy from 127.0.0.1 port 57432 ssh2: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s ID alice@example.test (serial 1001) CA ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60
Si no puedes editar sshd_config, una sola cuenta puede confiar en una CA desde su propio authorized_keys. La opción principals= funciona como el archivo de principals:
cert-authority,principals="deploy-team" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK63...
$ ssh deploy@web01.example.test 'echo logged in as $(whoami)'
logged in as deploy
Certificados de host
Firma la clave de host de cada servidor con la CA de hosts. -h la convierte en un certificado de host, y los principals son los nombres que usan los clientes para conectarse:
$ ssh-keygen -s host_ca -I web01 -h -n web01.example.test,web01 -V -5m:+52w /etc/ssh/ssh_host_ed25519_key.pub
Signed host key /etc/ssh/ssh_host_ed25519_key-cert.pub: id "web01" serial 0 for web01.example.test,web01 valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Después apunta HostCertificate al resultado (como en el archivo de sshd de arriba) y da a los clientes una sola línea en known_hosts (o en /etc/ssh/ssh_known_hosts) en lugar de una huella digital por host:
@cert-authority *.example.test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtSsqoqxwcl31DTJg3kTPsv9eLuuw5gU1BcvYybODlu Example host CA
A partir de ahí, los servidores nuevos y los reconstruidos son de confianza desde la primera conexión, sin pedir confirmación, siempre que su certificado incluya el nombre que usó el cliente. Esa última condición es la sorpresa habitual. Si te conectas con un nombre que no es un principal, el cliente recurre a las claves de host simples y, con la comprobación estricta, se detiene:
$ ssh deploy@db01.example.test
Certificate invalid: name is not a listed principal
No ED25519 host key is known for db01.example.test and you have requested strict checking.
Host key verification failed.
Incluye todos los nombres que usan los clientes, también los nombres cortos, los CNAME, las direcciones IP si la gente se conecta por IP y los nombres de los balanceadores de carga. ssh-keyscan -c obtiene el certificado de un servidor para inspeccionarlo, y las líneas @revoked de known_hosts rechazan una clave de host o una CA concretas.
Qué aspecto tienen los fallos
Ante cualquier problema con un certificado de usuario, el cliente solo dice Permission denied (publickey). El motivo está en el registro del servidor, con LogLevel VERBOSE:
| Situación | Registro de sshd (OpenSSH 10.0p2) |
|---|---|
| La cuenta no está entre los principals del certificado (después de la asociación) | Certificate does not contain an authorized principal |
Certificado emitido sin -n, sin AuthorizedPrincipalsFile | Certificate lacks principal list |
Certificado cuya ventana -V ya terminó | Certificate invalid: expired |
| Número de serie o clave en la KRL | Authentication key ED25519-CERT SHA256:... revoked by file /etc/ssh/revoked_keys |
source-address no coincide | cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1). |
Dos cosas que parecen problemas de certificados pero no lo son:
- Desfase de reloj. La validez se comprueba con el reloj del servidor. Emite certificados válidos desde unos minutos antes (
-V -5m:...) y mantén los servidores sincronizados por NTP. - Penalizaciones por fallos repetidos. Desde OpenSSH 9.8, sshd penaliza las direcciones que fallan la autenticación una y otra vez (
PerSourcePenalties, activado de forma predeterminada). Al probar certificados desde una misma máquina, un intento fallido hizo que la siguiente conexión se cortara antes de la autenticación:
srclimit_penalise: 127.0.0.1/32: activating ipv4 penalty of 19 seconds for penalty: failed authentication
$ ssh deploy@web01.example.test
Connection closed by 127.0.0.1 port 22
drop connection #0 from [127.0.0.1]:55080 on [127.0.0.1]:22 penalty: failed authentication
Si ssh muestra Connection closed justo después de una serie de fallos, espera a que termine la penalización antes de volver a cambiar la configuración.
Revocación con KRL
La validez corta es el principal mecanismo de revocación: un certificado de 8 horas deja de valer por sí solo. Para lo demás, OpenSSH tiene las listas de revocación de claves (KRL, desde OpenSSH 6.2), un formato binario compacto que permite revocar por número de serie, por ID de clave o por clave. Revocación del certificado de Alice con número de serie 1001:
$ printf 'serial: 1001\n' > /tmp/krl-spec
$ ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca.pub /tmp/krl-spec
Revoking from /tmp/krl-spec
$ ssh-keygen -Q -f /etc/ssh/revoked_keys id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub (alice@laptop): REVOKED
sshd lee RevokedKeys en cada autenticación, así que el siguiente inicio de sesión falla sin reiniciar nada. Añade -u para actualizar una KRL existente en lugar de sustituirla. Distribuye la KRL con las mismas herramientas que la clave de la CA. Un servidor con una KRL desactualizada acepta lo que no sabe que está revocado. Es un motivo más para que los certificados sean de corta duración y para tratar la KRL como una red de seguridad para las horas que quedan.
Comparación directa
| authorized_keys | Certificados | |
|---|---|---|
| Conceder acceso | Copiar una clave a cada servidor | Firmar una clave una vez |
| Retirar acceso | Borrarla en cada servidor | Dejar que caduque o añadirla a la KRL |
| Caducidad | Opcional por línea (expiry-time=) | Todo certificado tiene un período de validez |
| Identidad en los registros | Huella digital de la clave | ID de clave y número de serie |
| Restricciones | from=, command=, restrict por línea | source-address, force-command, extensiones por certificado |
| Confianza en los hosts | Huella digital por host (TOFU) | Una línea @cert-authority |
| Secreto crítico | Ninguno centralizado | La clave privada de la CA |
| Piezas móviles | Ninguna | Emisión, protección de la CA, distribución de la KRL |
Las restricciones por línea de authorized_keys funcionan de verdad y se probaron en el mismo servidor:
restrict,from="10.0.0.0/8",command="echo backup only" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
/home/deploy/.ssh/authorized_keys:1: Authentication tried for deploy with correct key but not from a permitted host (host=127.0.0.1, ip=127.0.0.1, required=10.0.0.0/8).
expiry-time="20261001" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
Para un puñado de servidores y personas, authorized_keys con restrict y expiry-time es suficiente. Los certificados compensan cuando las altas, las bajas y las reconstrucciones de hosts son semanales, o cuando necesitas saber quién inició sesión, no solo con qué clave.
Migración desde authorized_keys
No hace falta cambiarlo todo el mismo día. sshd comprueba authorized_keys y las CA de confianza a la vez, así que los dos tipos de inicio de sesión funcionan durante la transición:
- Despliega primero la confianza en la CA. Distribuye
TrustedUserCAKeys, los archivos de principals y una KRL vacía a todos los servidores, y dejaauthorized_keyscomo está. - Emite certificados para un primer equipo y deja que sus miembros los usen durante unas semanas. Cada inicio de sesión aparece ahora en el registro con
ID ... (serial ...). - Localiza a los rezagados. Un inicio de sesión mediante
authorized_keysse registra sin ID de certificado, por ejemploAccepted publickey for deploy from 127.0.0.1 port 58770 ssh2: ED25519 SHA256:tT5X32Wds5InYaLSh7mV+QRqAtxA4KXwlKV3hkf0YkI. Suelen ser automatizaciones: ejecutores de CI, tareas de copia de seguridad, monitorización. Dale a cada una su propio certificado, de corta duración o con restricciones estrictas. - Desactiva los archivos de claves con
AuthorizedKeysFile nonecuando ya nada legítimo los use. Mantén una vía de acceso de emergencia, como el acceso por consola o una CA de emergencia aparte guardada sin conexión, para el día en que el servicio de firma no funcione. - Haz la parte de los hosts por separado. Firma las claves de host y añade la línea
@cert-authorityal archivo global de los clientes,/etc/ssh/ssh_known_hosts. Las entradas antiguas por host deknown_hostspueden quedarse. Siguen funcionando hasta que cambien las claves de host.
En la práctica
- Emite los certificados desde tu proveedor de identidad. Un servicio de firma comprueba el inicio de sesión SSO del usuario (OIDC) y devuelve un certificado con sus principals, válido durante unas pocas horas. Nadie maneja la clave de la CA, y quien deja la empresa pierde el acceso SSH como muy tarde al día siguiente, sin tocar ningún servidor.
- Guarda la clave del usuario en hardware si puedes.
ssh-keygen -t ed25519-skcrea una clave respaldada por FIDO2. Los certificados funcionan con ella igual que con cualquier otra clave. - Rota la CA con un período de solapamiento. Añade la nueva CA de usuarios como segunda línea en
TrustedUserCAKeys, o la nueva CA de hosts como segunda línea@cert-authority. Emite desde la nueva CA y después retira la antigua. - Vigila también el lado de los hosts. Vuelve a firmar las claves de host antes de que caduquen sus certificados. Un certificado de host caducado hace que los clientes recurran a las claves de host simples, que normalmente no tienen:
Certificate invalid: expired
No ED25519 host key is known for web01.example.test and you have requested strict checking.
Host key verification failed.
Para inspeccionar un archivo de clave o de certificado sin ejecutar nada, el inspector de claves SSH decodifica claves OpenSSH y muestra su tipo, su tamaño y su huella digital. La auditoría de servidores SSH comprueba lo que ofrece un servidor, incluidos sus algoritmos de clave de host y de certificado y el intercambio de claves poscuántico.
Fuentes
- Páginas del manual de OpenSSH: ssh-keygen(1) (secciones CERTIFICATES y KEY REVOCATION LISTS), sshd_config(5) (
TrustedUserCAKeys,AuthorizedPrincipalsFile,RevokedKeys,HostCertificate,PerSourcePenalties), sshd(8) (formatos de authorized_keys y known_hosts). - draft-ietf-sshm-cert: SSH Certificate Format (el sucesor de
PROTOCOL.certkeysde OpenSSH). PROTOCOL.krl: el formato KRL. - Notas de las versiones de OpenSSH: 5.4 (certificados), 6.2 (KRL), 9.8 (
PerSourcePenalties).
Toda la salida de esta página procede de OpenSSH 10.0p2 (paquetes de Debian 13) en debian:trixie-20261005-slim, el 20 de octubre de 2026, con claves de prueba generadas en el contenedor.