Перейти к содержимому
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. Имена, указанные только в общем имени (CN), при сопоставлении не используются.

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

Связанные ошибки

Открыть всю базу ошибок подписей