Cybersecurity - WEB Server

Errore SSL “unable to get local issuer certificate”

Durante la configurazione di un certificato SSL commerciale su un server Nginx, è possibile imbattersi nel seguente errore

verify error:num=20:unable to get local issuer certificate

Si tratta di uno degli errori TLS più comuni in ambienti che utilizzano certificati emessi da CA commerciali come Sectigo, DigiCert o GlobalSign. Nonostante il certificato sia valido e correttamente emesso, il client non riesce a verificarne l’autenticità perché la catena di fiducia è incompleta.

Cos’è la catena di certificati (Certificate Chain)

Quando un client (browser o sistema operativo) si connette a un server HTTPS, verifica che il certificato SSL presentato dal server sia firmato da una Certificate Authority (CA) fidata. Questa verifica avviene risalendo una catena gerarchica

Catena di certificati (Certificate Chain)

La Root CA è presente nel trust store del sistema operativo o del browser. I certificati intermedi fungono da collegamento tra il certificato del dominio e la Root CA. Se uno o più certificati intermedi sono assenti, il client non riesce a completare la verifica e restituisce l’errore “unable to get local issuer certificate“.

Con Let’s Encrypt e Certbot questo problema non si presenta, poiché il client ACME costruisce e installa automaticamente la catena completa. Con i certificati commerciali, invece, la configurazione della chain è responsabilità dell’amministratore.

Diagnosi da Command Line

Prima di procedere con la soluzione, è opportuno verificare l’effettivo stato della catena di certificati direttamente da terminale utilizzando openssl

openssl s_client -connect dominio.it:443 2>&1

In presenza di una catena incompleta, l’output conterrà

depth=0 CN = dominio.it
verify error:num=20:unable to get local issuer certificate
verify return:1
---
...
Verify return code: 20 (unable to get local issuer certificate)

Il valore depth=0 indica che OpenSSL si è fermato al primo livello (il certificato del dominio) senza riuscire a risalire la catena. Per visualizzare tutti i certificati che il server sta inviando possiamo anche usare il comando filtrando per le righe che ci interessano

openssl s_client -connect dominio.it:443 -showcerts 2>&1 | grep -E "depth|verify|subject|issuer"

Quando la catena è completa, l’output mostrerà tutti i livelli

depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Sectigo Limited, CN = Sectigo RSA Domain Validation CA
verify return:1
depth=0 CN = dominio.it
verify return:1
...
Verify return code: 0 (ok)

L’ultima riga, Verify return code: 0 (ok), conferma che la catena è correttamente configurata e verificabile.

Causa: i certificati intermedi mancanti

I certificati commerciali vengono consegnati dalla CA sotto forma di uno ZIP contenente più file. Sectigo, ad esempio, include tipicamente

  • dominio.crt – il certificato del dominio
  • intermediate1.cer – primo certificato intermedio
  • intermediate2.cer – secondo certificato intermedio (più vicino alla Root CA)

L’errore si verifica quando nella configurazione di Nginx viene referenziato solo il file dominio.crt, senza includere gli intermedi

# Configurazione errata — catena incompleta
ssl_certificate /etc/nginx/ssl/dominio.crt;

Soluzione: creare il file fullchain

La soluzione consiste nel creare un unico file che contenga il certificato del dominio e tutti i certificati intermedi, nell’ordine corretto: prima il certificato del dominio, poi gli intermedi in sequenza dalla CA più vicina al dominio fino a quella più vicina alla Root.

cat dominio.crt intermediate1.cer intermediate2.cer > fullchain.crt

L’ordine è fondamentale! Il certificato del dominio deve essere sempre il primo, seguito dagli intermedi in ordine gerarchico crescente verso la Root CA.

Il file risultante deve contenere un blocco BEGIN CERTIFICATE per ogni certificato incluso

grep -c "BEGIN CERTIFICATE" fullchain.crt
# Output atteso: 3

Copiamo ora il file in una directory che poi andremo a specificare nel file configurazione

cp fullchain.crt /etc/nginx/ssl/fullchain.crt
chmod 644 /etc/nginx/ssl/fullchain.crt

Aprire il file di configurazione del sito (tipicamente in /etc/nginx/sites-available/ o /etc/nginx/conf.d/) e aggiornare la direttiva ssl_certificate

server {
    listen 443 ssl;
    server_name dominio.it www.dominio.it;

    ssl_certificate     /etc/nginx/ssl/fullchain.crt;
    ssl_certificate_key /etc/nginx/ssl/private.key;
}

Testiamo ora che la configurazione del file nginx sia corretta e ricarichiamo la configurazione

nginx -t
systemctl reload nginx

Il comando nginx -t verifica la correttezza della configurazione prima di applicarla. In assenza di errori, systemctl reload nginx ricarica il server senza interruzione del servizio.

Possiamo ora verificare nuovamente la catena con OpenSSL

openssl s_client -connect dominio.it:443 2>&1 | grep "Verify return code"

# output atteso
Verify return code: 0 (ok)

Per una verifica più completa è possibile utilizzare il tool online SSL Labs (ssllabs.com/ssltest), che analizza l’intera configurazione TLS del server e assegna un rating. Una configurazione corretta con HSTS abilitato consente di ottenere il massimo rating A+.

Conclusione

L’errore “unable to get local issuer certificate” è nella quasi totalità dei casi riconducibile a una catena di certificati incompleta. La soluzione è semplice: creare un file fullchain.crt che concateni il certificato del dominio con tutti i certificati intermedi forniti dalla CA, e configurare nginx affinché utilizzi questo file al posto del solo certificato.

È un passaggio che con Let’s Encrypt avviene in modo trasparente e automatico, ma che con i certificati commerciali richiede un intervento manuale. Una volta compreso il meccanismo della certificate chain, tuttavia, la configurazione diventa rapida e ripetibile.

Vale la pena sottolineare che, sebbene in questo articolo si sia fatto riferimento a Nginx, il principio è identico per qualsiasi altro web server: su Apache il fullchain viene specificato nella direttiva SSLCertificateChainFile (o direttamente in SSLCertificateFile nelle versioni più recenti), mentre su IIS i certificati intermedi vengono importati nel relativo store tramite la console di gestione certificati di Windows (certlm.msc). Cambia la sintassi di configurazione, ma la necessità di fornire la catena completa al client rimane invariata.