Message d’erreur
curl: (35) TLS connect error: error:0A000410:SSL routines::ssl/tls alert handshake failure
Également affiché sous la forme
- 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 et les versions ultérieures formulent la raison « ssl/tls alert handshake failure » ; OpenSSL 3.1 et les versions antérieures affichent « sslv3 alert handshake failure » (1.1.1 avec le code 14094410 et le nom de fonction ssl3_read_bytes). Les versions récentes de curl ajoutent « TLS connect error: » devant.
curl (OpenSSL backend) — CURLE_SSL_CONNECT_ERROR, TLS alert 40 (handshake_failure) received from the server
Ce que cela signifie
Le serveur a mis fin au handshake en envoyant l’alerte TLS 40 (handshake_failure) : il n’a pas pu s’accorder sur des paramètres avec ce que proposait curl. L’alerte n’indique aucune raison, si bien que la cause réelle n’apparaît que dans les journaux du serveur ; côté client, vous pouvez seulement comparer ce que chaque partie prend en charge. À ce stade, le certificat n’avait pas encore été vérifié.
Pourquoi cela se produit
Aucune suite de chiffrement en commun
fréquenteLes anciens serveurs limités aux suites à échange de clés RSA, CBC-SHA1, 3DES ou RC4 rencontrent des clients qui les ont supprimées ; les serveurs durcis qui n’autorisent que quelques suites rencontrent d’anciens clients qui ne les proposent pas. Dans les deux cas, le serveur ne trouve rien d’acceptable dans le ClientHello.
Le client n’a pas envoyé le nom du serveur (SNI)
fréquenteLes CDN et les proxys multilocataires (Cloudflare, AWS CloudFront, de nombreux ingress Kubernetes) rejettent les handshakes sans SNI ou portant sur des noms qu’ils ne servent pas. Une connexion par adresse IP ou avec des outils qui omettent le SNI déclenche exactement cette alerte.
Le serveur exige un certificat client
occasionnelleCertains serveurs TLS 1.2 configurés pour le TLS mutuel répondent à un certificat client absent ou inacceptable par handshake_failure, au lieu des alertes de certificat plus spécifiques.
Aucun groupe d’échange de clés ni algorithme de signature en commun
occasionnelleUn serveur qui n’autorise qu’un groupe hybride post-quantique, une courbe brainpool ou des signatures RSA-PSS peut échouer face aux clients qui ne les proposent pas, et inversement avec les clients très anciens.
Comment corriger l’erreur
- 1.
Comparer ce que proposent les deux parties
Énumérez les suites et les versions de protocole du serveur, puis comparez-les avec la liste de chiffrements du client. La sortie en mode verbeux de curl montre jusqu’où le handshake est allé et quelle version a été tentée.
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.
Propriétaires de serveurs : utiliser une configuration de chiffrement actuelle
Proposez TLS 1.3 et TLS 1.2 avec des suites ECDHE et AEAD (AES-GCM, ChaCha20-Poly1305). Tous les clients de la dernière décennie les prennent en charge, et aucun logiciel moderne n’a besoin de l’échange de clés RSA ni 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.
Envoyer le bon nom de serveur
Utilisez le nom d’hôte dans l’URL, ou conservez le nom tout en forçant une adresse avec --resolve. Avec openssl s_client, passez -servername lorsque vous vous connectez à une adresse 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.
Présenter le certificat client pour le TLS mutuel
Si le point de terminaison est une API mTLS, configurez le certificat client et sa clé ; les journaux du serveur décriront le problème comme un certificat de pair absent ou impossible à vérifier.
bash # Mutual TLS: present the client certificate and key the server expects curl --cert client.pem --key client.key https://mtls.example.com/
Erreurs associées
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)