Saltar al contenido
ecdsa.com
Python ssl module (OpenSSL)cryptography de Python

Mensaje de error

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

También se muestra como

  • ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

Python añade la ubicación en el código fuente C, por ejemplo «(_ssl.c:1082)»; el número cambia con la versión de Python y no significa nada para el diagnóstico. urllib envuelve el error como «<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] …>», y requests y urllib3, como «Max retries exceeded … (Caused by SSLError(…))».

Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)

Qué significa

El módulo ssl de Python pidió a OpenSSL que verificara el certificado del servidor, y OpenSSL no encontró el emisor del último certificado de la cadena entre los certificados de CA que cargó este SSLContext. El servidor no está necesariamente mal configurado: a menudo, este Python simplemente consulta un bundle de CA distinto (o vacío) del que usan tu navegador o tu sistema operativo.

Por qué ocurre

Cómo solucionarlo

  1. 1.

    Compilaciones de python.org para macOS: ejecutar Install Certificates.command

    El script instala certifi y lo enlaza como bundle del OpenSSL de ese Python. Hay que volver a ejecutarlo después de instalar otra versión de Python.

    bash
    # macOS, python.org installer: run once per installed Python version
    "/Applications/Python 3.14/Install Certificates.command"
  2. 2.

    Usar el almacén de confianza del sistema operativo con truststore

    truststore hace que ssl.SSLContext verifique a través del sistema operativo (el llavero en macOS, el almacén de certificados de Windows, el bundle del sistema en Linux), de modo que las raíces que despliega tu departamento de TI también funcionan en Python. También cubre urllib3, requests y pip, que lo usa de forma predeterminada desde pip 24.2.

    python
    # pip install truststore==0.10.5  — verify with the OS trust store (Keychain, Windows store, Linux bundle)
    import truststore
    truststore.inject_into_ssl()   # call once at start-up, before creating connections
    
    import urllib.request
    urllib.request.urlopen("https://api.example.com/")
  3. 3.

    Cargar la CA explícitamente para un cliente

    Pasa cafile (o cadata) a ssl.create_default_context() en el código que se comunica con un servicio privado. El contexto sigue verificando los nombres de host y las fechas.

    python
    import ssl, urllib.request
    
    ctx = ssl.create_default_context(cafile="/etc/ssl/private-ca/corp-root-ca.pem")
    urllib.request.urlopen("https://api.example.com/", context=ctx)
  4. 4.

    Comprobar y establecer la ruta del bundle predeterminado

    ssl.get_default_verify_paths() muestra qué archivo y qué directorio usa OpenSSL. SSL_CERT_FILE y SSL_CERT_DIR los sustituyen para el proceso; haz que apunten a un bundle que contenga tanto las raíces públicas como tu CA.

    bash
    # Where does this Python look by default?
    python3 -c "import ssl; print(ssl.get_default_verify_paths())"
    # Point OpenSSL-based clients at a bundle that includes your CA
    export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
  5. 5.

    Propietarios de servidores: enviar la cadena completa

    Si el handshake contiene un solo certificado y su emisor es un intermedio público, configura el servidor con el archivo de cadena completa.

    bash
    # Server side: 1 certificate in the handshake = intermediate missing
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
      | grep -c 'BEGIN CERTIFICATE'

Errores relacionados

Explorar toda la base de datos de errores de firma