Pular para o conteúdo
ecdsa.com
curl (OpenSSL backend)curl

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

Como corrigir

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

Ver toda a base de erros de assinatura