OpenSSL Fehlerbehebung: Lösung für den Fehler unable to get local issuer certificate
Beim Aufbau einer sicheren Verbindung mit OpenSSL (z.B. beim Ausführen von Netzwerkabfragen in PHP, dem Einsatz von openssl
oder curl
auf der Kommandozeile, sowie beim Verbindungsaufbau via SSL/TLS in Node.js/Python-Anwendungen) kann die Fehlermeldung 20:unable to get local issuer certificate
auftreten. Dieser bekannte Fehler hängt mit der Art und Weise zusammen, wie OpenSSL Zertifikate verifiziert und ist ein häufiges Relikt aus älteren Entwicklungen.
Aus Sicherheitsgründen benötigt OpenSSL für die Überprüfung einer Zertifikatskette stets Zugriff auf einen vertrauenswürdigen Zertifikatsanbieter (CA). Fehlt dieser oder ist er nicht eindeutig festgelegt, kann OpenSSL die Gültigkeit des Serverzertifikats nicht bestätigen und gibt diesen Fehler zurück.
In diesem Artikel erfahren Sie die Ursachen des Fehlers und erhalten praktische Lösungswege innerhalb der ServBay-Umgebung – etwa für die Konfiguration der CA-Vertrauenssignaturen in PHP, Python, Node.js und beim Einsatz des Befehls curl
.
Fehlermeldung 20:unable to get local issuer certificate
Fehlerbeschreibung
Versucht OpenSSL, das SSL/TLS-Zertifikat eines entfernten Servers zu prüfen, erstellt es dazu eine Zertifikatskette vom Serverzertifikat bis zur vertrauenswürdigen Root-CA. Findet OpenSSL lokal ein hierfür notwendiges Zwischen- oder Stammzertifikat nicht, bzw. ist der entsprechende Vertrauensspeicher (CAFile oder CAPath) nicht konfiguriert, scheitert der Verifizierungsprozess und liefert die Fehlermeldung 20:unable to get local issuer certificate
.
Anders gesagt: OpenSSL weiß nicht, welcher Herausgeber bzw. welche CA zu vertrauen ist und kann daher die Identität des Zielservers nicht bestätigen.
OpenSSL-Versionen und CA-Zertifikatspfad in ServBay
ServBay ist eine integrierte lokale Entwicklungsumgebung, die OpenSSL bereits mit einer Sammlung an gängigen Root-CA-Zertifikaten ausliefert. Je nach Chip-Typ Ihres macOS-Systems wird eine entsprechende OpenSSL-Version genutzt:
- Apple Silicon (M-Serie-Chip) ServBay: Setzt OpenSSL 3.2.1 ein.
- Intel-Chip ServBay: Nutzt OpenSSL 1.1.1u.
Die zugehörigen CA-Zertifikatdateien (cacert.pem
) und das Zertifikatsverzeichnis (certs
) finden sich unterhalb des Installationspfads Ihrer ServBay-Software. Je nach Plattform lautet der Pfad wie folgt:
ini
# Bundle-Datei mit allen vertrauenswürdigen Root-Zertifikaten
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Verzeichnis mit einzelnen Zertifikatsdateien (meist genügt cacert.pem, capath wird für manche Anwendungen benötigt)
capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
2
3
4
ini
# Bundle-Datei mit vertrauenswürdigen Root-Zertifikaten
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Verzeichnis mit einzelnen Zertifikatsdateien
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
2
3
4
ini
# Bundle-Datei mit vertrauenswürdigen Root-Zertifikaten
cafile=C:\ServBay\package\common\openssl\3.3\cacert.pem
# Verzeichnis mit einzelnen Zertifikatsdateien
capath=C:\ServBay\package\common\openssl\3.3\certs
1
2
3
4
2
3
4
Um den Fehler unable to get local issuer certificate
zu beheben, genügt es, OpenSSL explizit mitzuteilen, wo sich die oben genannten cafile
oder capath
befinden, sodass die Software die Root-CAs verwenden kann.
Praxisbeispiele für die Lösung
So legen Sie für unterschiedliche Tools und Programmierumgebungen den CA-Vertrauensspeicher fest.
Beispiel 1: Verbindungstest mit dem Befehl openssl
Führt der Befehl openssl s_client
zu einem Fehler wie:
bash
openssl s_client -quiet -connect gmail.com:443
1
Kann die Ausgabesequenz etwa so aussehen und enthält den Fehler 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
# ... weitere Verbindungsinformationen ...
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Lösung:
Geben Sie mit dem Argument -CAfile
den Pfad zur ServBay-Bundle-Datei für CA-Zertifikate explizit an:
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
Nach erfolgreicher Verbindung und Zertifikatsprüfung sehen Sie in der Ausgabe zwar weiterhin die Zeile verify return:1
, jedoch nicht mehr die Fehlerzeile 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
# ... weitere Verbindungsinformationen ...
1
2
3
4
5
6
7
2
3
4
5
6
7
Beispiel 2: OpenSSL-Einsatz in PHP
Zahlreiche PHP-Funktionen für Netzwerke (wie file_get_contents
mit HTTPS, stream_socket_client
für SSL-Verbindungen, das cURL-Module usw.) verwenden OpenSSL unter der Haube. Sie können den CA-Vertrauensspeicher global in der php.ini
oder spezifisch in Code-Kontexten deklarieren.
Variante A: Anpassung in php.ini
(empfohlen)
Die globale und einfachste Lösung: Bearbeiten Sie die passende php.ini
Ihrer PHP-Installation (der ServBay-Control-Panel zeigt Ihnen das zugehörige File) und ergänzen bzw. modifizieren Sie im Abschnitt [openssl]
die folgenden Zeilen. Achten Sie darauf, die richtige Pfadangabe für Ihren Chip zu wählen!
ini
[openssl]
; Trusted CA Zertifikatsbundle
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Verzeichnis mit CA-Zertifikaten (optional, aber empfohlen)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
5
2
3
4
5
ini
[openssl]
; Trusted CA Zertifikatsbundle
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Verzeichnis mit CA-Zertifikaten (optional, aber empfohlen)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
5
2
3
4
5
Nach dem Ändern der php.ini
sollten Sie den PHP-Service (oder gleich ServBay vollständig) neu starten, damit die Einstellungen wirksam werden.
Variante B: Optionen im Code setzen (betrifft nur einzelne Verbindungen)
Falls Sie keinen Einfluss auf die globale php.ini
nehmen wollen, bleibt die explizite Option im PHP-Code – etwa beim Aufbau eines SSL-Kontextes mit stream_context_create
:
php
<?php
// Beispiel: Verbindung zu einem SMTP-Server mit SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Passenden CA-Pfad wählen, je nach ServBay-Version
// Für Apple Silicon:
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Für Intel:
// $caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true, // Zertifikatsprüfung aktivieren
'verify_peer_name' => true, // Prüft, ob Hostname zum Zertifikat passt
'allow_self_signed' => false, // Keine selbstsignierten Zertifikate (außer Sie vertrauen diese explizit)
'cafile' => $caCertFile, // CA-Bundle angeben
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Optional: Verzeichnis
],
];
$context = stream_context_create($contextOptions);
// Aufbau einer SSL/TLS-Verbindung mit den Optionen
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30,
STREAM_CLIENT_CONNECT,
$context
);
if ($connection) {
echo "Verbindung zu $server:$port erfolgreich\n";
// Beispiel: Senden eines EHLO-Kommandos
fwrite($connection, "EHLO servbay.demo\r\n"); // beispielhafte Domain mit ServBay-Bezug
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Verbindung zu $server:$port fehlgeschlagen. Fehler: $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
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
Beispiel 3: Einsatz des OpenSSL-Moduls in Python (ssl-Modul)
Auch das ssl
-Modul von Python erlaubt Ihnen die explizite Definition des CA-Bundles oder Zertifikatverzeichnisses:
python
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Passenden CA-Pfad wählen, je nach ServBay-Version
# Für Apple Silicon:
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
# Für Intel:
# ca_cert_file = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem'
# SSL-Kontext inkl. CA-Bundle erstellen
context = ssl.create_default_context(cafile=ca_cert_file)
# Alternativ: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# klassische Socket-Verbindung
with socket.create_connection((server, port)) as sock:
# SSL-Wrap des klassischen Sockets
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"SSL-Verbindung etabliert. Protokoll: {ssock.version()}")
# Beispiel: EHLO-Kommando
ssock.sendall(b"EHLO servbay.demo\r\n") # Beispiel-Domain mit ServBay-Bezug
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
except Exception as e:
print(f"Verbindungs- oder SSL-Fehler: {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
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
Beispiel 4: OpenSSL-Konfiguration in Node.js (tls-Modul)
Das tls
-Modul von Node.js steuert den SSL/TLS-Verbindungsaufbau. Sie können via Option ca
ein CA-Bundle angeben, vorzugsweise als Inhalt der ServBay-cacert.pem
-Datei:
javascript
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Exemplarisch eine bekannte Website
const port = 443;
// Wählen Sie den richtigen CA-Bundle-Pfad je nach ServBay-Version
// Für Apple Silicon:
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Für Intel:
// const caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
const options = {
host: server,
port: port,
// CA-Zertifikat einlesen
ca: fs.readFileSync(caCertFile),
// Der Hostname-Check bleibt im Node.js tls-Modul aktiv,
// falls die CA korrekt angegeben und das Serverzertifikat gültig ist.
// Nur in Ausnahmefällen und unter Risiko ausschalten!
// checkServerIdentity: () => { return null; } // <-- Nicht empfohlen! Sicherheitseinbuße!
};
const socket = tls.connect(options, () => {
console.log('SSL-Verbindung hergestellt');
// Für HTTPS wäre hier ein HTTP-Request möglich (nur Verbindungsbeispiel)
// 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('Verbindung geschlossen');
});
socket.on('error', (error) => {
console.error('Fehler:', error.message);
});
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
Hinweis: Im Node.js-Beispiel bleibt der Hostname-Check (checkServerIdentity) aktiv – seine Deaktivierung birgt ein hohes Risiko. Der OpenSSL-Fehler unable to get local issuer certificate
bezieht sich allein auf den Vertrauensspeicher der CA, nicht auf die Hostnamensvalidierung. Korrekte Angabe der ca
-Option bei gültigem Serverzertifikat erlaubt die sichere Verbindung.
Beispiel 5: curl
mit OpenSSL und CA-Bundle
Auch das Kommandozeilentool curl
nutzt OpenSSL zur HTTPS-Kommunikation. Mittels --cacert
-Parameter können Sie das CA-Bundle angeben:
bash
# Mit ServBay-CA-Bundle eine HTTPS-Seite aufrufen
# Für Apple Silicon:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
# Für Intel:
# curl --cacert /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem https://example.com
1
2
3
4
5
6
2
3
4
5
6
Ist der Pfad korrekt gesetzt und das Serverzertifikat vertrauenswürdig, erhalten Sie die Daten ohne Zertifikatsfehler.
Fazit
Der Fehler 20:unable to get local issuer certificate
tritt bei SSL/TLS-Verbindungen mit OpenSSL oft auf, wenn kein Vertrauensspeicher hinterlegt ist. ServBay bringt dazu das CA-Bundle cacert.pem
, welches die wichtigsten öffentlichen Root-Zertifikate enthält.
Die Lösung besteht darin, für Ihre Entwicklungsumgebung – sei es in php.ini
, als Kontext-Option im Code, oder mit Parametern bei openssl
/curl
– stets den korrekten Pfad zur ServBay-cacert.pem
-Datei zu setzen. Orientieren Sie sich dabei an Ihrer macOS-Architektur (Apple Silicon/Intel) und der eingesetzten ServBay-OpenSSL-Version. Mit richtig konfiguriertem Vertrauensspeicher können Sie sicher und zuverlässig externen SSL/TLS-Diensten aus Ihrer lokalen Umgebung heraus verbinden.