استكشاف أخطاء OpenSSL وإصلاحها
قد تواجه خطأ 20:unable to get local issuer certificate
عند استخدام OpenSSL (بما في ذلك بيئة PHP، أوامر openssl
، أو أوامر curl
). هذه مشكلة شائعة في OpenSSL لسبب تاريخي. لأسباب أمنية، لا يثق OpenSSL بأي CA عند الاستدعاء المباشر، لذا يتطلب تحديد CAFile أو CAPath بشكل خاص. ستقدم هذه المقالة كيفية حل هذه المشكلة وتتضمن أمثلة برمجية ذات صلة بلغة PHP، Python، Node.js.
خطأ 20:unable to get local issuer certificate
وصف المشكلة
إذا ظهرت رسالة الخطأ 20:unable to get local issuer certificate
عند استخدام OpenSSL، فهذا لأن OpenSSL لا يستطيع العثور على شهادة الجهة المصدرة المحلية. لحل هذه المشكلة، يجب تحديد CAFile أو CAPath.
إصدار OpenSSL في ServBay
يختلف إصدار OpenSSL المستخدم في ServBay اعتمادًا على نوع الرقاقة:
- إصدار رقاقة Intel لـ ServBay: يستخدم إصدار OpenSSL 1.1.1u
- إصدار Rقاقة Apple Silicon لـ ServBay: يستخدم إصدار OpenSSL 3.2.1
مسارات الملفات المقابلة
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
مثال: استخدام أمر OpenSSL
إذا واجهت خطأ عند تشغيل الأمر التالي:
openssl s_client -quiet -connect gmail.com:443
قد ترى رسالة الخطأ التالية:
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
طريقة الحل
يمكن حل هذه المشكلة بتحديد CAFile:
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem
بعد نجاح الاتصال، ستظهر النتيجة التالية:
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
مثال: استخدام OpenSSL في PHP
إذا كنت تستخدم OpenSSL في PHP، يمكنك تعديل ملف php.ini
أو إضافة التكوين المناسب في الكود.
تعديل php.ini
في جزء [openssl]
من ملف php.ini
، أضف أو عدل ما يلي:
[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
إضافة التكوين في الكود
يمكنك تحديد CAFile داخل كود PHP أيضًا. على سبيل المثال:
<?php
$server = 'ssl0.ovh.net';
$port = 465;
// مسار ملف 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 "تم إنشاء الاتصال بـ $server:$port\n";
fwrite($connection, "EHLO example.com\r\n");
while ($line = fgets($connection)) {
echo $line;
}
fclose($connection);
} else {
echo "فشل الاتصال بـ $server:$port. خطأ: $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
مثال: استخدام OpenSSL في Python
إذا كنت تستخدم OpenSSL في Python، يمكنك تحديد CAFile في الكود، مثلًا:
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. المتبادل: {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
مثال: استخدام OpenSSL في Node.js
إذا كنت تستخدم OpenSSL في Node.js، يمكنك تحديد CAFile في الكود، مثلًا:
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; } // تجاهل تحقق هوية السيرفر
};
const socket = tls.connect(options, () => {
console.log('تم تأسيس اتصال SSL');
socket.write("EHLO example.com\r\n");
});
socket.on('data', (data) => {
console.log(data.toString());
});
socket.on('error', (error) => {
console.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
مثال: استخدام OpenSSL في curl
إذا واجهت مشكلة باستخدام أمر curl
، يمكنك استخدام CAFile لتحل المشكلة:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
الخلاصة
عند استخدام OpenSSL، إذا ظهرت رسالة خطأ 20:unable to get local issuer certificate
، يمكنك حلها بتحديد CAFile أو CAPath. اعتمادًا على نوع رقاقة الجهاز، العثور على مسار CAFile و CAPath المناسب وضبط التكوين في أوامر OpenSSL، أو PHP، أو Python، أو Node.js، أو curl.