TLS 握手在 Go 代码里的一些细节常常只在出错时才被注意到:证书不被信任、过期、主机名不符、没设 ServerName。这篇在本机用真实生成的 CA 和证书,把这些错误一个个触发出来,看错误原文,并量会话复用和证书密钥类型对握手耗时的影响。
握手成功(1.44 ms):版本 TLS 1.3,密码套件 TLS_AES_128_GCM_SHA256,ALPN 协商结果 "http/1.1",服务端名 "example.test",证书链长度 1,验证后的链数 1
tls.Conn.ConnectionState() 给出协商出的版本、密码套件、ALPN 结果、服务端名和验证后的证书链。默认就是 TLS 1.3;ALPN 取的是客户端提供的列表和服务端支持的列表的交集。
客户端没有信任这个 CA(RootCAs 为空,用系统根证书) → 失败:tls: failed to verify certificate: x509: “example.test” certificate is not trusted 服务端证书已过期 → 失败:tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2026-10-10T14:42:38+08:00 is after 2026-10-09T06:42:38Z 证书里的名字是 other.test,客户端要的是 example.test → 失败:tls: failed to verify certificate: x509: certificate is valid for other.test, not example.test 服务端最高只到 TLS 1.2,客户端要求最低 TLS 1.3 → 失败:remote error: tls: protocol version not supported 客户端不设 ServerName(用 IP 地址拨号) → 失败:tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config 同上,但 InsecureSkipVerify: true → 握手成功
四类典型错误的原文:不信任(certificate is not trusted,这个措辞来自 macOS 的系统验证器)、过期(has expired or is not yet valid,会带上当前时间)、主机名不符(valid for other.test, not example.test)、版本不匹配(服务端回 protocol version not supported)。另外不设 ServerName 又没设 InsecureSkipVerify,握手前就以配置错误拒绝;InsecureSkipVerify: true 会让握手成功,但文档明确说那样会遭遇中间人攻击,只用于测试。
本机回环上完整握手(30 次):中位 0.89 ms;配 ClientSessionCache 的复用握手(30 次中 DidResume 为真 30 次):中位 0.63 ms
配了 ClientSessionCache 并让服务端的会话票据到达后,30 次握手都是 DidResume,中位耗时 0.63 ms,对完整握手的 0.89 ms 省了约 30%。这是本机回环,没有网络往返,真实网络上节省的还有往返时延,我没有测。
服务端证书 ECDSA P-256:50 次完整握手中位 0.69 ms;密码套件 TLS_AES_128_GCM_SHA256 服务端证书 RSA 2048:50 次完整握手中位 1.59 ms;密码套件 TLS_AES_128_GCM_SHA256
服务端证书用 RSA 2048,完整握手 1.59 ms,比 ECDSA P-256 的 0.69 ms 慢约 2.3 倍(服务端要做一次 RSA 签名)。对大量短连接的服务,这是可以量到的成本。
InsecureSkipVerify controls whether a client verifies the server's certificate chain and host name. If InsecureSkipVerify is true, crypto/tls accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is used. This should be used only for testing or in combination with VerifyConnection or VerifyPeerCertificate. MinVersion contains the minimum TLS version that is acceptable. // By default, TLS 1.2 is currently used as the minimum. TLS 1.0 is the minimum supported by this package. MinVersion uint16
文档对 InsecureSkipVerify 的警告和 MinVersion 默认值(TLS 1.2)都与实验一致。