OpenSSL Probleemoplossing: Zo verhelp je de foutmelding unable to get local issuer certificate
Bij het opzetten van een veilige verbinding met OpenSSL (bijvoorbeeld tijdens HTTP-aanvragen in een PHP-omgeving, het uitvoeren van openssl of curl commando's, of het initiëren van SSL/TLS verbindingen in Node.js/Python applicaties), kun je als ontwikkelaar de foutmelding 20:unable to get local issuer certificate tegenkomen. Dit is een veelvoorkomend probleem dat te maken heeft met de manier waarop OpenSSL peer-certificaten controleert.
OpenSSL vereist uit veiligheidsoverwegingen altijd een expliciet ingestelde en betrouwbare root certificaatautoriteit (CA) bij het verifiëren van de certificaatketen. Als OpenSSL geen geldige CA's kan vinden, of als ze niet zijn ingesteld als vertrouwde ankers, mislukt de verificatie en verschijnt deze fout.
In deze handleiding leggen we uit wat de oorzaak is van deze fout en bieden we concrete oplossingen binnen de ServBay ontwikkelomgeving. Dit omvat het instellen van OpenSSL's trust store voor PHP, Python, Node.js en het gebruik van het curl commando.
Foutmelding 20:unable to get local issuer certificate
Probleemomschrijving
Bij het verifiëren van een remote SSL/TLS-certificaat probeert OpenSSL een keten op te bouwen die loopt van het servercertificaat naar een vertrouwde root CA. Als de benodigde intermediate of root CA-certificaten lokaal niet gevonden kunnen worden, of als een trust store (CAFile of CAPath) niet is geconfigureerd, zal de verificatie mislukken en verschijnt de fout 20:unable to get local issuer certificate.
Kort gezegd: OpenSSL kan niet vaststellen welke certificaatautoriteiten het moet vertrouwen, en kan dus de identiteit van de server niet bevestigen.
OpenSSL-versie en CA-certificaten in ServBay
ServBay is een geïntegreerde lokale webontwikkelomgeving, voorzien van het OpenSSL-pakket en gangbare openbare root CA-certificaten zodat je snel en veilig aan de slag kunt. De gebruikte OpenSSL-versie in ServBay is afhankelijk van het type processor op je apparaat:
- Apple Silicon (M-serie): OpenSSL versie 3.2.1.
- Intel-processors: OpenSSL versie 1.1.1u.
De bijbehorende CA-certificaatbestanden (cacert.pem) en certificaatmappen (certs) zijn te vinden in het installatiepad van ServBay. Dit pad verschilt per platform en architectuur:
ini
# Bundelbestand met alle vertrouwde rootcertificaten
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Directory met individuele certificaatbestanden (meestal is cacert.pem voldoende, maar soms is capath nodig)
capath=/Applications/ServBay/package/common/openssl/3.2/certs1
2
3
4
2
3
4
ini
# Bundelbestand met alle vertrouwde rootcertificaten
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Directory met individuele certificaatbestanden
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs1
2
3
4
2
3
4
ini
# Bundelbestand met alle vertrouwde rootcertificaten
cafile=C:\ServBay\package\common\openssl\3.3\cacert.pem
# Directory met individuele certificaatbestanden
capath=C:\ServBay\package\common\openssl\3.3\certs1
2
3
4
2
3
4
De kern van het oplossen van de fout unable to get local issuer certificate is dat je OpenSSL duidelijk maakt via welk pad en CA-bestand het de vertrouwde certficaten kan vinden.
Voorbeeldoplossingen
Hier zie je hoe je in verschillende tools en programmeertalen de OpenSSL CA trust store instelt.
Voorbeeld 1: Verbinding testen met het openssl commando
Als je met het volgende commando verbinding maakt en een foutmelding krijgt:
bash
openssl s_client -quiet -connect gmail.com:4431
Zul je mogelijk een vergelijkbare fout zien, met 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
# ... overige connectiegegevens ...1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Oplossing:
Geef het pad van ServBay’s CA-bundelbestand expliciet mee via de -CAfile parameter:
bash
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem1
bash
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem1
Als de verbinding en certificaatcontrole succesvol zijn, verschijnt geen enkele regel met de fout en is verify return:1 te zien:
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
# ... overige connectiegegevens ...1
2
3
4
5
6
7
2
3
4
5
6
7
Voorbeeld 2: OpenSSL in PHP gebruiken
Veel netwerkaanvragen in PHP (zoals file_get_contents naar een HTTPS-URL, stream_socket_client voor SSL-verbindingen, cURL-extensie enz.) zijn afhankelijk van OpenSSL. Je stelt het CA-bestand in via php.ini of in je code.
Methode A: php.ini aanpassen (aanbevolen)
Dit is de makkelijkste globale aanpak. Open het bij je PHP-versie passende php.ini bestand (je vindt dit via het ServBay dashboard), zoek de [openssl] sectie en voeg hieronder genoemde regels toe. Kies, afhankelijk van je processor, het juiste pad.
ini
[openssl]
; Standaard CA-certificatenbundel
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Directory met CA-certificaten (optioneel, maar aanbevolen)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs1
2
3
4
5
2
3
4
5
ini
[openssl]
; Standaard CA-certificatenbundel
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Directory met CA-certificaten (optioneel, maar aanbevolen)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs1
2
3
4
5
2
3
4
5
Herstart daarna de PHP-service (of ServBay volledig) zodat de wijzigingen ingaan.
Methode B: Instellen in je code (geldt enkel voor deze verbinding)
Wil je php.ini niet aanpassen, dan kun je bij stream_context_create de optie cafile meegeven in de context.
php
<?php
// Voorbeeld: Verbinding maken met een SMTP-server via SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Kies het juiste CA-bestand voor jouw ServBay versie
// Voor Apple Silicon:
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Voor Intel:
// $caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true, // Peer-certificaat controle activeren
'verify_peer_name' => true, // Controle van hostname in certificaat
'allow_self_signed' => false, // Geen zelfondertekende certificaten toestaan tenzij expliciet vertrouwd
'cafile' => $caCertFile, // CA-certificatenbundel opgeven
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Optioneel, CA-directory
],
];
$context = stream_context_create($contextOptions);
// Verbinding opzetten met contextopties
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // Timeout
STREAM_CLIENT_CONNECT,
$context
);
if ($connection) {
echo "Verbinding opgezet met $server:$port\n";
// Voorbeeld: EHLO-commando versturen
fwrite($connection, "EHLO servbay.demo\r\n");
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Kon geen verbinding maken met $server:$port. Fout: $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
Voorbeeld 3: OpenSSL gebruiken in Python (ssl-module)
Met Python’s ssl module kun je een SSL/TLS-context maken en daar je CA-certificaat aan meegeven.
python
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Kies het juiste CA-bestand voor jouw ServBay versie
# Voor Apple Silicon:
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
# Voor Intel:
# ca_cert_file = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem'
# Maak een standaard SSL-context met CA-bestand
context = ssl.create_default_context(cafile=ca_cert_file)
# Of stel de directory in: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Gewone socketverbinding maken
with socket.create_connection((server, port)) as sock:
# Socket omzetten naar SSL-socket
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"SSL-verbinding opgezet. Protocol: {ssock.version()}")
# Voorbeeld: EHLO-commando versturen
ssock.sendall(b"EHLO servbay.demo\r\n")
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
except Exception as e:
print(f"Kon geen verbinding maken of SSL-fout: {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
Voorbeeld 4: OpenSSL gebruiken in Node.js (tls-module)
Met Node.js’s tls module maak je TLS/SSL-verbindingen. Gebruik de ca optie in je connectie-instellingen, en geef de inhoud van het ServBay cacert.pem-bestand mee.
javascript
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Gebruik een betrouwbare website als voorbeeld
const port = 443;
// Kies het juiste CA-bestand voor jouw ServBay versie
// Voor Apple Silicon:
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Voor Intel:
// const caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
const options = {
host: server,
port: port,
// Inhoud van CA-certificaatbestand inlezen
ca: fs.readFileSync(caCertFile),
// Node.js tls doet standaard hostname-verificatie (checkServerIdentity).
// Als het CA-bestand klopt en het certificaat geldig is, zou de verificatie slagen.
// Alle verificaties ingeschakeld houden voor maximale veiligheid!
};
const socket = tls.connect(options, () => {
console.log('SSL-verbinding opgezet');
// Voor een HTTPS-verbinding zou je hier een HTTP-request sturen, nu alleen een demo
// 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('Verbinding gesloten');
});
socket.on('error', (error) => {
console.error('Fout:', 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
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
Let op: In het Node.js-voorbeeld is checkServerIdentity: () => { return null; } weggelaten. Deze optie zou de hostname-verificatie uitschakelen, wat onveilig is. De fout unable to get local issuer certificate heeft betrekking op de trust van het certificaat, niet op hostname-verificatie. Instellen van het juiste ca bestand, en correct geldige servercertificaten gebruiken, is voldoende. Hostname errors zijn meestal een probleem van het certificaat zelf.
Voorbeeld 5: De curl commandline tool gebruiken
Ook curl gebruikt standaard OpenSSL (of een andere SSL-bibliotheek) voor HTTPS-aanvragen. Stel via --cacert het bundelbestand met CA-certificaten van ServBay in.
bash
# Gebruik ServBay's CA-certificaatbestand om een HTTPS-site te benaderen
# Kies het juiste pad voor jouw ServBay versie
# Voor Apple Silicon:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
# Voor Intel:
# curl --cacert /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem https://example.com1
2
3
4
5
6
7
2
3
4
5
6
7
Als de CA goed staat ingesteld en het servercertificaat geldig is, zal curl geen certificaatfouten geven.
Samenvatting
De fout 20:unable to get local issuer certificate is gangbaar wanneer SSL/TLS-verbindingen worden gebouwd met OpenSSL, omdat het altijd een trust store nodig heeft om servercertificaten te verifiëren. ServBay biedt ontwikkelaars een compleet en up-to-date cacert.pem bundelbestand.
De oplossing is om overal waar OpenSSL gebruikt wordt (bijvoorbeeld via php.ini, via opties in je code, via commandline-opties als -CAfile in openssl of --cacert in curl) het pad van ServBay's cacert.pem op te geven. Let op welk pad hoort bij jouw macOS-chip (Apple Silicon of Intel) en welke ServBay OpenSSL-versie je gebruikt. Met een correct ingestelde trust store zorg je ervoor dat jouw lokale ontwikkelomgeving veilig kan communiceren met externe SSL/TLS-diensten.
