エラーメッセージ
remote error: tls: handshake failure
HTTP クライアントでは、「Get \"https://…\": remote error: tls: handshake failure」という行になります。クライアントがアラートを送信した場合、Go はサーバー側でも同じ「remote error: tls: <alert>」の形式を使い、「http: TLS handshake error from …」というログ行に出力します。
Go crypto/tls client — the server sent TLS alert 40 (handshake_failure)
このエラーの意味
「remote error」は、相手側が TLS アラートを送信したことを意味します。この場合は、サーバーが Go クライアントにアラート 40(handshake_failure)を送信しています。サーバーが Go の提示したパラメーター(暗号スイート、グループ、署名アルゴリズム)を拒否したか、Go が送信しなかったクライアント証明書を要求したかのどちらかです。どちらなのかは Go 側では判断できませんが、サーバーのログでわかります。
発生する理由
共通の暗号スイートがない
よくあるGo クライアントのデフォルトのリストには、RSA 鍵交換、CBC、3DES のいずれの暗号スイートも含まれていません。これらの暗号スイートに限定されたサーバーとは、共通するものがありません。
サーバーがクライアント証明書を要求している
よくある相互 TLS モードの TLS 1.2 サーバーは、証明書が送られてこない場合に handshake_failure で応答することがよくあります。
SNI が受け入れられない
ときどきServerName を指定せずに IP アドレスで接続した場合や、サーバーが提供していない名前で接続した場合、CDN やプロキシではこのアラートが発生することがあります。
修正方法
- 1.
暗号スイートを比較する
tls.CipherSuites() は、Go が安全とみなして提示する暗号スイートを列挙します。これをサーバーの設定と比較してください。
go // What does this Go client offer by default? for _, s := range tls.CipherSuites() { fmt.Println(s.Name) } - 2.
相互 TLS のクライアント証明書を送信する
tls.LoadX509KeyPair で証明書と鍵を読み込み、tls.Config.Certificates に設定してください。
go // Mutual TLS: present a client certificate cert, err := tls.LoadX509KeyPair("client.pem", "client.key") if err != nil { log.Fatal(err) } client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, }}} - 3.
サーバー管理者:暗号スイートを最新のものにする
TLS 1.3 の暗号スイートと、ECDHE ベースの TLS 1.2 の暗号スイートは、現行のすべての Go リリースで動作します。
text Server side: enable TLS 1.3 and TLS 1.2 ECDHE suites with AES-GCM or ChaCha20-Poly1305.