Generando un certificado autofirmado con openssl que funciona en Chrome 58

52

A partir de Chrome 58 ya no acepta certificados autofirmados que se basan en Common Name: https://productforums.google.com/forum/#!topic/chrome/zVo3M8CgKzQ;context-place=topicsearchin/chrome/category $ 3ACanary% 7Cort: relevancia% 7Cspell: falso

En cambio, requiere el uso Subject Alt Name. He estado siguiendo previamente esta guía sobre cómo generar un certificado autofirmado: https://devcenter.heroku.com/articles/ssl-certificate-self que funcionó muy bien porque necesitaba los archivos server.crty server.keypara lo que estoy haciendo. Ahora necesito generar nuevos certificados que incluyen, SANsin embargo, todos mis intentos de hacerlo no han funcionado con Chrome 58.

Esto es lo que hice:

Seguí los pasos en el artículo de Heroku mencionado anteriormente para generar la clave. Luego escribí un nuevo archivo de configuración de OpenSSL:

[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name
req_extensions      = san
extensions          = san
[ req_distinguished_name ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com

Luego generó el server.crtcon el siguiente comando:

openssl req \
-new \
-key server.key \
-out server.csr \
-config config.cnf \
-sha256 \
-days 3650

Estoy en una Mac, así que abrí el server.crtarchivo con Keychain, lo agregué a mis Certificados del sistema. Entonces lo puse a Always Trust.

Con la excepción del archivo de configuración para establecer el valor SAN, estos fueron pasos similares que utilicé en versiones anteriores de Chrome para generar y confiar en el certificado autofirmado.

Sin embargo, después de esto, sigo teniendo ERR_CERT_COMMON_NAME_INVALIDChrome 58.

bcardarella
fuente

Respuestas:

62

Mi solución:

openssl req \
    -newkey rsa:2048 \
    -x509 \
    -nodes \
    -keyout server.key \
    -new \
    -out server.crt \
    -subj /CN=dev.mycompany.com \
    -reqexts SAN \
    -extensions SAN \
    -config <(cat /System/Library/OpenSSL/openssl.cnf \
        <(printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')) \
    -sha256 \
    -days 3650

Estado: funciona para mí

bcardarella
fuente
2
Gran uso de subshell. Creo que puedes simplificarlo un poco:-config <(cat /System/Library/OpenSSL/openssl.cnf ; printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')
jrwren
1
Ya no recibo el error de Nombre alternativo del sujeto, pero ahora recibo un error sobre el nombre común, y configurar el certificado descargado como "siempre confiable" no funciona. ¿Alguna idea? @bcardarella
rugbert
2
Con la actualización a Chrome 59, el certificado muestra un error como este: hay problemas con la cadena de certificados del sitio (net :: ERR_CERT_COMMON_NAME_INVALID).
theHarsh
1
Cambié dev.company.namea localhosty esto funcionó para servir el sitio de desarrollo local de localhost. En macOS también tuve que agregar el certificado a Keychain y establecer SSL en "Always Trust".
Daniel M.
1
Esa es, con mucho, la solución más simple y no requiere jugar con sslconf o instalar una CA.
bp.
16

En Windows, guarde este script en su carpeta SSL como makeCERT.bat. Creará estos archivos: example.cnf, example.crt, example.key

@echo off

REM IN YOUR SSL FOLDER, SAVE THIS FILE AS: makeCERT.bat
REM AT COMMAND LINE IN YOUR SSL FOLDER, RUN: makecert
REM IT WILL CREATE THESE FILES: example.cnf, example.crt, example.key
REM IMPORT THE .crt FILE INTO CHROME Trusted Root Certification Authorities
REM REMEMBER TO RESTART APACHE OR NGINX AFTER YOU CONFIGURE FOR THESE FILES

REM PLEASE UPDATE THE FOLLOWING VARIABLES FOR YOUR NEEDS.
SET HOSTNAME=example
SET DOT=com
SET COUNTRY=US
SET STATE=KS
SET CITY=Olathe
SET ORGANIZATION=IT
SET ORGANIZATION_UNIT=IT Department
SET EMAIL=webmaster@%HOSTNAME%.%DOT%

(
echo [req]
echo default_bits = 2048
echo prompt = no
echo default_md = sha256
echo x509_extensions = v3_req
echo distinguished_name = dn
echo:
echo [dn]
echo C = %COUNTRY%
echo ST = %STATE%
echo L = %CITY%
echo O = %ORGANIZATION%
echo OU = %ORGANIZATION_UNIT%
echo emailAddress = %EMAIL%
echo CN = %HOSTNAME%.%DOT%
echo:
echo [v3_req]
echo subjectAltName = @alt_names
echo:
echo [alt_names]
echo DNS.1 = *.%HOSTNAME%.%DOT%
echo DNS.2 = %HOSTNAME%.%DOT%
)>%HOSTNAME%.cnf

openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout %HOSTNAME%.key -days 3560 -out %HOSTNAME%.crt -config %HOSTNAME%.cnf
STWilson
fuente
13

Aquí hay una solución que me funciona:

Crear clave CA y cert

# openssl genrsa -out server_rootCA.key 2048
# openssl req -x509 -new -nodes -key server_rootCA.key -sha256 -days 3650 -out server_rootCA.pem

Crear server_rootCA.csr.cnf

# server_rootCA.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=DE
ST=Berlin
L=NeuKoelln
O=Weisestrasse
OU=local_RootCA
[email protected]
CN = server.berlin

Crear archivo de configuración v3.ext

# v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = server.berlin

Crear clave de servidor

# openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <( cat server_rootCA.csr.cnf )

Crear certificado de servidor

# openssl x509 -req -in server.csr -CA server_rootCA.pem -CAkey server_rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile v3.ext

Agregue cert y clave al archivo de sitio Apache2, sección HTTPS (puerto 443)

SSLCertificateFile    /etc/apache2/ssl/server.crt
SSLCertificateKeyFile    /etc/apache2/ssl/server.key

Copie server_rootCA.pem del servidor a su máquina.

# scp [email protected]:~/server_rootCA.pem .

.. y agregarlo al navegador Chromium

Chromium -> Setting -> (Advanced) Manage Certificates -> Import -> 'server_rootCA.pem'

TODOS ESTÁS HECHO!

PD: en lugar de crear un par de certificados de CA y servidor funcionales (según las instrucciones anteriores), simplemente puede deshabilitar los encabezados HSTS en la configuración de su servidor HTTP. Esto evitará que Chromium imponga HTTPS y permitirá a los usuarios hacer clic en "Avanzado → proceder a your.url (inseguro)" sin tener que obtener e instalar su certificado CA (server_rootCA.pem) personalizado. En otras palabras, tener que deshabilitar HSTS permitirá que su sitio se vea públicamente a través de HTTP y / o una conexión HTTPS insegura (¡cuidado!).

Para Apache2, agregue lo siguiente al archivo de sitio, sección HTTP (puerto 80)

Header unset Strict-Transport-Security
Header always set Strict-Transport-Security "max-age=0;includeSubDomains"

Probado en Debian / Apache2.4 + Debian / Chromium 59

https://ram.k0a1a.net/self-signed_https_cert_after_chrome_58

binary.koala
fuente
Seguir la ruta de una autoridad de CA raíz que luego firma los certificados individuales es la única forma en que podría lograr que Chrome se autentique por completo; También tiene la ventaja de que solo necesitaba que la gente instalara un solo certificado. Gracias
geoff
44
¿Puede alguien explicarme por qué todos en esta área parecen usar bashisms como en -config <( cat server_rootCA.csr.cnf )lugar de simplemente -config server_rootCA.csr.cnf?
César
¿Puede actualizar su respuesta en relación con los encabezados de apache que pueden evitar el problema? ¿Puede indicar dónde deben ir estos dentro de una definición de host virtual? He intentado varias alternativas y todavía no puedo acceder a los sitios a través de https. Gracias
Nikos M.
12

Hay varias respuestas excelentes que dan ejemplos de cómo hacer que esto funcione, pero ninguna que explique dónde fallaron las cosas en su intento. OpenSSL puede ser bastante no intuitivo algunas veces, por lo que vale la pena recorrerlo.

Primero, aparte, OpenSSL por defecto ignora cualquier valor de nombre distinguido que proporcione en la configuración. Si desea usarlos, debe agregarlos prompt = no a su configuración. Además, el comando tal como está escrito solo genera una solicitud de certificado, no un certificado en sí mismo, por lo que el -dayscomando no hace nada.

Si genera su solicitud de certificado con este comando que dio e inspeccionó el resultado, el Nombre alternativo del sujeto está presente:

$ openssl req -new -key server.key -out server.csr -config config.cnf -sha256
$ openssl req -text -noout -in server.csr
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:dev.mycompany.com
    Signature Algorithm: sha256WithRSAEncryption
         ...

Pero luego, si genera el certificado utilizando el comando en el enlace heroku e inspecciona el resultado, falta el nombre alternativo del sujeto:

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
$ openssl x509 -text -noout -in server.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            89:fd:75:26:43:08:04:61
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Validity
            Not Before: Jan 21 04:27:21 2018 GMT
            Not After : Jan 21 04:27:21 2019 GMT
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         ...

La razón es que, por defecto, OpenSSL no copia extensiones de la solicitud al certificado. Normalmente, el certificado sería creado / firmado por una CA en base a una solicitud de un cliente, y algunas extensiones podrían otorgarle al certificado más poder del que la CA tenía la intención de confiar ciegamente en las extensiones definidas en la solicitud.

Hay formas de decirle a OpenSSL que copie las extensiones, pero en mi humilde opinión, es más trabajo que simplemente proporcionar las extensiones en un archivo de configuración cuando genera el certificado.

Si intentara usar su archivo de configuración existente, no funcionará porque la sección de nivel superior está marcada, [req]por lo que esa configuración solo se aplica al comando req, no al comando x509. No es necesario tener un marcador de sección de nivel superior, por lo que puede eliminar esa primera línea y luego funcionará bien tanto para generar solicitudes como para certificados.

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt -extfile config.cnf

Alternativamente, puede usar el -x509argumento del reqcomando para generar un certificado autofirmado en un solo comando, en lugar de crear primero una solicitud y luego un certificado. En este caso, no es necesario eliminar la [req]línea de sección, ya que esa sección es leída y utilizada por el comando req.

$ openssl req -x509 -sha256 -days 365 -key server.key -out server.crt -config config.cnf

Para recapitular, aquí está el archivo de configuración modificado utilizado en los comandos anteriores:

default_bits        = 2048
distinguished_name  = dn
x509_extensions     = san
req_extensions      = san
extensions          = san
prompt              = no
[ dn ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com
pavon
fuente
2
Esta es la única explicación que me ayudó a entender por qué el certificado salió sin una SAN (en mi caso, necesitaba incluir x509_extensions en el archivo de configuración)
Daniel Beardsmore
2

Mi solución es mantener el principal openssl.cnfcomo está y solo al final agregar una nueva sección como [ cert_www.example.com ]donde www.example.com es el sitio web para el que quiero crear un certificado, y en él, poner subjectAltNamelo que necesitaría (y Algo más). Por supuesto, la sección podría llamarse como quieras.

Después de eso, puedo ejecutar el openssl reqcomando como antes, solo agrego -extensions cert_www.example.compara que su contenido sea recogido y agrego -subjpara agregar directamente toda la información de DN.

No olvide verificar el contenido del certificado después de su creación y antes de su uso, con openssl x509 -text

Patrick Mevzek
fuente
1

Bash script con config horneado

Como un script de shell que debería funcionar en plataformas con bash. Asume el HOSTNAMEentorno env establecido para el shell o proporciona un nombre de host de su elección, p. Ej.self_signed_cert.sh test

set -e

if [ -z "$1" ]; then
  hostname="$HOSTNAME"
else
  hostname="$1"
fi

local_openssl_config="
[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = san_self_signed
[ req_distinguished_name ]
CN=$hostname
[ san_self_signed ]
subjectAltName = DNS:$hostname, DNS:localhost
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
extendedKeyUsage = serverAuth, clientAuth, timeStamping
"

openssl req \
  -newkey rsa:2048 -nodes \
  -keyout "$hostname.key.pem" \
  -x509 -sha256 -days 3650 \
  -config <(echo "$local_openssl_config") \
  -out "$hostname.cert.pem"
openssl x509 -noout -text -in "$hostname.cert.pem"

Lo anterior inyecta más o menos las necesidades mínimas de información de archivo de configuración openssl.

Tenga en cuenta que se incluye extra DNS:localhostcomo SAN para permitir las pruebas a través de localhost más fácilmente. Elimina ese bit extra del script si no lo quieres.

Crédito

La respuesta de bcardarella es excelente (no puedo comentar / votar porque no hay suficiente representante). Sin embargo, la respuesta utiliza una ubicación de archivo de configuración openssl existente que es específica de la plataforma ... por lo tanto:

Funciona para mi

Obviamente, uno simplemente necesitaría encontrar el archivo de configuración openssl para su propia plataforma y sustituir la ubicación correcta.

Prueba

Para una forma de probar, importe test.cert.pema las autoridades de Chrome en chrome://settings/certificatesy:

openssl s_server -key test.key.pem -cert test.cert.pem -accept 20443 -www &
openssl_pid=$!
google-chrome https://localhost:20443

Y después de probar

kill $openssl_pid
JPvRiel
fuente