Go语言NewRequestWithCert函数报x509无IP SANs证书验证错误排查
问题分析与修复方案
错误原因
报错x509: cannot validate certificate for <host ip> because it doesn't contain any IP SANs本质是:你通过IP地址访问服务端,但服务端的SSL证书里没有把该IP纳入SAN(主题备用名称),同时你的代码还搞反了TLS证书验证的逻辑配置。
代码核心问题
- 证书池配置方向错误:你把证书加到了
ClientCAs字段,这个字段是用来让服务端验证客户端证书的。作为客户端,我们需要验证服务端证书的合法性,应该把CA证书加到RootCAs字段。 - 多余的客户端认证配置:
ClientAuth: tls.RequireAndVerifyClientCert是要求服务端必须验证客户端证书,如果你不需要给服务端提供客户端证书,这个配置完全没必要,反而会干扰TLS握手流程。 - ServerName使用逻辑:如果服务端证书绑定的是域名,但你用IP访问,
ServerName要设置成证书上的域名,这样TLS验证时会用这个域名去匹配证书的SAN或CN;如果证书本身就没有IP SAN,要么修改证书添加对应IP,要么测试环境临时跳过验证(生产环境绝对禁止)。
修正后的代码
func (o *Client) NewRequestWithCert(urlLink string, requestData interface{}, responseData interface{}, certName string, host string) error { // Keep on client o.requestData = &requestData o.responseData = &responseData o.url = urlLink certByte, errCert := ioutil.ReadFile("/app/certificates/" + certName) if errCert != nil { logrus.Errorf("cert err: %v", errCert) return errCert } caCertPool := x509.NewCertPool() // 将CA证书加入RootCAs,用于验证服务端证书合法性 if !caCertPool.AppendCertsFromPEM(certByte) { logrus.Errorf("failed to add CA certificate to pool") return errors.New("invalid CA certificate") } logrus.Debug("certByte : ", certByte) tlsConfig := &tls.Config{ ServerName: host, // 若用IP访问但证书绑域名,这里填证书域名;若证书含IP SAN,填IP也可 RootCAs: caCertPool, // 修正为正确的证书池字段 // 移除不必要的ClientAuth配置,除非你需要向服务端提供客户端证书 // ClientAuth: tls.RequireAndVerifyClientCert, } // 测试环境临时跳过证书验证(生产环境绝对禁用) // tlsConfig.InsecureSkipVerify = true // New http client o.client = &http.Client{ Timeout: 30 * time.Second, Transport: &http.Transport{ TLSClientConfig: tlsConfig, }, } // Make buffer var requestDataBuffer bytes.Buffer err := xml.NewEncoder(&requestDataBuffer).Encode(requestData) if err != nil { // 补上之前遗漏的编码错误判断 logrus.Errorf("encode request data err: %v", err) return err } logrus.Info("[ProxyRequest] URL: ", urlLink) logrus.Info("[ProxyRequest] Data: ", requestDataBuffer.String()) // Build a new request, but not doing the POST yet req, err := http.NewRequest("POST", urlLink, &requestDataBuffer) if err != nil { return err } // Keep on client o.req = req // Fill default header o.fillHeader() return nil }
额外说明
- 生产环境优先解决证书本身的问题:给服务端证书添加对应IP的SAN字段,这是最安全合规的方案。
- 若必须用IP访问且无法修改证书,仅能在测试环境临时开启
InsecureSkipVerify = true,生产环境绝对禁止,否则会暴露在中间人攻击风险中。
内容的提问来源于stack exchange,提问作者LuneLun
相关产品推荐
相关产品推荐

