Mensaje de error
curl: (35) TLS connect error: error:0A000410:SSL routines::ssl/tls alert handshake failure
También se muestra como
- curl: (35) error:0A000410:SSL routines::sslv3 alert handshake failure
- curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
OpenSSL 3.3 y versiones posteriores escriben el motivo como «ssl/tls alert handshake failure»; OpenSSL 3.1 y anteriores imprimen «sslv3 alert handshake failure» (1.1.1, con el código 14094410 y el nombre de función ssl3_read_bytes). Las versiones recientes de curl anteponen «TLS connect error: ».
curl (OpenSSL backend) — CURLE_SSL_CONNECT_ERROR, TLS alert 40 (handshake_failure) received from the server
Qué significa
El servidor termina el handshake enviando la alerta TLS 40 (handshake_failure): no puede acordar parámetros con lo que ofrece curl. La alerta no incluye ningún motivo, así que la causa real solo es visible en los registros del servidor; en el lado del cliente solo puedes comparar lo que admite cada parte. En este punto el certificado aún no se ha comprobado.
Por qué ocurre
Ningún conjunto de cifrado en común
frecuenteLos servidores antiguos limitados a intercambio de claves RSA o a conjuntos CBC-SHA1, 3DES o RC4 se encuentran con clientes que los han eliminado; los servidores reforzados que solo permiten unos pocos conjuntos se encuentran con clientes antiguos que no los tienen. En ambos casos el servidor no encuentra nada aceptable en el ClientHello.
El cliente no envió el nombre del servidor (SNI)
frecuenteLas CDN y los proxies multiinquilino (Cloudflare, AWS CloudFront, muchos ingress de Kubernetes) rechazan los handshakes sin SNI o para nombres que no sirven. Conectarse por dirección IP o con herramientas que omiten el SNI provoca exactamente esta alerta.
El servidor exige un certificado de cliente
ocasionalAlgunos servidores TLS 1.2 configurados para TLS mutuo responden con handshake_failure cuando el certificado de cliente falta o no es aceptable, en lugar de enviar las alertas de certificado más específicas.
Ningún grupo de intercambio de claves ni algoritmo de firma en común
ocasionalUn servidor que solo permite un grupo híbrido poscuántico, una curva brainpool o firmas RSA-PSS puede fallar con clientes que no los ofrecen, y lo mismo ocurre a la inversa con clientes muy antiguos.
Cómo solucionarlo
- 1.
Comparar lo que ofrece cada lado
Enumera los conjuntos y las versiones de protocolo del servidor y compáralos con la lista de cifrados del cliente. La salida detallada de curl muestra hasta dónde llegó el handshake y qué versión se intentó.
bash # What the server accepts (needs nmap) and what this client offers nmap --script ssl-enum-ciphers -p 443 api.example.com openssl ciphers -v 'DEFAULT' | head -20 # Full handshake trace from curl's side curl -sv https://api.example.com/ -o /dev/null 2>&1 | grep -E '^\* (TLS|SSL)' - 2.
Propietarios del servidor: usar una configuración de cifrado actual
Ofrece TLS 1.3 y TLS 1.2 con conjuntos ECDHE y AEAD (AES-GCM, ChaCha20-Poly1305). Todos los clientes de la última década los admiten, y nada moderno necesita intercambio de claves RSA ni CBC.
text # nginx: TLS 1.2 with forward-secret AEAD suites + TLS 1.3 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_prefer_server_ciphers off; - 3.
Enviar el nombre de servidor correcto
Usa el nombre de host en la URL, o conserva el nombre mientras fuerzas una dirección con --resolve. Con openssl s_client, pasa -servername al conectarte a una IP.
bash # Use the host name in the URL (SNI is sent automatically); for IP targets keep the name: curl --resolve api.example.com:443:203.0.113.10 https://api.example.com/ # openssl s_client sends SNI only with -servername (or a host name in -connect) openssl s_client -connect 203.0.113.10:443 -servername api.example.com </dev/null - 4.
Presentar el certificado de cliente para TLS mutuo
Si el endpoint es una API mTLS, configura el certificado de cliente y la clave; los registros del servidor indicarán que el problema es un certificado del otro extremo ausente o que no se puede verificar.
bash # Mutual TLS: present the client certificate and key the server expects curl --cert client.pem --key client.key https://mtls.example.com/
Errores relacionados
SSL routines:ssl3_read_bytes:ssl/tls alert handshake failureOpenSSL (client side)SSL routines:tls_post_process_client_hello:no shared cipherOpenSSL (server side: nginx, Apache, Postfix, openssl s_server)curl: (35) TLS connect error: error:0A00042E:SSL routines::tlsv1 alert protocol…curl (OpenSSL backend)[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] ssl/tls alert handshake failurePython ssl module (OpenSSL)javax.net.ssl.SSLHandshakeException: (handshake_failure) Received fatal alert:…Java (JSSE)