跳到主要内容
ecdsa.com
Go crypto/tls (Go ≥ 1.20)Go

错误信息

tls: failed to verify certificate: x509: certificate signed by unknown authority

Go crypto/tls (Go ≥ 1.20) — wrapping x509 verification errors

含义

从 Go 1.20 开始,TLS 握手失败时会给底层的 x509 错误加上“tls: failed to verify certificate:”前缀,因此日志中既能看到出错的层(TLS 握手),也能看到根本原因(证书链构建失败)。后缀各不相同——未知颁发机构、证书过期、主机名不匹配——真正需要处理的是后缀。这个前缀也很适合用来过滤日志:用 grep 搜索它,就能把 TLS 信任失败和应用层错误区分开,而这正是加上这层包装的原因。

原因

修复方法

  1. 1.

    为客户端提供正确的信任锚

    与不带前缀的 x509 错误解法相同:准备一个包含签发该证书的 CA 的根证书池,并把它设置到实际用于拨号的 Transport 上。

    go
    pool := x509.NewCertPool()
    caPEM, _ := os.ReadFile("ca.pem")
    pool.AppendCertsFromPEM(caPEM)
    
    client := &http.Client{Transport: &http.Transport{
        TLSClientConfig: &tls.Config{RootCAs: pool},
    }}
    resp, err := client.Get("https://internal.example:8443/healthz")
  2. 2.

    查看服务器实际出示的内容

    在改动客户端代码之前,先查看服务器提供的证书链、其中各证书的颁发者和有效期——大多数问题最终都要通过服务端的证书链配置来解决。

    bash
    openssl s_client -connect internal.example:8443 -servername internal.example \
      </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
  3. 3.

    保持验证开启,测试中也一样

    httptest 服务器会直接给你一个预先配置好的客户端;使用它,而不是全局禁用验证。

    go
    srv := httptest.NewTLSServer(handler)
    defer srv.Close()
    resp, err := srv.Client().Get(srv.URL)   // trusts srv's cert, nothing else

相关错误

浏览完整的签名错误库