¿openssl rechaza certificados autofirmados sin restricciones básicas?

7

Tengo dos certificados autofirmados extremadamente similares, generados a través de dos métodos diferentes.

Para probarlos tengo:

  1. Se agregó una entrada en el archivo de mis hosts para local.mydomain.com
  2. Configure un servidor nginx para que escuche en ese dominio en el puerto 443 con el certificado en prueba más la clave privada asociada (a continuación, cambio el certificado y reinicio nginx para comparar)
  3. Conectado a nginx con openssl s_client -connect local.mydomain.com -CAfile /path/to/the/ca/cert.pem

Un certificado falla:

CONNECTED(00000003)
depth=0 CN = local.mydomain.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = local.mydomain.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=local.mydomain.com
   i:/CN=local.mydomain.com
---

Un certificado tiene éxito:

CONNECTED(00000003)
depth=0 CN = local.mydomain.com
verify return:1
---
Certificate chain
 0 s:/CN = local.mydomain.com
   i:/CN = local.mydomain.com
---

Comparo los detalles de los certificados con openssl x509 -in /path/to/the/ca/cert.pem -text -noout

El certificado que falla:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            47:dc:02:c7:11:fc:8e:96:45:22:aa:6b:23:79:32:ca
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=local.mydomain.com
        Validity
            Not Before: Nov 18 11:55:31 2016 GMT
            Not After : Nov 18 12:15:31 2017 GMT
        Subject: CN=local.mydomain.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    <stuff>
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Client Authentication, TLS Web Server Authentication
            X509v3 Subject Alternative Name:
                DNS:local.mydomain.com
            X509v3 Subject Key Identifier:
                6D:4F:AF:E4:60:23:72:E5:83:27:91:7D:1D:5F:E9:7C:D9:B6:00:2A
    Signature Algorithm: sha256WithRSAEncryption
         <stuff>

El certificado de trabajo:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            9b:6b:3d:a3:b9:a3:a4:b4
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=local.mydomain.com
        Validity
            Not Before: Nov 19 13:27:30 2016 GMT
            Not After : Nov 19 13:27:30 2017 GMT
        Subject: CN=local.mydomain.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    <stuff>
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                03:E7:DA:AA:2E:CC:23:ED:C5:07:3D:E1:33:86:F5:22:D4:76:EB:CB
            X509v3 Authority Key Identifier:
                keyid:03:E7:DA:AA:2E:CC:23:ED:C5:07:3D:E1:33:86:F5:22:D4:76:EB:CB

            X509v3 Basic Constraints:
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         57<stuff>

Mirando esto, la diferencia más obvia es que el certificado de trabajo tiene CA:TRUE debajo de X509v3 Basic Constraints . Sin embargo, al leer en la web tuve la impresión de que los certificados autofirmados no estaban destinados a ser CA, en particular, esto dice que normalmente no lo serán:

Preguntas básicas sobre certificados autofirmados

La respuesta dice que al autofirmarse no hay CA involucrada. Pero tal vez openssl requiere certificados autofirmados para tener ese conjunto de todos modos? ¿O tal vez los certificados difieren de alguna otra manera pertinente?

ACTUALIZACIÓN:

Pasé un tiempo tratando de imprimir y depurar las partes internas de openssl, no es que entienda nada de eso. En el archivo v3_purp.c se encuentra la siguiente macro:

#define ku_reject(x, usage) \
        (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))

Se utiliza en este bit de código que verifica los certificados autofirmados:

/* Does subject name match issuer ? */

if (X509_check_akid(x, x->akid) == X509_V_OK &&
            !ku_reject(x, KU_KEY_CERT_SIGN))
            x->ex_flags |= EXFLAG_SS;

En el caso del certificado que falla, (x)->ex_flags & EXFLAG_KUSAGE es igual a 2

EXFLAG_KUSAGE se configura para el certificado que falla aquí, anteriormente en el mismo archivo:

static void x509v3_cache_extensions(X509 *x)
{
    ......
    if ((usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) {
        if (usage->length > 0) {
            x->ex_kusage = usage->data[0];
            if (usage->length > 1)
                x->ex_kusage |= usage->data[1] << 8;
        } else
            x->ex_kusage = 0;
        x->ex_flags |= EXFLAG_KUSAGE;
        ASN1_BIT_STRING_free(usage);
    }
    ....

Parece que el problema es que el certificado que falla tiene la extensión X509v3 Key Usage , pero no especifica KU_KEY_CERT_SIGN en esa extensión?

ACTUALIZACIÓN 2:

Según enlace :

  

"El bit keyCertSign se afirma cuando se utiliza la clave pública del sujeto   Para la verificación de firmas en certificados de clave pública. Si el   Se afirma el bit keyCertSign, luego el bit cA en las restricciones básicas   la extensión (Sección 4.2.1.9) DEBE ser declarada ".

Por lo tanto, el bit CA en las restricciones básicas no tiene por qué estar presente, pero si incluye una sección de Uso de claves X509v3 en el certificado, entonces de acuerdo con la base de código de openssl, debe especificar keyCertSign, y de acuerdo con el RFC si especifica keyCertSign También debe incluir las restricciones básicas de bits de CA.

    
pregunta junichiro 19.11.2016 - 20:13
fuente

2 respuestas

5

El problema descrito en la pregunta se puede reproducir con una configuración más sencilla. He creado dos certificados autofirmados para las pruebas que solo difieren en que uno tiene un indicador de CA ( ss-ca.pem ) mientras que el otro no ( ss-noca.pem ). Con openssl verify se puede verificar si el certificado se puede verificar en una ruta de CA específica.

El certificado autofirmado con CA: true se verifica exitosamente contra sí mismo ('OK') aunque tropieza con X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (error 18) al verificar la cadena:

$ openssl verify -CAfile ss-ca.pem ss-ca.pem 
ss-ca.pem: CN = test CA
error 18 at 0 depth lookup:self signed certificate
OK

Con el certificado autofirmado con CA: falso, la verificación no se realiza correctamente (no 'Aceptar') y muestra el error X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (error 20):

$ openssl verify -CAfile ss-noca.pem ss-noca.pem 
ss-noca.pem: CN = test no CA
error 20 at 0 depth lookup:unable to get local issuer certificate

Mi teoría es que OpenSSL intenta construir la cadena de confianza en un certificado otorgado con -CAfile . Para construir la cadena de confianza, el sujeto del certificado del emisor debe coincidir con el emisor del certificado, la firma debe ser válida (es decir, validada con la clave pública del emisor) y el certificado del emisor debe poder firmar certificados, es decir, CA: true. Si bien las dos primeras comprobaciones no devuelven problemas con ambos certificados, la comprobación de CA: true solo es válida para ss-ca.pem .

Creo que la principal diferencia entre OpenSSL -CAfile y otros conceptos de un almacén de confianza es que -CAfile solo hace lo que su nombre implica: contiene una lista de CA confiables que se usan para validar la cadena de confianza. Al contrario de lo que ocurre con este otro almacén de confianza, las implementaciones también pueden contener cualquier tipo de certificados en los que la rutina de validación simplemente compruebe si el certificado enviado por el servidor es de confianza directa porque está contenido en el almacén de confianza, independientemente del tipo de indicadores que tenga el certificado o incluso si está vencido.

Esta diferencia entre un almacén de confianza de propósito general y OpenSSL -CAfile también se puede ver cuando se coloca un certificado de entidad final que no es de CA como confiable en la tienda. En el ejemplo, uso un certificado de CA ca.pem que ha emitido un certificado de entidad final server.pem que, por supuesto, no tiene CA: verdadero.

La validación del certificado de entidad final contra el certificado de CA funciona como se esperaba:

$ openssl verify -CAfile ca.pem server.pem
server.pem: OK

Pero tratar de confiar directamente en el certificado de la entidad final colocándolo en el almacén de CA no funciona porque el almacén de CA no es un almacén de confianza de propósito general, sino que está limitado a los certificados de CA:

$ openssl verify -CAfile server.pem server.pem
error 20 at 0 depth lookup:unable to get local issuer certificate

Con un almacén de confianza de propósito general, la última verificación también debería haber tenido éxito porque el certificado de la entidad final se declaró explícitamente como confiable.

    
respondido por el Steffen Ullrich 20.11.2016 - 08:43
fuente
0

Este problema se ha solucionado hoy en OpenSSL (consulte enlace ); debería estar disponible pronto.

    
respondido por el dvo 18.12.2018 - 11:59
fuente

Lea otras preguntas en las etiquetas