本文へスキップ
ecdsa.com
curl (OpenSSL backend)curl

エラーメッセージ

curl: (35) TLS connect error: error:0A000410:SSL routines::ssl/tls alert handshake failure

別の表記

  • 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 以降では理由が「ssl/tls alert handshake failure」と表記され、OpenSSL 3.1 以前では「sslv3 alert handshake failure」と出力されます(1.1.1 ではコード 14094410 と関数名 ssl3_read_bytes も付きます)。最近の curl のリリースでは、先頭に「TLS connect error:」が付きます。

curl (OpenSSL backend) — CURLE_SSL_CONNECT_ERROR, TLS alert 40 (handshake_failure) received from the server

このエラーの意味

サーバーは TLS アラート 40(handshake_failure)を送信してハンドシェイクを終了しました。curl が提示した内容では、パラメーターについて合意できなかったということです。このアラートには理由が含まれないため、実際の原因はサーバーのログでしか確認できません。クライアント側でできるのは、双方がサポートしている内容を比較することだけです。この時点では、証明書はまだ検証されていません。

発生する理由

修正方法

  1. 1.

    双方が提示する内容を比較する

    サーバーの暗号スイートとプロトコルバージョンを列挙し、クライアントの暗号リストと比較してください。curl の詳細出力を見ると、ハンドシェイクがどこまで進んだか、どのバージョンが試行されたかがわかります。

    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.

    サーバー管理者:現行の暗号設定を使う

    TLS 1.3 と、ECDHE および AEAD の暗号スイート(AES-GCM、ChaCha20-Poly1305)を使う TLS 1.2 を提供してください。過去 10 年間のクライアントはすべてこれらに対応しており、最近のソフトウェアで RSA 鍵交換や 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.

    正しいサーバー名を送信する

    URL ではホスト名を使うか、--resolve でアドレスを強制しつつ名前は維持してください。openssl s_client では、IP アドレスに接続するときに -servername を指定してください。

    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.

    相互 TLS のクライアント証明書を提示する

    エンドポイントが mTLS の API であれば、クライアント証明書と鍵を設定してください。サーバーのログでは、この問題はピア証明書がない、または検証できないこととして示されます。

    bash
    # Mutual TLS: present the client certificate and key the server expects
    curl --cert client.pem --key client.key https://mtls.example.com/

関連するエラー

署名エラーデータベースをすべて見る