Khắc phục sự cố OpenSSL
Khi sử dụng OpenSSL (bao gồm trong môi trường PHP, lệnh openssl
, lệnh curl
), bạn có thể gặp phải lỗi 20:unable to get local issuer certificate
. Đây là một vấn đề thường gặp do lịch sử của OpenSSL. Vì lý do bảo mật, OpenSSL không tin tưởng bất kỳ CA nào khi được gọi trực tiếp, cần phải chỉ định CAFile hoặc CAPath. Bài viết này sẽ hướng dẫn cách giải quyết vấn đề này và cung cấp các ví dụ mã liên quan cho PHP, Python và Node.js.
Lỗi 20:unable to get local issuer certificate
Mô tả vấn đề
Nếu gặp phải lỗi 20:unable to get local issuer certificate
khi sử dụng OpenSSL, điều này là do OpenSSL không tìm thấy chứng chỉ của cơ quan phát hành. Cách giải quyết vấn đề này là chỉ định CAFile hoặc CAPath.
Phiên bản OpenSSL trong ServBay
Phiên bản OpenSSL trong ServBay khác nhau tùy thuộc vào loại chip mà ServBay sử dụng:
- Phiên bản ServBay cho chip Intel: Sử dụng OpenSSL phiên bản 1.1.1u
- Phiên bản ServBay cho chip Apple Silicon: Sử dụng OpenSSL phiên bản 3.2.1
Đường dẫn tệp tương ứng
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
capath=/Applications/ServBay/package/common/openssl/3.2/certs
2
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
2
Ví dụ: Sử dụng lệnh OpenSSL
Nếu gặp lỗi khi thực hiện lệnh sau:
openssl s_client -quiet -connect gmail.com:443
Bạn có thể thấy lỗi như sau:
Connecting to 172.217.163.37
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
2
3
4
5
6
7
8
Cách giải quyết
Chỉ định CAFile để giải quyết vấn đề này:
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem
Sau khi kết nối thành công, bạn sẽ thấy đầu ra như sau:
Connecting to 172.217.163.37
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
2
3
4
5
6
7
Ví dụ: Sử dụng OpenSSL trong PHP
Nếu sử dụng OpenSSL trong PHP, bạn có thể giải quyết vấn đề này bằng cách chỉnh sửa tệp php.ini
hoặc thêm cấu hình tương ứng trong mã PHP.
Chỉnh sửa php.ini
Trong phần [openssl]
của tệp php.ini
, thêm hoặc chỉnh sửa nội dung sau:
[openssl]
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs
2
3
[openssl]
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
2
3
Thêm cấu hình trong mã PHP
Bạn cũng có thể chỉ định CAFile trong mã PHP. Ví dụ:
<?php
$server = 'ssl0.ovh.net';
$port = 465;
// Đường dẫn tệp CA
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
'cafile' => $caCertFile,
],
];
$context = stream_context_create($contextOptions);
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30,
STREAM_CLIENT_CONNECT,
$context
);
if ($connection) {
echo "Connection established to $server:$port\n";
fwrite($connection, "EHLO example.com\r\n");
while ($line = fgets($connection)) {
echo $line;
}
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
Ví dụ: Sử dụng OpenSSL trong Python
Nếu sử dụng OpenSSL trong Python, bạn có thể chỉ định CAFile trong mã. Ví dụ:
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
context = ssl.create_default_context(cafile=ca_cert_file)
with socket.create_connection((server, port)) as sock:
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"SSL established. Peer: {ssock.getpeercert()}")
ssock.sendall(b"EHLO example.com\r\n")
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Ví dụ: Sử dụng OpenSSL trong Node.js
Nếu sử dụng OpenSSL trong Node.js, bạn có thể chỉ định CAFile trong mã. Ví dụ:
const tls = require('tls');
const fs = require('fs');
const server = 'ssl0.ovh.net';
const port = 465;
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
const options = {
host: server,
port: port,
ca: fs.readFileSync(caCertFile),
checkServerIdentity: () => { return null; } // Bỏ qua xác thực máy chủ
};
const socket = tls.connect(options, () => {
console.log('SSL connection established');
socket.write("EHLO example.com\r\n");
});
socket.on('data', (data) => {
console.log(data.toString());
});
socket.on('error', (error) => {
console.error('Error:', error);
});
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
Ví dụ: Sử dụng OpenSSL trong curl
Nếu gặp vấn đề khi sử dụng lệnh curl
, bạn có thể chỉ định CAFile để giải quyết:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
Tổng kết
Khi sử dụng OpenSSL, nếu gặp lỗi 20:unable to get local issuer certificate
, bạn có thể giải quyết bằng cách chỉ định CAFile hoặc CAPath. Dựa vào loại chip của thiết bị, tìm đúng đường dẫn CAFile và CAPath, và thiết lập trong cấu hình OpenSSL, PHP, Python, Node.js hoặc curl.