Penyelesaian Masalah OpenSSL: Atasi Ralat unable to get local issuer certificate
Apabila melakukan sambungan selamat menggunakan OpenSSL (seperti menjalankan permintaan rangkaian dalam persekitaran PHP, menjalankan perintah openssl
atau curl
, atau membina sambungan SSL/TLS dalam aplikasi Node.js/Python), anda mungkin akan menemui ralat 20:unable to get local issuer certificate
. Ini adalah isu lazim yang berkaitan dengan cara OpenSSL mengesahkan sijil pihak lawan.
Demi keselamatan, secara lalai OpenSSL memerlukan pengetahuan jelas tentang Pihak Berkuasa Pensijilan (CA) akar yang dipercayai untuk mengesahkan rantaian sijil. Sekiranya OpenSSL tidak menemui atau tidak diberikan 'anchor' kepercayaan ini, ia tidak dapat mengesahkan kesahihan sijil pelayan lalu melaporkan ralat ini.
Dokumen ini akan menghuraikan punca ralat ini serta menyediakan panduan khusus untuk menyelesaikannya dalam persekitaran ServBay, termasuk konfigurasi stor kepercayaan OpenSSL untuk PHP, Python, Node.js, serta perintah curl
.
Mesej Ralat 20:unable to get local issuer certificate
Penerangan Isu
Apabila OpenSSL cuba mengesahkan sijil SSL/TLS pelayan jauh, ia membina rantaian dari sijil pelayan ke CA akar yang dipercayai. Jika OpenSSL tidak dapat menjumpai mana-mana sijil perantara atau sijil akar dalam rantaian ini di komputer anda, atau jika tiada stor kepercayaan (CAFile atau CAPath) yang dikonfigurasikan dengan betul, proses pengesahan akan gagal dan ralat 20:unable to get local issuer certificate
dipaparkan.
Ringkasnya, OpenSSL tidak tahu CA mana yang patut dipercayai dan seterusnya tidak dapat mengesahkan identiti pelayan yang dihubungkan.
Versi OpenSSL dan Laluan Sijil CA dalam ServBay
Sebagai persekitaran pembangunan web tempatan bersepadu, ServBay menyediakan pakej OpenSSL bersama fail akar CA awam yang biasa digunakan. Versi OpenSSL yang digunakan bergantung kepada cip pada peranti anda:
- ServBay Apple Silicon (cip siri M): Menggunakan OpenSSL versi 3.2.1.
- ServBay Intel: Menggunakan OpenSSL versi 1.1.1u.
Fail sijil CA (cacert.pem
) dan direktori sijil (certs
) terletak di dalam direktori pemasangan pakej ServBay. Lokasi ini berbeza mengikut versi anda:
ini
# Fail gabungan semua sijil akar dipercayai
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Direktori fail sijil individu (kebiasaannya cacert.pem sudah memadai, tetapi ada aplikasi perlukan capath)
capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
2
3
4
ini
# Fail gabungan semua sijil akar dipercayai
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Direktori fail sijil individu
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
2
3
4
Kunci penyelesaian ralat unable to get local issuer certificate
ialah menetapkan laluan cafile
atau capath
yang disebut di atas apabila anda memerlukan OpenSSL, supaya ia tahu di mana mencari sijil CA yang dipercayai.
Contoh Penyelesaian
Di bawah ialah contoh bagaimana untuk memberikan stor kepercayaan CA OpenSSL dalam pelbagai alat dan bahasa.
Contoh 1: Ujian Sambungan Menggunakan Perintah openssl
Jika anda menggunakan openssl s_client
secara terus dan menerima ralat seperti berikut:
bash
openssl s_client -quiet -connect gmail.com:443
1
Anda mungkin akan melihat ralat seperti di bawah, 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
# ... maklumat sambungan lain ...
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Cara Penyelesaian:
Gunakan parameter -CAfile
untuk menetapkan laluan fail CA yang disediakan oleh ServBay:
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
Jika berjaya, anda tidak akan lagi melihat baris verify error:num=20
dan nilai verify return
adalah 1
:
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
# ... maklumat sambungan lain ...
1
2
3
4
5
6
7
2
3
4
5
6
7
Contoh 2: Menggunakan OpenSSL dalam PHP
Kebanyakan fungsi rangkaian PHP (seperti file_get_contents
ke URL HTTPS, membina sambungan SSL melalui stream_socket_client
, atau kelanjutan cURL) bergantung pada OpenSSL. Anda boleh menetapkan stor kepercayaan CA dalam php.ini
atau melalui kod menggunakan stream context.
Kaedah A: Edit php.ini
(Disyorkan)
Ini adalah solusi menyeluruh dan paling mudah. Edit fail php.ini
PHP anda (boleh diedit dari panel kawalan ServBay), cari seksyen [openssl]
dan tambah/ubah suai seperti berikut, berdasarkan cip anda:
ini
[openssl]
; Tentukan fail gabungan sijil CA dipercayai
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Tentukan direktori sijil CA (opsyenal, tapi digalakkan)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
5
2
3
4
5
ini
[openssl]
; Tentukan fail gabungan sijil CA dipercayai
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Tentukan direktori sijil CA (opsyenal, tapi digalakkan)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
5
2
3
4
5
Setelah disimpan, sila mulakan semula perkhidmatan PHP dalam ServBay (atau restart ServBay).
Kaedah B: Tetapan Kod (Hanya Sesi Ini)
Jika tidak mahu ubah php.ini
, anda boleh tentukan pilihan cafile
dalam stream_context_create
:
php
<?php
// Contoh: Sambungan ke pelayan SMTP menggunakan SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Pilih laluan CA mengikut versi 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 pengesahan sijil pihak lawan
'verify_peer_name' => true, // Semak host dalam sijil sama dengan host sambungan
'allow_self_signed' => false, // Tidak benarkan sijil self-signed
'cafile' => $caCertFile, // Setkan fail CA
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Pilihan
],
];
$context = stream_context_create($contextOptions);
// Bina sambungan SSL/TLS
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // Timeout
STREAM_CLIENT_CONNECT,
$context // Pilihan konteks
);
if ($connection) {
echo "Connection established to $server:$port\n";
// Contoh: Hantar arahan EHLO
fwrite($connection, "EHLO servbay.demo\r\n"); // Guna nama domain contoh 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
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
Contoh 3: Menggunakan OpenSSL dalam Python (modul ssl)
Modul ssl
Python membolehkan anda mencipta konteks SSL/TLS dengan spesifikasi fail CA.
python
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Pilih laluan fail CA berdasarkan versi 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'
# Cipta konteks SSL lalai dan setkan fail CA
context = ssl.create_default_context(cafile=ca_cert_file)
# Atau guna capath: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Bina sambungan socket biasa
with socket.create_connection((server, port)) as sock:
# Bungkus socket biasa sebagai socket SSL
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"SSL connection established. Negotiated Protocol: {ssock.version()}")
# Contoh: Hantar arahan EHLO
ssock.sendall(b"EHLO servbay.demo\r\n") # Guna nama domain contoh ServBay
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}")
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
Contoh 4: Menggunakan OpenSSL dalam Node.js (modul tls)
Modul tls
dalam Node.js digunakan untuk sambungan TLS/SSL. Tetapkan CA dipercayai melalui atribut ca
dalam pilihan sambungan, dengan membaca kandungan fail cacert.pem
dari ServBay.
javascript
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Guna laman web contoh yang dipercayai
const port = 443;
// Pilih laluan CA berdasarkan versi 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,
// Baca kandungan fail CA
ca: fs.readFileSync(caCertFile),
// Node.js secara lalai mengesahkan nama host
// Jangan matikan checkServerIdentity kecuali anda benar-benar faham risikonya
// checkServerIdentity: () => { return null; } // <-- Elakkan mematikan semakan ini!
};
const socket = tls.connect(options, () => {
console.log('SSL connection established');
// Untuk sambungan HTTPS, biasanya perlu hantar permintaan HTTP, ini hanya contoh
// 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); // Papar ralat
});
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
Nota: Dalam contoh Node.js, baris checkServerIdentity: () => { return null; }
telah dibuang kerana ia mematikan semakan nama hos yang sangat penting untuk keselamatan. Ralat OpenSSL unable to get local issuer certificate
adalah berkaitan kepercayaan sijil CA, bukan semakan nama hos (verify_peer_name
). Sekiranya fail CA disediakan dengan betul dan sijil pelayan sah, semakan nama hos oleh Node.js seharusnya lulus.
Contoh 5: Menggunakan Perintah curl
dengan OpenSSL
Perintah curl
juga menggunakan OpenSSL (atau pustaka SSL lain) untuk permintaan HTTPS. Anda boleh menetapkan fail ca dengan --cacert
.
bash
# Gunakan fail CA dari ServBay untuk akses laman HTTPS
# Pilih laluan mengikut versi 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.com
1
2
3
4
5
6
7
2
3
4
5
6
7
Jika laluan fail CA betul dan sijil pelayan sah, curl
akan berjaya mengambil kandungan tanpa ralat sijil.
Rumusan
Ralat 20:unable to get local issuer certificate
adalah sangat biasa apabila menggunakan OpenSSL untuk sambungan SSL/TLS dan berpunca daripada OpenSSL memerlukan stor kepercayaan CA jelas bagi pengesahan sijil pelayan. ServBay menyediakan fail cacert.pem
yang lengkap dengan sijil CA akar yang popular.
Untuk penyelesaian, pastikan anda sentiasa menetapkan laluan fail cacert.pem
oleh ServBay dalam persekitaran pembangunan anda—sama ada dalam php.ini
, pilihan konteks kod, atau parameter perintah seperti -CAfile
(openssl
) atau --cacert
(curl
). Pilih laluan yang betul mengikut cip macOS anda (Apple Silicon atau Intel) dan versi OpenSSL ServBay. Dengan konfigurasi stor kepercayaan CA yang tepat, anda boleh memastikan persekitaran pembangunan tempatan anda dapat berkomunikasi dengan perkhidmatan SSL/TLS luaran secara selamat.