OpenSl rifiuta i certificati autofirmati senza vincoli di base?

7

Ho due certificati autofirmati estremamente simili, generati con due metodi diversi.

Per testarli ho:

  1. Aggiunta una voce nel mio file hosts per local.mydomain.com
  2. Configura un server nginx per l'ascolto su quel dominio sulla porta 443 con il certificato in prova più la chiave privata associata (quindi, cambio il certificato e riavvia il nginx per il confronto)
  3. Connesso a nginx con openssl s_client -connect local.mydomain.com -CAfile /path/to/the/ca/cert.pem

Un certificato fallisce:

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 certificato ha esito positivo:

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

Confronto i dettagli dei certificati con openssl x509 -in /path/to/the/ca/cert.pem -text -noout

Il certificato fallito:

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>

Il certificato di lavoro:

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>

Guardando a questo la differenza più ovvia è che il certificato di lavoro ha CA:TRUE sotto X509v3 Basic Constraints . Tuttavia, dalla lettura sul Web avevo l'impressione che i certificati autofirmati non dovessero essere CA, in particolare questo dice che normalmente non saranno:

Domande di certificato autofirmato di base

La risposta là dice che essendo autofirmati non c'è nessuna CA coinvolta. Ma forse openssl richiede cerattori autofirmati per avere quel set comunque? O forse i certificati differiscono in qualche altro modo pertinente?

UPDATE:

Ho passato un po 'di tempo a provare a printf eseguire il debug degli interni di openssl, non che ne capisca niente. Nel file v3_purp.c è presente la seguente macro:

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

Viene utilizzato in questo bit di codice che controlla i certificati autofirmati:

/* 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;

Nel caso del certificato fallito (x)->ex_flags & EXFLAG_KUSAGE è uguale a 2

EXFLAG_KUSAGE viene impostato per il certificato in errore qui, prima nello stesso file:

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);
    }
    ....

Quindi sembra che il problema è che il certificato fallito ha l'estensione X509v3 Key Usage , ma non specifica KU_KEY_CERT_SIGN in quell'estensione?

UPDATE 2:

In base al link :

"The keyCertSign bit is asserted when the subject public key is used for verifying signatures on public key certificates. If the keyCertSign bit is asserted, then the cA bit in the basic constraints extension (Section 4.2.1.9) MUST also be asserted."

Quindi il bit CA nei vincoli di base non deve essere presente, ma se si include una sezione Utilizzo chiave X509v3 nel certificato, in base al codice openssl, è necessario specificare keyCertSign e, in base a RFC, specificare keyCertSign, quindi devi anche includere i vincoli di base del bit CA?

    
posta junichiro 19.11.2016 - 20:13
fonte

2 risposte

5

Il problema descritto nella domanda può essere riprodotto con una configurazione più semplice. Ho creato due certificati autofirmati per il test, che differiscono solo per il fatto che uno ha un flag CA ( ss-ca.pem ) mentre l'altro no ( ss-noca.pem ). Con openssl verify è possibile verificare se il certificato può essere verificato su un percorso CA specifico.

Il certificato autofirmato con CA: true viene verificato con successo su se stesso ('OK') anche se inciampa su X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (errore 18) durante la verifica della catena:

$ 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 il certificato autofirmato con CA: false la verifica non riesce (no 'OK') e mostra l'errore X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (errore 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

La mia teoria è che OpenSSL cerchi di costruire la catena di fiducia in un certificato dato con -CAfile . Per costruire la catena di fiducia l'oggetto del certificato dell'emittente deve corrispondere all'emittente del certificato, la firma deve essere valida (cioè convalidata utilizzando la chiave pubblica dell'emittente) e il certificato dell'emittente deve essere autorizzato a firmare i certificati, cioè CA: true. Mentre i primi due controlli non restituiscono alcun problema con entrambi i certificati, il controllo di CA: true è valido solo per ss-ca.pem .

Penso che la differenza principale tra OpenSSL -CAfile e altri concetti di un trust store sia che -CAfile fa solo ciò che suggerisce: contiene un elenco di CA affidabili che vengono utilizzate per convalidare la catena di fiducia. Contrariamente a queste altre implementazioni di trust store potrebbero anche contenere qualsiasi tipo di certificati in cui la routine di validazione controlla semplicemente se il certificato inviato dal server è direttamente attendibile perché è contenuto nel trust store, indipendentemente dal tipo di flag del certificato o persino se è scaduto.

Questa differenza tra un trust store generico e OpenSSL -CAfile può anche essere vista quando si inserisce un certificato di entità finale non CA come attendibile nell'archivio. Nell'esempio utilizzo un certificato CA ca.pem che ha emesso un certificato di entità finale server.pem che, naturalmente, non ha CA: true.

La convalida del certificato dell'entità finale rispetto al certificato CA funziona come previsto:

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

Tuttavia, provare a fidarsi direttamente del certificato dell'entità finale inserendolo nell'archivio CA non funziona perché l'archivio CA non è un trust store generico ma limitato ai certificati CA:

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

Con un trust store generico, l'ultima verifica avrebbe dovuto essere eseguita anche perché il certificato dell'entità finale era dichiarato esplicitamente come attendibile.

    
risposta data 20.11.2016 - 08:43
fonte
1

Questo problema è stato risolto oggi in OpenSSL (vedi link ); dovrebbe essere disponibile presto.

    
risposta data 18.12.2018 - 11:59
fonte

Leggi altre domande sui tag