跳到主要内容
ecdsa.com
Python ssl module (OpenSSL host check)Python cryptography 库

错误信息

[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。

原因

修复方法

  1. 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. 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. 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())

相关错误

浏览完整的签名错误库