OpenSSL故障排除
在使用OpenSSL時(包括在PHP環境、openssl
命令、curl
命令),可能會遇到錯誤提示20:unable to get local issuer certificate
。這是一個常見的OpenSSL歷史遺留問題。出於安全原因,OpenSSL在直接調用時不信任任何的CA,需要專門指定CAFile或CAPath。本文將介紹如何解決該問題,並提供PHP、Python、Node.js的相關代碼示例。
錯誤提示20:unable to get local issuer certificate
問題描述
如果在使用OpenSSL時出現錯誤提示20:unable to get local issuer certificate
,這是因為OpenSSL無法找到本地的頒發機構證書。解決這個問題的方法是指定CAFile或CAPath。
ServBay中的OpenSSL版本
根據ServBay使用的晶片類型,OpenSSL版本有所不同:
- Intel晶片版本的ServBay:使用OpenSSL 1.1.1u版本
- 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
示例:在PHP中使用OpenSSL
如果在PHP中使用OpenSSL,可以通過修改php.ini
文件或在代碼中添加相應的配置來解決問題。
修改php.ini
在php.ini
文件的[openssl]
部分,添加或修改以下內容:
[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
在代碼中添加配置
您也可以在PHP代碼中指定CAFile。例如:
<?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
示例:在Python中使用OpenSSL
如果在Python中使用OpenSSL,可以在代碼中指定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 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
示例:在Node.js中使用OpenSSL
如果在Node.js中使用OpenSSL,可以在代碼中指定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 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
示例:在curl中使用OpenSSL
如果在使用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配置中進行設置。