OpenSSL es la herramienta de línea de comandos estándar para trabajar con claves privadas y certificados X.509 en Linux. En este tutorial usarás OpenSSL 3 en Ubuntu 24.04 para generar una clave privada, crear una solicitud de firma de certificado (CSR) con nombres alternativos, emitir un certificado autofirmado, montar una pequeña CA interna para tus servicios privados, instalar el certificado en Nginx, convertir entre formatos y comprobar fechas, cadenas y coincidencia de claves.
Para sitios públicos lo habitual es obtener certificados gratuitos y automáticos de Let's Encrypt con Certbot. OpenSSL sigue siendo imprescindible para generar CSR para una CA comercial, para servicios internos y para diagnosticar cualquier problema de certificados.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. OpenSSL viene instalado por defecto.
- Un usuario no root con privilegios
sudo. - Para la parte de Nginx: Nginx instalado y el puerto 443 abierto en el firewall (
sudo ufw allow 'Nginx Full'). - Un nombre de dominio o nombre interno para el certificado. En los ejemplos se usa
your_domain; sustitúyelo por el tuyo.
Comprueba la versión de OpenSSL:
openssl version
OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
Trabaja en un directorio propio con permisos restringidos:
mkdir -p ~/certs && chmod 700 ~/certs && cd ~/certs
Paso 1: Generar una clave privada
La clave privada es el secreto del certificado: quien la tenga puede suplantar tu servidor. Genera una clave ECDSA P-256, que es rápida y compatible con todos los navegadores y clientes actuales:
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out your_domain.key
chmod 600 your_domain.key
Si necesitas compatibilidad con clientes muy antiguos, genera en su lugar una clave RSA de 3072 bits:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out your_domain.key
Comprueba que la clave es válida:
openssl pkey -in your_domain.key -noout -text | head -n 1
Private-Key: (256 bit)
Notaestas claves no llevan frase de contraseña, porque el servidor web necesita leerlas al arrancar sin intervención. La protección es el permiso
600y que el archivo nunca salga del servidor.
Paso 2: Crear una solicitud de firma de certificado (CSR)
La CSR contiene la clave pública y los nombres para los que quieres el certificado. Es lo que se envía a la CA. Los navegadores solo validan el campo Subject Alternative Name (SAN), no el CN, así que incluye ahí todos los nombres:
openssl req -new -key your_domain.key -out your_domain.csr \
-subj "/C=ES/O=Tu Empresa/CN=your_domain" \
-addext "subjectAltName=DNS:your_domain,DNS:www.your_domain"
Revisa el contenido y verifica la firma de la CSR:
openssl req -in your_domain.csr -noout -subject -verify
openssl req -in your_domain.csr -noout -text | grep -A1 'Subject Alternative Name'
Certificate request self-signature verify OK
subject=C = ES, O = Tu Empresa, CN = your_domain
X509v3 Subject Alternative Name:
DNS:your_domain, DNS:www.your_domain
Si vas a usar una CA comercial, envía el contenido de your_domain.csr (nunca la clave) en su formulario. Te devolverá el certificado y normalmente uno o varios certificados intermedios.
Paso 3: Crear un certificado autofirmado
Un certificado autofirmado sirve para pruebas y entornos de desarrollo. Los navegadores mostrarán un aviso porque nadie de confianza lo ha firmado. Puedes crear clave y certificado en un solo comando:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc \
-keyout selfsigned.key -out selfsigned.crt -days 365 \
-subj "/CN=your_domain" \
-addext "subjectAltName=DNS:your_domain"
La opción -noenc deja la clave sin cifrar y -days fija la validez. Comprueba el resultado:
openssl x509 -in selfsigned.crt -noout -subject -issuer -dates
subject=CN = your_domain
issuer=CN = your_domain
notBefore=Sep 25 10:12:03 2026 GMT
notAfter=Sep 25 10:12:03 2027 GMT
Emisor y sujeto son iguales: esa es la señal de un certificado autofirmado.
Paso 4: Montar una CA interna para servicios privados
Si tienes varios servicios internos (paneles, APIs privadas, bases de datos con TLS), es mejor crear tu propia autoridad certificadora, instalarla como confiable en tus máquinas y firmar con ella los certificados. Así evitas los avisos sin depender de una CA pública.
Crea la clave de la CA. Esta sí debe llevar frase de contraseña, porque solo la usarás al firmar:
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -aes-256-cbc -out ca.key
chmod 600 ca.key
Crea el certificado raíz de la CA, válido diez años:
openssl req -x509 -new -key ca.key -sha256 -days 3650 -out ca.crt -subj "/O=Tu Empresa/CN=Tu Empresa CA interna"
Comprueba que tiene la marca de CA:
openssl x509 -in ca.crt -noout -ext basicConstraints
X509v3 Basic Constraints: critical
CA:TRUE
Para firmar el certificado del servidor, crea un archivo con las extensiones que debe llevar:
nano your_domain.ext
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:your_domain, DNS:www.your_domain
Firma la CSR del paso 2 con la CA. OpenSSL pedirá la frase de contraseña de ca.key:
openssl x509 -req -in your_domain.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out your_domain.crt -days 397 -sha256 -extfile your_domain.ext
La validez de 397 días coincide con el máximo que aceptan los navegadores para certificados de servidor. Verifica que el certificado está firmado por tu CA:
openssl verify -CAfile ca.crt your_domain.crt
your_domain.crt: OK
Para que las máquinas de tu red confíen en esta CA, copia ca.crt (nunca ca.key) a cada una y añádela al almacén del sistema. En Ubuntu y Debian:
sudo cp ca.crt /usr/local/share/ca-certificates/tu-empresa-ca.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Importanteguarda
ca.keyfuera del servidor, en un lugar cifrado. Quien la obtenga puede emitir certificados válidos para cualquier nombre en las máquinas que confíen en tu CA.
Paso 5: Instalar el certificado en Nginx
Coloca la clave y el certificado en las rutas estándar de Ubuntu. Si tu CA te entregó certificados intermedios, Nginx necesita el certificado del servidor seguido de los intermedios en un único archivo:
cat your_domain.crt intermediate.crt > your_domain.fullchain.crt
Con la CA interna del paso anterior no hay intermedios, así que basta con copiar your_domain.crt. Instala los archivos con los permisos correctos:
sudo install -m 644 your_domain.crt /etc/ssl/certs/your_domain.crt
sudo install -m 600 -o root -g root your_domain.key /etc/ssl/private/your_domain.key
Edita el bloque server de tu sitio:
sudo nano /etc/nginx/sites-available/your_domain
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name your_domain www.your_domain;
ssl_certificate /etc/ssl/certs/your_domain.crt;
ssl_certificate_key /etc/ssl/private/your_domain.key;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html;
}
Si usas una cadena con intermedios, apunta ssl_certificate al archivo fullchain. Valida y recarga:
sudo nginx -t
sudo systemctl reload nginx
Paso 6: Verificar un certificado en un servidor
openssl s_client se conecta al servidor igual que un navegador y muestra el certificado que entrega. La opción -servername envía el nombre por SNI, imprescindible cuando hay varios sitios en la misma IP:
openssl s_client -connect your_domain:443 -servername your_domain </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
Para ver la cadena completa y el resultado de la verificación:
openssl s_client -connect your_domain:443 -servername your_domain -showcerts </dev/null 2>/dev/null | grep -E 'Verify return code|^ [0-9] s:'
0 s:CN = your_domain
Verify return code: 0 (ok)
Un código distinto de 0 indica el problema, por ejemplo 21 (unable to verify the first certificate) cuando falta el intermedio. Con la CA interna, añade -CAfile ca.crt si la máquina desde la que compruebas aún no confía en ella.
Paso 7: Comprobar que clave, CSR y certificado coinciden
Un error clásico es instalar un certificado con una clave que no le corresponde; Nginx fallará con key values mismatch. Compara el hash de la clave pública de cada archivo:
openssl pkey -in your_domain.key -pubout | sha256sum
openssl req -in your_domain.csr -noout -pubkey | sha256sum
openssl x509 -in your_domain.crt -noout -pubkey | sha256sum
Los tres hashes deben ser idénticos.
Paso 8: Convertir entre formatos
Cada plataforma espera un formato distinto. Estas son las conversiones más habituales:
| Formato | Extensiones | Uso típico |
|---|---|---|
| PEM | .pem, .crt, .key | Texto Base64. Nginx, Apache, HAProxy. |
| DER | .der, .cer | Binario. Java y algunos dispositivos. |
| PKCS#12 | .pfx, .p12 | Clave, certificado y cadena en un archivo protegido. Windows, IIS. |
PEM a DER y vuelta:
openssl x509 -in your_domain.crt -outform der -out your_domain.der
openssl x509 -in your_domain.der -inform der -out your_domain.pem
Empaquetar clave, certificado y cadena en PKCS#12 (pedirá una contraseña de exportación):
openssl pkcs12 -export -out your_domain.pfx -inkey your_domain.key -in your_domain.crt -certfile ca.crt
Extraer de un .pfx el certificado y la clave por separado:
openssl pkcs12 -in your_domain.pfx -clcerts -nokeys -out extraido.crt
openssl pkcs12 -in your_domain.pfx -nocerts -noenc -out extraido.key
chmod 600 extraido.key
Paso 9: Controlar la caducidad
Un certificado caducado deja el servicio inaccesible para los clientes. La opción -checkend comprueba si caduca dentro de un número de segundos y devuelve un código de salida, así que es ideal para scripts. Por ejemplo, para 30 días:
openssl x509 -in /etc/ssl/certs/your_domain.crt -noout -checkend 2592000
Certificate will not expire
Si faltan menos de 30 días, imprime Certificate will expire y sale con código 1. Puedes usarlo en tu sistema de monitorización o en una tarea programada que te avise. Para certificados públicos, la solución definitiva es la renovación automática con Certbot.
Solución de problemas
key values mismatch al recargar Nginx. La clave no corresponde al certificado. Compara los hashes del paso 7 y usa la clave con la que generaste la CSR.
El navegador dice que el certificado no es válido para el nombre. El nombre no está en el SAN. Revisa con openssl x509 -in your_domain.crt -noout -ext subjectAltName y vuelve a emitir el certificado con todos los nombres.
unable to get local issuer certificate o Verify return code: 21. Falta el certificado intermedio en la cadena. Crea el archivo fullchain del paso 5 con el certificado del servidor primero y los intermedios después.
Nginx no arranca con Permission denied al leer la clave. Nginx lee la clave como root al arrancar; comprueba que el archivo existe en la ruta indicada y que pertenece a root con permisos 600.
Conclusión
Has generado claves y CSR con SAN, creado certificados autofirmados y una CA interna, instalado un certificado en Nginx, verificado cadenas y coincidencia de claves, convertido entre formatos y comprobado la caducidad con OpenSSL. Con estos comandos puedes diagnosticar prácticamente cualquier problema de certificados.
Como siguientes pasos puedes:
- Obtener certificados públicos gratuitos y renovarlos automáticamente con Certbot y Let's Encrypt.
- Endurecer la configuración TLS de Nginx con HSTS y OCSP stapling.
- Añadir la comprobación de caducidad de tus certificados a tu sistema de monitorización.
