Go crypto/tls clientGo
错误信息
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 不被接受
偶见通过 IP 地址连接却没有设置 ServerName,或者使用服务器不提供服务的名称,都可能在 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.