错误信息
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'api.example.com'.
也可能输出为
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '127.0.0.1'.
引号中的名称是你连接的主机。对于 IP 地址,Python 报出的是“IP address mismatch, certificate is not valid for '…'.”。消息后面跟着源代码位置,例如“(_ssl.c:1082)”。
Python ssl module (OpenSSL host check) — ssl.SSLCertVerificationError, X509_V_ERR_HOSTNAME_MISMATCH (62)
含义
证书链是受信任的,但 Python 被要求验证的名称(URL 中的主机,或 server_hostname)不在证书的主题备用名称(SAN)中。从 Python 3.7 开始,这项检查由 OpenSSL 在握手期间完成,因此它表现为证书验证失败,而不是旧的 ssl.CertificateError。
原因
证书中没有列出这个主机名
常见证书覆盖的是其他名称(www.、根域名、旧的服务名称)。通配符只覆盖恰好一级标签,因此 *.example.com 匹配 api.example.com,但不匹配 example.com 或 a.b.example.com。
通过 IP 地址、localhost 或容器别名连接
常见URL 中使用的是 127.0.0.1、私有 IP、“localhost”或 docker-compose 服务名称,而为公网 DNS 名称签发的证书中不会出现其中任何一个。
服务器返回了另一个网站的证书
偶见共享主机根据 SNI 名称选择证书;没有单独配置的名称会得到默认证书。
修复方法
- 1.
列出证书覆盖的名称
将 subjectAltName 条目与你传给 Python 的主机进行比较。
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -subject -ext subjectAltName - 2.
重新签发证书,在 subjectAltName 中包含所有名称
添加所有 DNS 名称;如果要通过 IP 访问,还要添加 IP 条目。只出现在通用名称中的名称不会用于匹配。
bash # Include every DNS name and IP address clients use openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \ -addext "subjectAltName=DNS:api.example.com,DNS:api.internal.example.com,IP:10.0.0.5" - 3.
连接到某个地址时仍验证证书中的名称
如果你必须连接到特定的地址(负载均衡器节点、隧道),就让 server_hostname 保持为证书中的名称,而不是关闭主机名检查。
python import socket, ssl # Connect to an address, but verify (and send SNI) for the certificate's name ctx = ssl.create_default_context() with socket.create_connection(("10.0.0.5", 443)) as raw: with ctx.wrap_socket(raw, server_hostname="api.example.com") as tls: print(tls.version())
相关错误
x509: certificate is valid for example.com, not api.example.comGo crypto/x509curl: (60) SSL: no alternative certificate subject name matches target hostname…curl (OpenSSL backend)Hostname/IP does not match certificate's altnames: Host: api.example.com. is no…Node.js tls.checkServerIdentity()Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1…requests / urllib3 (Python)