CentOS8 出现 SSL certificate problem: unable to get local issuer certificate 解决办法

我想直接看解决办法

1. 错误来源

我在一个 docker 安装的 Centos8.5 系统中启动我自己写的 kook 机器人时,遇到了下面的异常输出

plaintext
1
aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host www.kookapp.cn:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1002)')]
点我查看完整错误截图

错误截图

这个图片如果加载不出来也没关系,不影响本文内容。

报错的大概意思是没有办法获取到本地的 issuer certificate。如果你知道证书和 CA 的含义,那么大概能猜到这个报错是什么意思。

所有站点的 ssl 证书,都需要经过 CA 机构的认证和颁发。操作系统会默认内嵌已有 CA 机构的公钥,以用于解密 CA 颁发的证书中的数字签名。

因为 CA 机构是内嵌在操作系统本地的,如果一个证书的颁发机构不在本地已有 CA 中,该站点的 SSL 证书可能是自签名证书,就会被报不安全。因为在默认情况下,自签名证书是不被认可的。

而无法获取本地 issuer certificate,我猜测意思就是没有办法获取道操作系统内嵌的 CA。

2. 解决

刚开始我尝试过通过 python 的 ssl 模组取消证书验证、更新本地证书和 openssl,都没有解决这个问题。

在 Kook 平台 khl.py 服务器的大佬帮助下,最终定位并解决了这个问题。

2.1 更新本地 ca 包

yum install ca-certificates,已经装过的话那就是 yum update ca-certificates 如果不行再 pip3 install certifi

操作完毕上面的步骤,发现本地已经安装了最新版本的 ca-certificates,python 也有安装 certifi

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ yum update ca-certificates
Repository extras is listed more than once in the configuration
Warning: failed loading '/etc/yum.repos.d/epel.repo', skipping.
Last metadata expiration check: 8:42:37 ago on Thu 23 Feb 2023 03:09:14 PM UTC.
Dependencies resolved.
Nothing to do.
Complete!

$ pip3.10 install certifi
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: certifi in ./.local/lib/python3.10/site-packages (2022.12.7)

$ pip3.10 install certifi -U
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: certifi in ./.local/lib/python3.10/site-packages (2022.12.7)

2.2 查看本地主机的链接

于是尝试使用如下命令,看看主机是否能成功连接到 kaiheila.cn(kookapp.cn) 并认证 ssl 证书

plaintext
1
openssl s_client -connect www.kookapp.cn:443 -showcerts

输出了如下的信息

plaintext
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = "DigiCert, Inc.", CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1
verify return:1
depth=0 CN = *.kookapp.cn
verify return:1
CONNECTED(00000003)
---
Certificate chain
0 s:CN = *.kookapp.cn
i:C = US, O = "DigiCert, Inc.", CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1
-----BEGIN CERTIFICATE-----
MIIHkTCCBXmgAwIBAgIQCUlurlf6m1VXsuISNqlT8DANBgkqhkiG9w0BAQsFADBc
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xNDAyBgNVBAMT
K1JhcGlkU1NMIEdsb2JhbCBUTFMgUlNBNDA5NiBTSEEyNTYgMjAyMiBDQTEwHhcN
MjIwNjE1MDAwMDAwWhcNMjMwNjE1MjM1OTU5WjAXMRUwEwYDVQQDDAwqLmtvb2th
cHAuY24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB1uHkQ/oEkGNl
oVa6wHz424VznxL+eUt6AAfNhtIXeone8rtnJqeoDJ24lmBYUrL00tChCp4rdTdd
crqPppDYSEY7+/U0hf39pDrmsGKZeeOu7JvANjtvqLFdXsqe5CnXtxAo5QUXB9gc
kttoLTZZX5O1Gyi6mqDtsFCTaVpsQmZWVcbA21zLAdAZJyUyzM2VyfCUPQHs+VFE
rcmA9SN8nkppLyf00lIHCWR6v9HYC2XgjHN2JX8ARJwTEddhHjvleMc/SlHncU6f
pZ8itWx8NzBL6MwPKEjmHgKp0VYBMY6PHrSR/j/XtgOweodp1JBIr6yLLlyCu/lJ
ksCwMUIZAgMBAAGjggOSMIIDjjAfBgNVHSMEGDAWgBTwnIX9op99j8lou9XUiU0d
vtOQ/zAdBgNVHQ4EFgQUYe6lb4/s4J82aqWfNDaev2MD8k4wIwYDVR0RBBwwGoIM
Ki5rb29rYXBwLmNuggprb29rYXBwLmNuMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwgZ8GA1UdHwSBlzCBlDBIoEagRIZCaHR0
cDovL2NybDMuZGlnaWNlcnQuY29tL1JhcGlkU1NMR2xvYmFsVExTUlNBNDA5NlNI
QTI1NjIwMjJDQTEuY3JsMEigRqBEhkJodHRwOi8vY3JsNC5kaWdpY2VydC5jb20v
UmFwaWRTU0xHbG9iYWxUTFNSU0E0MDk2U0hBMjU2MjAyMkNBMS5jcmwwPgYDVR0g
BDcwNTAzBgZngQwBAgEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2Vy
dC5jb20vQ1BTMIGHBggrBgEFBQcBAQR7MHkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
Y3NwLmRpZ2ljZXJ0LmNvbTBRBggrBgEFBQcwAoZFaHR0cDovL2NhY2VydHMuZGln
aWNlcnQuY29tL1JhcGlkU1NMR2xvYmFsVExTUlNBNDA5NlNIQTI1NjIwMjJDQTEu
Y3J0MAkGA1UdEwQCMAAwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsBaQB3AOg+0No+
9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAABgWbUskcAAAQDAEgwRgIhAL1q
ZKig2NqYtlAVnaXtJ7Tk+OuROrPHGR8t2zyNTb6xAiEAsLL5yuvJ/WtIj0bLwbKR
0/nFe/Juub3aZjrNvUhzZIIAdQA1zxkbv7FsV78PrUxtQsu7ticgJlHqP+Eq76gD
wzvWTAAAAYFm1LJ6AAAEAwBGMEQCIDK3qkxuTUB1Dngycy9/LECyVXjlpi2eJBAC
6oZPJQ/VAiBs1iupiYwavmD2m6QrDzzZrW2MJWx4LMxCtvf6pmN3NwB3ALc++yTf
nE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABgWbUsnkAAAQDAEgwRgIhAL5v
fkeeQAwowpkUhf0U/Z2fFiL8IY+QsCyED40zw0d1AiEA02UjHM3tztsrMn2xyzmT
kVgXnly/OozP+LjdwdsTa6gwDQYJKoZIhvcNAQELBQADggIBAGdBom7y2NXOZL7K
xtZFBFQCBJrEQOHZvuN4Etkmotb+0aVnrEZ/Qc5+zl86YqURcmOZgPhzSjuVfTvU
sJJ1TkC/tMYofoo1Db7B5yRxQmjEYRNLFAZ8rbP/pd5Js8ZGWW7RcxqG+zebqDDD
2CWvpiJNStu/yjWwdsFXQUwU0XOGUfwJDHD0eSBaa3uclAn3kUqF82l2X9qzvy9E
exGjPOSWyNuksjeScr32OyNUrwo7RUYJU4Ztl8xaiaxcvg7u2/o9WWzPhHMVI6ij
rJbj6Wn+BMe3SMYcR/L+foItjWAuOqOVB7IKO32JgCkP1vyl8AWs0up8ddI/9jn3
px8ii0HphKJnkCPZNmLnnOhFXQjoPy2PRmKqusP0kleJw1ZpQXsQpm3yZqFggyFH
8S4ii83kkHBynC89dlwlBIkF29Ds4mYjau6mZZxE69By7ptvFvcYREYslaRMxd16
6PEf5mp5jBKSiI2iYcP8PQOudhObpkuw+KHz8ntkf++HeluuEiqsKyiyyiXm5dmR
E/9uIUjPcQia5ohs2VpAgOLhLG6yphWCHQ6awFj9q3Ce1Mm5yXyZFvLXWGwoJj6j
CVXAZDchwX4uiHW2fI3GHz1pTPg0L3NE/M2CHnQWJlh9cgOljmaZXCeKmLdA9phD
+NQho5xVElRiF+rijlrkMutS7p1Y
-----END CERTIFICATE-----
1 s:C = US, O = "DigiCert, Inc.", CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1
i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
MIIFyzCCBLOgAwIBAgIQCgWbJfVLPYeUzGYxR3U4ozANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0yMjA1MDQwMDAwMDBaFw0zMTExMDkyMzU5NTlaMFwxCzAJBgNVBAYTAlVT
MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE0MDIGA1UEAxMrUmFwaWRTU0wgR2xv
YmFsIFRMUyBSU0E0MDk2IFNIQTI1NiAyMDIyIENBMTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBAKY5PJhwCX2UyBb1nelu9APen53D5+C40T+BOZfSFaB0
v0WJM3BGMsuiHZX2IHtwnjUhLL25d8tgLASaUNHCBNKKUlUGRXGztuDIeXb48d64
k7Gk7u7mMRSrj+yuLSWOKnK6OGKe9+s6oaVIjHXY+QX8p2I2S3uew0bW3BFpkeAr
LBCU25iqeaoLEOGIa09DVojd3qc/RKqr4P11173R+7Ub05YYhuIcSv8e0d7qN1sO
1+lfoNMVfV9WcqPABmOasNJ+ol0hAC2PTgRLy/VZo1L0HRMr6j8cbR7q0nKwdbn4
Ar+ZMgCgCcG9zCMFsuXYl/rqobiyV+8U37dDScAebZTIF/xPEvHcmGi3xxH6g+dT
CjetOjJx8sdXUHKXGXC9ka33q7EzQIYlZISF7EkbT5dZHsO2DOMVLBdP1N1oUp0/
1f6fc8uTDduELoKBRzTTZ6OOBVHeZyFZMMdi6tA5s/jxmb74lqH1+jQ6nTU2/Mma
hGNxUuJpyhUHezgBA6sto5lNeyqc+3Cr5ehFQzUuwNsJaWbDdQk1v7lqRaqOlYjn
iomOl36J5txTs0wL7etCeMRfyPsmc+8HmH77IYVMUOcPJb+0gNuSmAkvf5QXbgPI
Zursn/UYnP9obhNbHc/9LYdQkB7CXyX9mPexnDNO7pggNA2jpbEarLmZGi4grMmf
AgMBAAGjggGCMIIBfjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTwnIX9
op99j8lou9XUiU0dvtOQ/zAfBgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3R
VTAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl
cnQuY29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v
RGlnaUNlcnRHbG9iYWxSb290Q0EuY3J0MEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6
Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwPQYD
VR0gBDYwNDALBglghkgBhv1sAgEwBwYFZ4EMAQEwCAYGZ4EMAQIBMAgGBmeBDAEC
AjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEBAAfjh/s1f5dDdfm0sNm74/dW
MbbsxfYV1LoTpFt+3MSUWvSbiPQfUkoV57b5rutRJvnPP9mSlpFwcZ3e1nSUbi2o
ITGA7RCOj23I1F4zk0YJm42qAwJIqOVenR3XtyQ2VR82qhC6xslxtNf7f2Ndx2G7
Mem4wpFhyPDT2P6UJ2MnrD+FC//ZKH5/ERo96ghz8VqNlmL5RXo8Ks9rMr/Ad9xw
Y4hyRvAz5920myUffwdUqc0SvPlFnahsZg15uT5HkK48tHR0TLuLH8aRpzh4KJ/Y
p0sARNb+9i1R4Fg5zPNvHs2BbIve0vkwxAy+R4727qYzl3027w9jEFC6HMXRaDc=
-----END CERTIFICATE-----
---
Server certificate
subject=CN = *.kookapp.cn

issuer=C = US, O = "DigiCert, Inc.", CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3993 bytes and written 392 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

在这里面能看到如下内容

plaintext
1
2
SSL handshake has read 3993 bytes and written 392 bytes
Verification: OK

也就是说,我们的本地主机是能成功认证 kookapp.cn 的 ssl 证书的,那么问题就出现在 python 的 ssl 模块中了!

2.3 检测 python 的 ssl 模块

python 开启一个 REPL,输入如下的两行代码

python
1
2
import ssl
ssl.create_default_context().cert_store_stats()

输出的内容如下

plaintext
1
2
3
>>> import ssl
>>> ssl.create_default_context().cert_store_stats()
{'x509': 0, 'crl': 0, 'x509_ca': 0}

这里就能看出来问题了,全都是 0!难怪说没有办法获取本地的 CA 证书。

作为对比,一台正常的 windows11 电脑输出如下,应该是有数值的。

plaintext
1
2
3
>>> import ssl
>>> ssl.create_default_context().cert_store_stats()
{'x509': 87, 'crl': 0, 'x509_ca': 84}

2.4 解决错误

找到了错,那就得想办法解决!

python 开启一个 REPL,输入如下的两行代码

python
1
2
import ssl
ssl.get_default_verify_paths()

输出内容如下,这是本地 python 查找 CA 证书的位置

python
1
2
3
>>> import ssl
>>> ssl.get_default_verify_paths()
DefaultVerifyPaths(cafile=None, capath='/usr/local/openssl-1.1.1/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/usr/local/openssl-1.1.1/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/usr/local/openssl-1.1.1/ssl/certs')

如果是 mac 系统,输出如下

python
1
2
>>> ssl.get_default_verify_paths()
DefaultVerifyPaths(cafile=None, capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/Library/Frameworks/Python.framework/Versions/3.10/etc/openssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/Library/Frameworks/Python.framework/Versions/3.10/etc/openssl/certs')

其中 /usr/local/openssl-1.1.1/ssl/cert.pem 中应该会保存 CA 的信息,使用 ls 查看本地的这个文件的内容

plaintext
1
ls /usr/local/openssl-1.1.1/ssl/cert.pem

好家伙,发现文件压根不存在!

plaintext
1
ls: cannot access '/usr/local/openssl-1.1.1/ssl/cert.pem': No such file or directory

看看 openssl 的版本和路径

plaintext
1
openssl  version -d

输出如下

plaintext
1
OPENSSLDIR: "/etc/pki/tls"

于是看看这个目录下有没有我们需要的 cert.pem

plaintext
1
2
$ ls /etc/pki/tls
cert.pem certs ct_log_list.cnf misc openssl.cnf private

有!那么接下来要做的,就是创建一个软连接,将其弄到 /usr/local 中!

plaintext
1
ln -s /etc/pki/tls/cert.pem /usr/local/openssl-1.1.1/ssl/cert.pem

这样 Python 才能成功找到本地的 CA 证书。

3. 完美!

做完这一切后,再次运行机器人,这次么有报错了!

plaintext
1
2
3
4
5
$ py3 main.py
[FileManage] load all files
[BOT] Start at: [23-02-24 00:25:29]
[BOT.TASK] fetch_public_channel success

也成功响应了命令!

image-20230224092852118

MAC 下更常见?

似乎这个问题在 mac 下更常见?由于没有使用过 mac 设备,本文不解释相关内容

image-20230224093050205