OpenSSL Troubleshooting
When using OpenSSL (including in PHP environment, openssl
command, curl
command), you may encounter the error message 20:unable to get local issuer certificate
. This is a common historical issue with OpenSSL. For security reasons, OpenSSL does not trust any CA by default when called directly, requiring you to explicitly specify a CAFile or CAPath. This article will explain how to solve this problem and provide relevant code examples in PHP, Python, and Node.js.
Error Message 20:unable to get local issuer certificate
Problem Description
If you encounter the error message 20:unable to get local issuer certificate
when using OpenSSL, it means that OpenSSL cannot find the local issuer certificate. The solution to this problem is to specify a CAFile or CAPath.
OpenSSL Versions in ServBay
The OpenSSL version varies depending on the chip type used by ServBay:
- ServBay with Intel Chip: Uses OpenSSL 1.1.1u version.
- ServBay with Apple Silicon Chip: Uses OpenSSL 3.2.1 version.
Corresponding File Paths
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
Example: Using OpenSSL Command
If you encounter an error when executing the following command:
openssl s_client -quiet -connect gmail.com:443
You may see the following error output:
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
Solution
Solve the problem by specifying the CAFile:
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem
After a successful connection, the output is as follows:
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
Example: Using OpenSSL in PHP
If you are using OpenSSL in PHP, you can solve the problem by modifying the php.ini
file or adding the appropriate configuration in your code.
Modify php.ini
Add or modify the following content in the [openssl]
section of the php.ini
file:
[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
Add Configuration in Code
You can also specify the CAFile in your PHP code. For example:
<?php
$server = 'ssl0.ovh.net';
$port = 465;
// CA file path
$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
Example: Using OpenSSL in Python
If you are using OpenSSL in Python, you can specify the CAFile in your code. For example:
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
Example: Using OpenSSL in Node.js
If you are using OpenSSL in Node.js, you can specify the CAFile in your code. For example:
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; } // Ignore server identity verification
};
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
Example: Using OpenSSL in curl
If you encounter problems using the curl
command, you can solve them by specifying the CAFile:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
Summary
When using OpenSSL, if you encounter the 20:unable to get local issuer certificate
error, you can solve it by specifying a CAFile or CAPath. According to the device's chip type, find the corresponding CAFile and CAPath paths and set them in OpenSSL commands, PHP, Python, Node.js, or curl configurations.