Pemecahan Masalah OpenSSL: Mengatasi Kesalahan unable to get local issuer certificate
Saat menggunakan OpenSSL untuk koneksi aman—misalnya saat melakukan permintaan jaringan di lingkungan PHP, menjalankan perintah openssl atau curl, atau membangun koneksi SSL/TLS pada aplikasi Node.js/Python—developer kadang menghadapi pesan error 20:unable to get local issuer certificate. Ini adalah masalah yang umum dan merupakan warisan sejarah dari cara OpenSSL melakukan verifikasi sertifikat rekanan.
Karena alasan keamanan, secara default OpenSSL memerlukan daftar otoritas sertifikat (CA) terpercaya untuk memverifikasi rantai sertifikat. Tanpa keberadaan atau spesifikasi anchor kepercayaan ini, OpenSSL gagal memverifikasi validitas sertifikat server dan akan menampilkan kesalahan ini.
Artikel ini menjelaskan secara detail penyebab error ini, serta memberikan solusi spesifik di lingkungan ServBay, termasuk cara mengatur trust store OpenSSL untuk PHP, Python, Node.js, dan perintah curl.
Pesan Error 20:unable to get local issuer certificate
Deskripsi Masalah
Ketika OpenSSL mencoba memverifikasi sertifikat SSL/TLS dari server remote, ia akan membangun rantai sertifikat dari sertifikat server ke root CA terpercaya. Jika OpenSSL tidak menemukan sertifikat intermediate atau root CA pada penyimpanan lokal, atau tidak bisa menemukan trust store yang dikonfigurasi (CAFile atau CAPath), proses verifikasi gagal dan muncul error 20:unable to get local issuer certificate.
Secara sederhana, ini terjadi karena OpenSSL tidak tahu CA mana yang boleh dipercaya, sehingga tidak mampu memastikan identitas server yang terkoneksi.
Versi OpenSSL dan Path Sertifikat CA di ServBay
ServBay adalah lingkungan pengembangan web lokal terintegrasi yang sudah memasang paket OpenSSL dengan root CA publik yang umum digunakan. Versi OpenSSL yang digunakan ServBay tergantung pada jenis chip perangkat Anda:
- ServBay Apple Silicon (chip seri M): menggunakan OpenSSL versi 3.2.1
- ServBay Intel: menggunakan OpenSSL versi 1.1.1u
File sertifikat CA yang terkait (cacert.pem) serta direktori sertifikat (certs) terdapat di path instalasi paket ServBay. Sesuaikan dengan platform dan arsitektur Anda:
ini
# Bundel berisi semua root CA terpercaya
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Direktori berisi file sertifikat independen (umumnya cacert.pem sudah cukup, tapi beberapa aplikasi perlu capath)
capath=/Applications/ServBay/package/common/openssl/3.2/certs1
2
3
4
2
3
4
ini
# Bundel berisi semua root CA terpercaya
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Direktori berisi file sertifikat independen
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs1
2
3
4
2
3
4
ini
# Bundel berisi semua root CA terpercaya
cafile=C:\ServBay\package\common\openssl\3.3\cacert.pem
# Direktori berisi file sertifikat independen
capath=C:\ServBay\package\common\openssl\3.3\certs1
2
3
4
2
3
4
Kunci untuk mengatasi error unable to get local issuer certificate adalah memberikan path cafile atau capath di semua tool yang menggunakan OpenSSL, sehingga OpenSSL tahu di mana mencari sertifikat CA terpercaya.
Contoh Solusi
Berikut contoh cara menentukan trust store CA OpenSSL pada berbagai tool dan bahasa pemrograman.
Contoh 1: Tes Koneksi menggunakan perintah openssl
Jika Anda menjalankan perintah openssl s_client dan menemui error seperti:
bash
openssl s_client -quiet -connect gmail.com:4431
Anda mungkin melihat output error seperti berikut, termasuk 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
# ... Informasi koneksi lainnya ...1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Solusi:
Gunakan argumen -CAfile untuk menentukan path file CA bundle dari ServBay:
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
Jika koneksi dan verifikasi berhasil, tidak ada verify error:num=20 dalam output:
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
# ... Informasi koneksi lainnya ...1
2
3
4
5
6
7
2
3
4
5
6
7
Contoh 2: Penggunaan OpenSSL dalam PHP
Banyak fitur jaringan PHP—seperti file_get_contents ke URL HTTPS, koneksi stream_socket_client SSL, dan extensi cURL—bergantung pada OpenSSL. Anda dapat menetapkan trust store CA dengan mengedit php.ini konfigurasi global atau melalui opsi stream context dalam kode.
Metode A: Edit php.ini (disarankan)
Cara paling praktis adalah mengedit file php.ini untuk versi PHP yang Anda gunakan (editor bisa dibuka dari panel ServBay). Pada bagian [openssl], tambahkan/ubah konfigurasi berikut sesuai dengan chip perangkat Anda:
ini
[openssl]
; File bundle CA terpercaya
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Direktori CA (opsional, tapi disarankan)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs1
2
3
4
5
2
3
4
5
ini
[openssl]
; File bundle CA terpercaya
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Direktori CA (opsional, tapi disarankan)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs1
2
3
4
5
2
3
4
5
Setelah mengedit php.ini, restart layanan PHP ServBay agar konfigurasi berlaku.
Metode B: Pengaturan context di kode (hanya untuk koneksi saat itu)
Jika tidak ingin mengubah php.ini, Anda dapat menetapkan file CA pada opsi SSL context saat membuat koneksi:
php
<?php
// Contoh: koneksi ke server SMTP yang menggunakan SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Pilih path sesuai perangkat ServBay Anda
// Untuk Apple Silicon:
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Untuk Intel:
// $caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true, // Aktifkan verifikasi sertifikat rekan
'verify_peer_name' => true, // Verifikasi kesesuaian hostname dengan sertifikat
'allow_self_signed' => false, // Tolak sertifikat self-signed kecuali sudah dipercaya secara eksplisit
'cafile' => $caCertFile, // Tentukan file CA bundle
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Opsional: direktori CA
],
];
$context = stream_context_create($contextOptions);
// Koneksi SSL/TLS menggunakan context
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // Timeout koneksi
STREAM_CLIENT_CONNECT,
$context // Opsi context
);
if ($connection) {
echo "Connection established to $server:$port\n";
// Contoh: kirim perintah EHLO
fwrite($connection, "EHLO servbay.demo\r\n"); // gunakan domain demo terkait ServBay
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Failed to connect to $server:$port. Error: $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
Contoh 3: Pemakaian OpenSSL di Python (modul ssl)
Modul ssl di Python juga memungkinkan pembuatan SSL context dengan CA terpercaya:
python
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Pilih path CA sesuai ServBay Anda
# Untuk Apple Silicon:
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
# Untuk Intel:
# ca_cert_file = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem'
# Buat SSL context default dan tentukan file CA
context = ssl.create_default_context(cafile=ca_cert_file)
# Atau: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Buat socket reguler
with socket.create_connection((server, port)) as sock:
# Bungkus socket reguler ke socket SSL
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"Koneksi SSL berhasil. Protokol: {ssock.version()}")
# Contoh: kirim perintah EHLO
ssock.sendall(b"EHLO servbay.demo\r\n") # gunakan domain demo ServBay
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
except Exception as e:
print(f"Gagal koneksi atau error SSL: {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
Contoh 4: Penggunaan OpenSSL di Node.js (modul tls)
Modul tls di Node.js digunakan untuk koneksi SSL/TLS. Anda bisa gunakan atribut ca pada opsi koneksi, dengan isi file cacert.pem dari ServBay.
javascript
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Contoh dengan situs yang terpercaya
const port = 443;
// Pilih path CA sesuai ServBay Anda
// Untuk Apple Silicon:
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Untuk Intel:
// const caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
const options = {
host: server,
port: port,
// Ambil isi file CA
ca: fs.readFileSync(caCertFile),
// Modul tls default melakukan verifikasi hostname
// Selama CA benar dan sertifikat server valid, verifikasi ini akan lolos.
// Jangan matikan checkServerIdentity kecuali paham risikonya!
// checkServerIdentity: () => { return null; } // <-- Jangan gunakan kecuali Anda tahu apa risikonya!
};
const socket = tls.connect(options, () => {
console.log('Koneksi SSL berhasil');
// Untuk koneksi HTTPS umumnya perlu request HTTP, ini hanya contoh koneksi
// 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('Koneksi ditutup');
});
socket.on('error', (error) => {
console.error('Error:', error.message); // Tampilkan pesan error
});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
Catatan: Pada contoh Node.js, opsi checkServerIdentity: () => { return null; } dihapus. Menonaktifkan verifikasi hostname sangat tidak disarankan karena berisiko keamanan tinggi. Error unable to get local issuer certificate terkait dengan root CA, bukan verifikasi hostname (verify_peer_name). Jika CA sudah benar dan sertifikat server valid, verifikasi nama host oleh Node.js akan berhasil. Jika gagal, biasanya itu masalah pada sertifikat, bukan trust store CA.
Contoh 5: Menggunakan OpenSSL di perintah curl
Perintah curl dengan HTTPS juga menggunakan OpenSSL (atau SSL library lain). Gunakan parameter --cacert untuk menentukan file CA bundle.
bash
# Mengakses situs HTTPS menggunakan file CA dari ServBay
# Pilih path sesuai ServBay Anda
# Untuk Apple Silicon:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
# Untuk 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
Jika path CA sudah benar dan sertifikat server valid, curl berhasil mengakses tanpa error verifikasi sertifikat.
Kesimpulan
Error 20:unable to get local issuer certificate sangat umum terjadi dalam koneksi SSL/TLS menggunakan OpenSSL, dan penyebab utama adalah OpenSSL harus tahu dengan jelas dimana trust store yang berisi CA terpercaya. ServBay telah menyediakan file cacert.pem yang berisi root CA publik.
Solusi utama, pastikan path cacert.pem dari ServBay ditentukan dengan benar—baik pada konfigurasi global (misal php.ini), opsi SSL context dalam kode, maupun parameter perintah seperti -CAfile pada openssl atau --cacert pada curl. Selalu sesuaikan path berdasarkan jenis chip macOS Anda (Apple Silicon atau Intel) dan versi OpenSSL pada ServBay. Dengan konfigurasi trust store yang tepat, Anda dapat memastikan lingkungan pengembangan lokal Anda dapat berkomunikasi dengan aman dengan server SSL/TLS eksternal.
