OpenSSL Sorun Giderme: unable to get local issuer certificate
Hatasının Çözümü
OpenSSL kullanarak güvenli bağlantılar kurarken (örneğin, PHP'de ağ talepleri yaparken, openssl
veya curl
komutlarıyla çalışırken ya da Node.js/Python uygulamalarında SSL/TLS bağlantısı oluştururken), geliştiriciler sıklıkla 20:unable to get local issuer certificate
şeklinde bir hata mesajı alabilirler. Bu hata, OpenSSL’in eş sertifikasını doğrulama biçimiyle ilgili geçmişten gelen yaygın bir sorundur.
Güvenlik nedeniyle, OpenSSL varsayılan olarak bir sertifika zincirini doğrularken hangi kök sertifika otoritelerine (CA) güveneceğini bilmek ister. Güvenilen bu köklerin veya zincir üzerindeki herhangi bir ara sertifikanın nerede olduğunu bulamazsa ya da tanımlı bir güven deposu (CAFile veya CAPath) yoksa, sunucu sertifikasının geçerliliğini doğrulayamaz ve bu hatayı bildirir.
Bu dökümanda, hatanın nedenini açıklayacak ve ServBay ortamında bu sorunu çözmek için PHP, Python, Node.js ve curl
komutu da dahil olmak üzere OpenSSL güven deposunun nasıl yapılandırılacağını göstereceğiz.
20:unable to get local issuer certificate
Hatası
Problem Tanımı
OpenSSL uzak bir sunucunun SSL/TLS sertifikasını doğrulamaya çalıştığında, sunucu sertifikasından güvenilen kök CA'ya kadar bir sertifika zinciri oluşturur. Eğer yerelde bu zincirdeki ara sertifikalar ya da kök sertifika bulunamazsa veya uygun şekilde yapılandırılmış bir güven deposu (CAFile veya CAPath) yoksa, doğrulama işlemi başarısız olur ve 20:unable to get local issuer certificate
hatası döner.
Kısaca özetleyecek olursak: OpenSSL hangi sertifika otoritesine güveneceğini bilmemektedir ve bu nedenle önünde olan sunucuya güvenememektedir.
ServBay’deki OpenSSL Sürümü ve CA Sertifika Yolları
ServBay, entegre yerel web geliştirme ortamı olarak, OpenSSL paketini ve yaygın kök CA sertifikalarını önceden içerir; böylece geliştiriciler için süreci kolaylaştırır. ServBay’in kullandığı OpenSSL sürümü cihazınızın işlemci türüne göre değişir:
- Apple Silicon (M Serisi) için ServBay: OpenSSL 3.2.1 sürümünü kullanır.
- Intel işlemcili ServBay: OpenSSL 1.1.1u sürümünü kullanır.
Bu OpenSSL sürümlerinin ilgili CA sertifika dosyası (cacert.pem
) ve sertifika dizini (certs
), ServBay yazılım paketinin kurulu olduğu dizinde yer alır. Kendi ServBay sürümünüze uygun yolu bulmalısınız:
# Güvenen tüm kök sertifikaların bulunduğu toplu dosya
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Bireysel sertifika dosyalarının olduğu dizin (Genellikle sadece cacert.pem yeterlidir, ancak bazı uygulamalar capath isteyebilir)
capath=/Applications/ServBay/package/common/openssl/3.2/certs
2
3
4
# Güvenen tüm kök sertifikaların bulunduğu toplu dosya
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Bireysel sertifika dosyalarının olduğu dizin
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
2
3
4
unable to get local issuer certificate
hatasının esas çözümü; OpenSSL’in ihtiyaç duyduğu yerde yukarıdaki cafile
veya capath
yolunu açıkça belirtmektir ve böylece OpenSSL’in güvenilir CA sertifikalarını nerede bulacağını göstermiş olursunuz.
Çözüm Örnekleri
Aşağıda, farklı araç ve programlama dillerinde OpenSSL’in CA güven deposunu nasıl belirtebileceğiniz gösterilmiştir.
Örnek 1: Bağlantı Testi İçin openssl
Komutu Kullanmak
Eğer doğrudan openssl s_client
komutuyla bir bağlantı test ettiğinizde hata alıyorsanız:
openssl s_client -quiet -connect gmail.com:443
Aşağıdakine benzer bir hata mesajı görebilirsiniz; içinde verify error:num=20:unable to get local issuer certificate
satırı yer alır:
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
# ... diğer bağlantı bilgileri ...
2
3
4
5
6
7
8
Çözüm:
ServBay’in CA sertifika toplu dosyasını -CAfile
parametresiyle belirtin:
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
Doğrulama başarılı olduğunda, çıktıda verify return
değeri 1
olur ve artık verify error:num=20
satırı yer almaz:
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
# ... diğer bağlantı bilgileri ...
2
3
4
5
6
7
Örnek 2: PHP’de OpenSSL Kullanımı
PHP’deki çoğu ağ fonksiyonu (ör. file_get_contents
ile HTTPS URL’lerine erişim, stream_socket_client
ile SSL bağlantısı kurmak, cURL uzantısı vs.) altyapı olarak OpenSSL’e dayanır. CA güven deposunu belirtmenin yolları şunlardır:
Yöntem A: php.ini
Dosyasını Düzenlemek (Tavsiye Edilir)
En pratik ve global çözümdür. Kullandığınız PHP sürümüne ait php.ini
dosyasını (ServBay kontrol panelinden düzenleme imkanınız vardır) açın, [openssl]
bölümünü bulun ve aşağıdaki satırları ekleyin veya güncelleyin. İşlemci tipinize göre uygun yol seçin.
[openssl]
; Güvenilir CA sertifikası toplu dosyasını belirtin
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Bireysel CA sertifikası içeren dizini belirtin (opsiyonel, ama önerilir)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs
2
3
4
5
[openssl]
; Güvenilir CA sertifikası toplu dosyasını belirtin
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Bireysel CA sertifikası içeren dizini belirtin (opsiyonel, ama önerilir)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
2
3
4
5
php.ini
dosyasını kaydettikten sonra ServBay içinde PHP servisini (veya komple ServBay'i) yeniden başlatmalısınız, böylece ayar etkin olur.
Yöntem B: Kod İçinde Ayar Yapmak (Sadece O Anki Bağlantıyı Etkiler)
Global php.ini
dosyasını değiştirmek istemiyorsanız, stream_context_create
ile SSL/TLS bağlantı context’i hazırlarken ssl
seçeneğiyle cafile
belirtilebilir.
<?php
// Örnek: Bir SSL/TLS SMTP sunucusuna bağlanma
$server = 'ssl0.ovh.net';
$port = 465;
// ServBay sürümünüze göre doğru CA dosya yolunu seçin
// Apple Silicon için:
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Intel için:
// $caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true, // Karşı sertifika doğrulaması açık
'verify_peer_name' => true, // Sertifikadaki hostname doğrulaması
'allow_self_signed' => false, // Kendi imzalı sertifikalara izin yok (ancak özellikle güveniyorsanız açabilirsiniz)
'cafile' => $caCertFile, // CA toplu dosya yolu
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // (Opsiyonel) CA dizini
],
];
$context = stream_context_create($contextOptions);
// Hazırlanan context ile SSL/TLS bağlantısı kur
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // Zaman aşımı
STREAM_CLIENT_CONNECT,
$context // Context seçenekleri
);
if ($connection) {
echo "Connection established to $server:$port\n";
// Örnek: EHLO komutu gönderiliyor
fwrite($connection, "EHLO servbay.demo\r\n"); // ServBay markasına ait örnek alan adı
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Failed to connect to $server:$port. Error: $errstr ($errno)\n";
}
?>
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
Örnek 3: Python’da OpenSSL (ssl modülü) Kullanmak
Python’un ssl
modülü sayesinde SSL/TLS context’i oluştururken güvenilen CA sertifikasını belirtebilirsiniz.
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# ServBay sürümünüze göre doğru CA dosya yolu
# Apple Silicon için:
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
# Intel için:
# ca_cert_file = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem'
# Varsayılan bir SSL context’i oluşturulup CA dosyası atanıyor
context = ssl.create_default_context(cafile=ca_cert_file)
# Dizin ile de belirtebilirsiniz: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Standart bir socket bağlantısı kuruluyor
with socket.create_connection((server, port)) as sock:
# Socket, SSL ile sarılıyor
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"SSL connection established. Negotiated Protocol: {ssock.version()}")
# Örnek: EHLO komutu
ssock.sendall(b"EHLO servbay.demo\r\n") # ServBay markasına ait örnek alan adı
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
except Exception as e:
print(f"Failed to connect or SSL error: {e}")
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
Örnek 4: Node.js’de OpenSSL (tls modülü) Kullanmak
Node.js’deki tls
modülüyle TLS/SSL bağlantısı kurulabilir. Bağlantı seçeneklerinde ca
özelliğiyle güvenilen CA sertifikası belirtilebilir. ca
özelliği, bir veya daha fazla sertifikayı içeren string veya Buffer dizisi olabilir. ServBay’in cacert.pem
dosyasını okumak en kolay yoldur.
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Örnek olarak genel, güvenilir bir web sitesi
const port = 443;
// ServBay sürümüne göre doğru CA dosya yolu
// Apple Silicon için:
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Intel için:
// const caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
const options = {
host: server,
port: port,
// CA sertifikası dosyasını oku
ca: fs.readFileSync(caCertFile),
// Node.js tls modülü, varsayılan olarak hostname doğrulaması (checkServerIdentity) yapar.
// Eğer CA dosyanız ve sunucu sertifikası geçerli ise, işlemler başarılı olacaktır.
// checkServerIdentity opsiyonu gerekmedikçe devre dışı bırakılmamalı!
// checkServerIdentity: () => { return null; } // <-- Bu satırı kullanmayın, güvenliği ciddi şekilde tehlikeye atar!
};
const socket = tls.connect(options, () => {
console.log('SSL connection established');
// HTTPS trafiği için HTTP isteği göndermek gerekir, burada sadece bağlantı örneği gösterilmiştir
// 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('Connection closed');
});
socket.on('error', (error) => {
console.error('Error:', error.message); // Hata mesajı yazdırılıyor
});
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
Not: Node.js örneğinde, checkServerIdentity: () => { return null; }
seçeneği çıkarıldı. Bu, sertifika üzerindeki hostname doğrulamasını devre dışı bırakır ve güvenlik açısından son derece sakıncalıdır. OpenSSL’deki unable to get local issuer certificate
hatası, kök CA güveniyle alakalıdır; hostname doğrulamasıyla (verify_peer_name) karıştırılmamalıdır. ca
seçeneğini doğru belirleyin, geçerli bir sunucu sertifikası kullanın ve Node.js’in varsayılan hostname doğrulama davranışını değiştirmeyin. Eğer hostname doğrulaması ile ilgili hatalar alırsanız bu genellikle sertifika kalitesiyle alakalıdır, CA deposu ile ilgili değil.
Örnek 5: curl
Komutunda OpenSSL Kullanmak
curl
komutu da HTTPS bağlantıları için sıklıkla OpenSSL (veya başka SSL kütüphanesi) kullanır. CA sertifika toplu dosyasını --cacert
ile belirtebilirsiniz.
# ServBay’in CA sertifika dosyası ile bir HTTPS sitesine erişim
# ServBay sürümüne göre uygun yol seçilmeli
# Apple Silicon için:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
# Intel için:
# curl --cacert /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem https://example.com
2
3
4
5
6
7
CA yolu doğru ve sunucu sertifikası geçerli ise, curl
komutu içeriği başarıyla getirir ve sertifika doğrulama hatası vermez.
Özet
OpenSSL ile SSL/TLS bağlantısı kurarken görülen 20:unable to get local issuer certificate
hatası çok yaygındır ve temel sebebi, OpenSSL’in sunucu sertifikasını doğrulamak için açıkça bir güven deposu (CA sertifika dosyası) istemesidir. ServBay, geliştiriciler için yaygın kök CA sertifikalarının bulunduğu hazır bir cacert.pem
dosyası sağlar.
Çözüm ise, geliştirme ortamınızda (php.ini
dosyasında, kod içinde SSL context ayarlarında, openssl
komut satırı için -CAfile
parametresiyle veya curl
için --cacert
ile) uygun ServBay cacert.pem
dosya yolunu belirtmektir. Lütfen macOS makinenizin işlemci tipine (Apple Silicon veya Intel) ve ServBay OpenSSL sürümüne uygun yolu seçin. Doğru CA güven deposunu ayarlayarak yerel geliştirme ortamınızın dışarıdaki SSL/TLS servisleriyle güvenli şekilde iletişim kurmasını sağlayabilirsiniz.