Risoluzione problemi OpenSSL: come correggere l’errore unable to get local issuer certificate
Durante la creazione di connessioni sicure con OpenSSL (ad esempio durante richieste di rete in ambiente PHP, l’uso dei comandi openssl
o curl
, oppure stabilendo connessioni SSL/TLS in applicazioni Node.js/Python), gli sviluppatori possono incorrere nell’errore 20:unable to get local issuer certificate
. Questo problema, storico e molto comune, è legato al modo in cui OpenSSL verifica i certificati del peer.
Per motivi di sicurezza, OpenSSL richiede di conoscere e potersi fidare esplicitamente delle Autorità di Certificazione (CA) di root quando valida la catena di certificati. Se non trova o non viene impostato un “ancora di fiducia”, OpenSSL non può controllare la validità del certificato del server, e quindi mostra questo errore.
In questa guida viene spiegato il motivo di questo errore e vengono fornite le soluzioni specifiche per l’ambiente ServBay, inclusa la configurazione del trust store OpenSSL per PHP, Python, Node.js e il comando curl
.
Messaggio d’errore 20:unable to get local issuer certificate
Descrizione del problema
Quando OpenSSL tenta di verificare il certificato SSL/TLS di un server remoto, costruisce una catena di certificazione dal certificato del server fino a una CA di root affidabile. Se localmente non trova tutti i certificati intermedi necessari o quello della root CA, oppure se non è impostato un trust store (tramite CAFile o CAPath), la verifica fallisce, generando l’errore 20:unable to get local issuer certificate
.
In breve: OpenSSL non sa a quale autorità di certificazione può affidarsi, quindi non può autenticare il server con cui sta comunicando.
Versioni di OpenSSL e percorsi CA in ServBay
ServBay, ambiente integrato per lo sviluppo web locale, include di default OpenSSL e i certificati delle CA più utilizzate, pronti per gli sviluppatori. La versione di OpenSSL varia in base all’architettura del dispositivo:
- ServBay per Apple Silicon (chip M series): include OpenSSL versione 3.2.1
- ServBay su chip Intel: include OpenSSL versione 1.1.1u
I relativi certificati CA (cacert.pem
) e la directory dei certificati (certs
) si trovano nel percorso d’installazione dei pacchetti ServBay. È importante individuare il percorso corretto in base alla piattaforma:
ini
# File bundle con tutti i certificati root affidabili
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Directory dei singoli certificati (solitamente cacert.pem è sufficiente, ma capath può servire in certi casi)
capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
2
3
4
ini
# File bundle con tutti i certificati root affidabili
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Directory dei singoli certificati
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
2
3
4
ini
# File bundle con tutti i certificati root affidabili
cafile=C:\ServBay\package\common\openssl\3.3\cacert.pem
# Directory dei singoli certificati
capath=C:\ServBay\package\common\openssl\3.3\certs
1
2
3
4
2
3
4
La soluzione principale per l’errore unable to get local issuer certificate
è indicare esplicitamente il percorso di cafile
o capath
dove necessario, così che OpenSSL possa trovare i certificati CA affidabili.
Esempi di soluzioni
Ecco come specificare il trust store CA di OpenSSL nei principali strumenti e linguaggi.
Esempio 1: Test connessione con comando openssl
Se riscontri errori con openssl s_client
:
bash
openssl s_client -quiet -connect gmail.com:443
1
Puoi vedere questa tipologia di errore, con la riga verify error:num=20:unable to get local issuer certificate
:
bash
depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 C=US, O=Google Trust Services, CN=WR2
verify return:1
depth=0 CN=gmail.com
verify return:1
# ... altre informazioni di connessione ...
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Soluzione:
Specifica il percorso del file delle CA fornito da ServBay con il parametro -CAfile
:
bash
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem
1
bash
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
1
Se la connessione e la verifica hanno esito positivo, l’output non mostrerà più verify error:num=20
:
bash
depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1
verify return:1
depth=1 C=US, O=Google Trust Services, CN=WR2
verify return:1
depth=0 CN=gmail.com
verify return:1
# ... altre informazioni di connessione ...
1
2
3
4
5
6
7
2
3
4
5
6
7
Esempio 2: Configurazione OpenSSL in PHP
Molte funzioni PHP (ad esempio file_get_contents
su URL HTTPS, stream_socket_client
per SSL, cURL, ecc.) utilizzano OpenSSL come base. È possibile configurare il trust store delle CA modificando php.ini
o l’opzione di contesto stream nel codice.
Metodo A: Modificare php.ini
(consigliato)
Questa è la soluzione globale più rapida. Modifica il php.ini
della versione PHP corrente (puoi accedervi dal pannello di controllo ServBay) nella sezione [openssl]
, aggiungendo o aggiornando queste righe per impostare openssl.cafile
e openssl.capath
in base all’architettura.
ini
[openssl]
; Specifica il file bundle CA affidabile
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Specifica la directory dei certificati CA (opzionale, ma consigliata)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
5
2
3
4
5
ini
[openssl]
; Specifica il file bundle CA affidabile
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Specifica la directory dei certificati CA (opzionale, ma consigliata)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
5
2
3
4
5
Dopo aver modificato php.ini
, riavvia il servizio PHP in ServBay (o ServBay stesso) per applicare le nuove impostazioni.
Metodo B: Configurazione nel codice (solo per connessioni specifiche)
Se non vuoi modificare il php.ini
globale, puoi impostare il percorso CA tramite le opzioni del contesto stream durante la creazione della connessione SSL/TLS:
php
<?php
// Esempio: connessione a server SMTP SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Scegli il percorso giusto per la tua versione ServBay
// Per Apple Silicon:
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Per Intel:
// $caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true, // Abilita la verifica del certificato del peer
'verify_peer_name' => true, // Verifica che l’host nel certificato corrisponda
'allow_self_signed' => false, // Blocca certificati autofirmati, a meno che non siano affidabili
'cafile' => $caCertFile, // Specifica il file CA bundle
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Opzionale
],
];
$context = stream_context_create($contextOptions);
// Crea la connessione SSL/TLS utilizzando il contesto configurato
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // Timeout di connessione
STREAM_CLIENT_CONNECT,
$context // Opzioni di contesto
);
if ($connection) {
echo "Connessione stabilita verso $server:$port\n";
// Esempio: invio comando EHLO
fwrite($connection, "EHLO servbay.demo\r\n"); // Usando un dominio di esempio ServBay
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Connessione fallita verso $server:$port. Errore: $errstr ($errno)\n";
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Esempio 3: OpenSSL in Python (modulo ssl)
Il modulo ssl
di Python permette di creare un contesto SSL/TLS e specificare il file delle CA affidabili:
python
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Scegli il percorso giusto per la tua versione ServBay
# Per Apple Silicon:
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
# Per Intel:
# ca_cert_file = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem'
# Crea un contesto SSL predefinito, specificando il CA file
context = ssl.create_default_context(cafile=ca_cert_file)
# Puoi anche usare la directory: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Crea la connessione socket normale
with socket.create_connection((server, port)) as sock:
# Incapsula la socket con SSL
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"Connessione SSL stabilita. Protocollo negoziato: {ssock.version()}")
# Esempio: invio comando EHLO
ssock.sendall(b"EHLO servbay.demo\r\n") # Usando un dominio di esempio ServBay
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
except Exception as e:
print(f"Connessione o errore SSL fallito: {e}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Esempio 4: OpenSSL in Node.js (modulo tls)
Il modulo tls
di Node.js gestisce le connessioni SSL/TLS. Nel campo ca
puoi indicare il contenuto del file cacert.pem
ServBay. Ecco come fare:
javascript
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Sito affidabile e noto come esempio
const port = 443;
// Scegli il percorso giusto per la tua versione ServBay
// Per Apple Silicon:
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Per Intel:
// const caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
const options = {
host: server,
port: port,
// Leggi il contenuto del file CA
ca: fs.readFileSync(caCertFile),
// Il modulo tls di Node.js verifica automaticamente il nome del server (checkServerIdentity)
// Se il CA file è corretto e il certificato del server valido, la verifica andrà a buon fine.
// Non disabilitare la verifica del nome (checkServerIdentity) a meno che tu non conosca i rischi.
// checkServerIdentity: () => { return null; } // <-- Sconsigliato: disabilita controlli indispensabili!
};
const socket = tls.connect(options, () => {
console.log('Connessione SSL stabilita');
// Per HTTPS occorrerebbe inviare una richiesta HTTP; qui solo esempio per stabilire la connessione
// socket.write('GET / HTTP/1.1\r\nHost: ' + server + '\r\n\r\n');
});
socket.on('data', (data) => {
console.log(data.toString());
});
socket.on('close', () => {
console.log('Connessione chiusa');
});
socket.on('error', (error) => {
console.error('Errore:', error.message); // Stampa dell’errore
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Nota: Nel codice Node.js è stata rimossa la linea checkServerIdentity: () => { return null; }
che disabilita la verifica del nome host del server, pratica sconsigliata per motivi di sicurezza. L’errore OpenSSL unable to get local issuer certificate
riguarda il trust delle CA root, non la verifica dell’host (verify_peer_name
). Se il file CA è impostato correttamente e il certificato del server è valido, Node.js effettua la verifica dell’host in automatico. Errori in questa verifica sono di solito legati a problemi nel certificato stesso, non nella configurazione del trust store CA.
Esempio 5: Uso di OpenSSL con il comando curl
Anche il comando curl
fa uso di OpenSSL (o altri SSL library) per le richieste HTTPS. Puoi indicare il file CA bundle con l’opzione --cacert
:
bash
# Usa il file CA fornito da ServBay per collegarti a un sito HTTPS
# Scegli il percorso giusto in base alla versione ServBay
# Per Apple Silicon:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
# Per Intel:
# curl --cacert /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem https://example.com
1
2
3
4
5
6
7
2
3
4
5
6
7
Se il percorso CA è corretto e il certificato del server è valido, curl
completerà la richiesta senza errori di verifica.
Conclusione
L’errore 20:unable to get local issuer certificate
è molto comune quando si lavora con connessioni SSL/TLS tramite OpenSSL ed è dovuto all’assenza di una configurazione esplicita del trust store CA. ServBay mette a disposizione il file cacert.pem
con le root CA più diffuse.
Per risolvere questo problema, imposta il corretto percorso del bundle CA fornito da ServBay in tutti i punti del tuo ambiente di sviluppo: in php.ini
, nei parametri delle connessioni SSL nei tuoi script e nei comandi come openssl
(-CAfile
) e curl
(--cacert
). Scegli sempre il percorso della tua versione ServBay (Apple Silicon o Intel). Una corretta configurazione del trust store CA ti garantirà connessioni SSL/TLS sicure tra il tuo ambiente di sviluppo locale e servizi esterni.