Mensagem de erro
curl: (35) TLS connect error: error:0A000410:SSL routines::ssl/tls alert handshake failure
Também aparece como
- curl: (35) error:0A000410:SSL routines::sslv3 alert handshake failure
- curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
O OpenSSL 3.3 e posteriores escrevem o motivo como “ssl/tls alert handshake failure”; o OpenSSL 3.1 e anteriores imprimem “sslv3 alert handshake failure” (o 1.1.1, com o código 14094410 e o nome da função ssl3_read_bytes). Versões recentes do curl acrescentam “TLS connect error: ” no início.
curl (OpenSSL backend) — CURLE_SSL_CONNECT_ERROR, TLS alert 40 (handshake_failure) received from the server
O que significa
O servidor encerrou o handshake enviando o alerta TLS 40 (handshake_failure): não conseguiu negociar parâmetros compatíveis com o que o curl ofereceu. O alerta não traz motivo, então a causa real só aparece nos logs do servidor; no lado do cliente, você só pode comparar o que cada lado aceita. Nesse ponto, o certificado ainda não tinha sido verificado.
Por que acontece
Nenhuma cipher suite em comum
comumServidores legados restritos a cipher suites com troca de chaves RSA, CBC-SHA1, 3DES ou RC4 encontram clientes que as removeram; servidores com configuração reforçada que permitem só algumas cipher suites encontram clientes antigos que não as têm. Em ambos os casos, o servidor não encontra nada aceitável no ClientHello.
O cliente não enviou o nome do servidor (SNI)
comumCDNs e proxies multilocatários (Cloudflare, AWS CloudFront, muitos controladores Ingress do Kubernetes) rejeitam handshakes sem SNI ou para nomes que eles não atendem. Conectar por endereço IP ou com ferramentas que omitem o SNI dispara exatamente esse alerta.
O servidor exige um certificado de cliente
ocasionalAlguns servidores TLS 1.2 configurados para TLS mútuo respondem a um certificado de cliente ausente ou inaceitável com handshake_failure, em vez dos alertas de certificado mais específicos.
Nenhum grupo de troca de chaves ou algoritmo de assinatura em comum
ocasionalUm servidor que só permite um grupo híbrido pós-quântico, uma curva brainpool ou assinaturas RSA-PSS pode falhar com clientes que não os oferecem, e o inverso acontece com clientes muito antigos.
Como corrigir
- 1.
Comparar o que os dois lados oferecem
Enumere as cipher suites e as versões do protocolo do servidor e compare-as com a lista de cifras do cliente. A saída detalhada do curl mostra até onde o handshake chegou e qual versão foi tentada.
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.
Donos do servidor: usar uma configuração de cifras atual
Ofereça TLS 1.3 e TLS 1.2 com cipher suites ECDHE e AEAD (AES-GCM, ChaCha20-Poly1305). Todos os clientes da última década oferecem suporte a elas, e nada moderno precisa de troca de chaves RSA nem de 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 o nome de servidor correto
Use o nome do host na URL, ou mantenha o nome e force o endereço com --resolve. Com o openssl s_client, passe -servername ao se conectar a um 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.
Apresentar o certificado de cliente para TLS mútuo
Se o endpoint for uma API mTLS, configure o certificado de cliente e a chave; os logs do servidor vão indicar o problema como certificado do peer ausente ou não verificável.
bash # Mutual TLS: present the client certificate and key the server expects curl --cert client.pem --key client.key https://mtls.example.com/
Erros 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)