Let's Encrypt cubre la mayoría de sitios web, pero hay casos en los que necesitas instalar un certificado emitido por otra autoridad: certificados OV o EV comprados a una CA comercial, certificados de una CA interna de tu empresa o certificados autofirmados para entornos de pruebas. En este tutorial generarás una clave privada y una solicitud de firma (CSR) con OpenSSL, montarás la cadena completa del certificado, comprobarás que todo encaja y lo configurarás en Nginx o Apache en Ubuntu 24.04.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, y un usuario no root con privilegios
sudo. - Nginx o Apache instalado y sirviendo tu sitio por HTTP.
- Un dominio con su registro DNS A apuntando al servidor. En la guía se usa
your_domaincomo marcador: sustitúyelo por tu dominio real. - El puerto 443 abierto en el firewall:
sudo ufw allow 'Nginx Full'osudo ufw allow 'Apache Full'.
OpenSSL viene instalado en Ubuntu 24.04. Compruébalo:
openssl version
OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
Paso 1: Generar la clave privada y el CSR
La CA nunca ve tu clave privada: tú la generas en el servidor y le envías un CSR, que contiene la clave pública y los nombres del certificado. Crea un directorio de trabajo para los archivos del certificado y sitúate en él:
sudo mkdir -p /etc/ssl/your_domain
cd /etc/ssl/your_domain
Genera una clave RSA de 2048 bits y el CSR en un solo comando. Los navegadores solo tienen en cuenta los nombres de la extensión Subject Alternative Name (SAN), así que incluye ahí todos los dominios que debe cubrir el certificado:
sudo openssl req -new -newkey rsa:2048 -nodes \
-keyout your_domain.key -out your_domain.csr \
-subj "/C=ES/O=Your Company/CN=your_domain" \
-addext "subjectAltName=DNS:your_domain,DNS:www.your_domain"
Qué hace cada opción:
-newkey rsa:2048: crea una clave RSA nueva de 2048 bits, el tamaño que aceptan todas las CA. Puedes usarrsa:3072si tu política lo exige.-nodes: guarda la clave sin contraseña. Es necesario para que Nginx o Apache arranquen sin intervención manual.-subj: rellena el sujeto sin preguntas interactivas. Para certificados DV basta conCN; para OV o EV, la CA validará los datos de la organización.-addext: añade la extensión SAN al CSR.
Para un certificado wildcard, usa DNS:your_domain,DNS:*.your_domain en el SAN. Si prefieres una clave ECDSA, más pequeña y rápida, sustituye -newkey rsa:2048 por -newkey ec -pkeyopt ec_paramgen_curve:prime256v1, siempre que tu CA la admita.
Protege la clave privada:
sudo chmod 600 your_domain.key
Revisa el contenido del CSR antes de enviarlo:
sudo openssl req -in your_domain.csr -noout -text -verify
Certificate request self-signature verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = ES, O = Your Company, CN = your_domain
...
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:your_domain, DNS:www.your_domain
Muestra el CSR para copiarlo en el formulario de la CA:
sudo cat your_domain.csr
Copia el bloque completo, incluidas las líneas -----BEGIN CERTIFICATE REQUEST----- y -----END CERTIFICATE REQUEST-----.
Advertencia
your_domain.keyno debe salir nunca del servidor. No la envíes a la CA, no la subas a un repositorio y no la guardes en copias de seguridad sin cifrar.
Paso 2: Montar la cadena completa del certificado
Cuando la CA valide tu solicitud te entregará, como mínimo, el certificado del dominio y uno o varios certificados intermedios (a veces en un archivo llamado ca-bundle, chain o intermediate). El servidor debe enviar al cliente el certificado del dominio seguido de los intermedios; si falta un intermedio, muchos clientes (móviles, curl, APIs) rechazarán la conexión aunque el navegador de tu equipo funcione.
Sube los archivos al directorio de trabajo, por ejemplo con scp, y compruébalos. El certificado del dominio debe mostrar tu dominio como sujeto:
sudo openssl x509 -in your_domain.crt -noout -subject -issuer -dates
subject=CN = your_domain
issuer=C = US, O = Example CA, CN = Example CA Intermediate
notBefore=Sep 25 00:00:00 2026 GMT
notAfter=Sep 24 23:59:59 2027 GMT
El issuer del certificado del dominio debe coincidir con el sujeto del primer intermedio. Crea el archivo de cadena completa concatenando ambos en ese orden (dominio primero, intermedios después, sin la raíz):
sudo sh -c 'cat your_domain.crt intermediate.crt > your_domain.fullchain.crt'
Verifica que la cadena conduce a una raíz de confianza del sistema:
sudo openssl verify -untrusted intermediate.crt your_domain.crt
your_domain.crt: OK
Si la CA es interna, añade -CAfile ruta/a/la/raiz.crt al comando, ya que su raíz no está en el almacén del sistema.
Paso 3: Comprobar que la clave privada corresponde al certificado
Un error frecuente es instalar un certificado emitido para otro CSR. Compara la clave pública del certificado con la de tu clave privada:
sudo openssl x509 -in your_domain.crt -noout -pubkey | sha256sum
sudo openssl pkey -in your_domain.key -pubout | sha256sum
3f9d0c1e...a41b -
3f9d0c1e...a41b -
Los dos hashes deben ser idénticos. Si difieren, el certificado no corresponde a esta clave y el servidor web se negará a arrancar.
Alternativa: crear un certificado autofirmado para pruebas
Si solo necesitas HTTPS en un entorno de desarrollo o en un servicio interno, puedes generar la clave y un certificado autofirmado en un único paso, sin CSR ni CA:
sudo openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout /etc/ssl/your_domain/your_domain.key \
-out /etc/ssl/your_domain/your_domain.fullchain.crt \
-subj "/CN=your_domain" \
-addext "subjectAltName=DNS:your_domain,DNS:www.your_domain"
sudo chmod 600 /etc/ssl/your_domain/your_domain.key
Los navegadores mostrarán una advertencia porque ninguna CA de confianza lo ha firmado. Es adecuado para pruebas, no para un sitio público. Con este certificado puedes saltarte los pasos 2 y 3 y usar las mismas rutas en la configuración del servidor.
Paso 4: Configurar el certificado en Nginx
Abre el archivo de tu sitio:
sudo nano /etc/nginx/sites-available/your_domain
Añade un bloque para HTTPS y redirige el tráfico HTTP. Ajusta root a la ruta de tu sitio:
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name your_domain www.your_domain;
ssl_certificate /etc/ssl/your_domain/your_domain.fullchain.crt;
ssl_certificate_key /etc/ssl/your_domain/your_domain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
root /var/www/your_domain;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
ssl_certificate debe apuntar al archivo de cadena completa, no solo al certificado del dominio. Activa el sitio si aún no lo está, valida la sintaxis y recarga:
sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Si el enlace simbólico ya existía, ln mostrará un error que puedes ignorar.
Paso 4 (Apache): Configurar el certificado en Apache
Si usas Apache, activa los módulos ssl y rewrite:
sudo a2enmod ssl rewrite
Crea el VirtualHost para HTTPS:
sudo nano /etc/apache2/sites-available/your_domain-ssl.conf
<VirtualHost *:443>
ServerName your_domain
ServerAlias www.your_domain
DocumentRoot /var/www/your_domain
SSLEngine on
SSLCertificateFile /etc/ssl/your_domain/your_domain.fullchain.crt
SSLCertificateKeyFile /etc/ssl/your_domain/your_domain.key
SSLProtocol -all +TLSv1.2 +TLSv1.3
ErrorLog ${APACHE_LOG_DIR}/your_domain-ssl-error.log
CustomLog ${APACHE_LOG_DIR}/your_domain-ssl-access.log combined
</VirtualHost>
Desde Apache 2.4.8, SSLCertificateFile admite el certificado con sus intermedios en el mismo archivo, por lo que ya no hace falta SSLCertificateChainFile. En el VirtualHost del puerto 80 (/etc/apache2/sites-available/your_domain.conf) añade la redirección:
<VirtualHost *:80>
ServerName your_domain
ServerAlias www.your_domain
Redirect permanent / https://your_domain/
</VirtualHost>
Activa el sitio, valida y recarga:
sudo a2ensite your_domain-ssl
sudo apache2ctl configtest
sudo systemctl reload apache2
Syntax OK
Paso 5: Verificar la instalación
Comprueba desde el propio servidor que HTTPS responde con el certificado correcto:
curl -I https://your_domain
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Con un certificado autofirmado, curl fallará con SSL certificate problem: self-signed certificate; añade -k para ignorar la validación en pruebas.
Revisa la cadena que envía el servidor. Deben aparecer el certificado del dominio (0) y al menos un intermedio (1):
echo | openssl s_client -connect your_domain:443 -servername your_domain -showcerts 2>/dev/null | grep -E '^ ?[0-9] s:|Verify return code'
0 s:CN = your_domain
1 s:C = US, O = Example CA, CN = Example CA Intermediate
Verify return code: 0 (ok)
Un Verify return code distinto de 0 (ok), como 21 (unable to verify the first certificate), indica que falta un intermedio en el archivo de cadena completa.
Paso 6: Controlar la fecha de caducidad
Los certificados comerciales no se renuevan solos: tendrás que generar un CSR nuevo (o reutilizar el actual, si tu CA lo permite), obtener el certificado y repetir los pasos 2 a 5. Consulta la fecha de caducidad del certificado instalado:
sudo openssl x509 -in /etc/ssl/your_domain/your_domain.fullchain.crt -noout -enddate
notAfter=Sep 24 23:59:59 2027 GMT
La opción -checkend devuelve un código de salida distinto de cero si el certificado caduca dentro del número de segundos indicado, lo que permite integrarla en tu sistema de monitorización. Por ejemplo, para 30 días:
sudo openssl x509 -in /etc/ssl/your_domain/your_domain.fullchain.crt -noout -checkend 2592000
Certificate will not expire
Desde 2026 los certificados públicos tienen una validez máxima cada vez más corta, así que conviene tener esta comprobación automatizada.
Solución de problemas
- Nginx no arranca con
key values mismatcho Apache conAH02565: Certificate and private key ... do not match: el certificado no corresponde a la clave. Repite la comprobación del paso 3 y usa la clave con la que generaste el CSR. unable to verify the first certificateencurlo en apps móviles: falta el intermedio. Rehazyour_domain.fullchain.crtcon el certificado del dominio seguido del intermedio que te dio la CA.PEM_read_bio_X509_AUX ... no start line: el archivo no está en formato PEM. Si la CA te dio un.dero.cerbinario, conviértelo conopenssl x509 -inform der -in cert.cer -out cert.crt.- El navegador indica
NET::ERR_CERT_COMMON_NAME_INVALID: el dominio al que accedes no está en el SAN del certificado. Comprueba los nombres conopenssl x509 -in your_domain.crt -noout -ext subjectAltName.
Conclusión
Has generado una clave privada y un CSR con OpenSSL, montado la cadena completa del certificado, verificado que la clave y el certificado coinciden y configurado HTTPS en Nginx o Apache con redirección desde HTTP. El mismo procedimiento sirve para certificados de una CA comercial, de una CA interna o autofirmados.
Como siguientes pasos puedes:
- Añadir un recordatorio o una comprobación automática con
openssl x509 -checkendantes de cada caducidad. - Activar HTTP/2 en el servidor web ahora que el sitio sirve HTTPS.
- Valorar Let's Encrypt con Certbot para los dominios que no necesiten validación OV o EV, ya que la renovación es automática.
