使用自签名证书的 TLS 连接失败

Go TLS connection with self-signed certs failing

当 运行 在我的笔记本电脑上本地使用我自己的 'developer' 证书访问内部服务时,以下简化的测试用例代码有效

如果我 运行 在远程机器上使用动态生成的证书(所有这些都由我组织中的一个单独的团队处理),它会失败并显示 400 和 "No required SSL certificate was sent" 错误

但如果我在远程机器上使用 curl,并指定与我的 Go 代码中引用的证书相同的证书,它将起作用

看来证书不是问题而是 Go 代码,但这本身似乎不是问题,因为它在本地使用我自己的证书

package main

import (
  "crypto/tls"
  "crypto/x509"
  "fmt"
  "io/ioutil"
  "net/http"
  "os"
  "time"
)

func main() {
  transport, transErr := configureTLS()
  if transErr != nil {
    fmt.Printf("trans error: %s", transErr.Error())
    return
  }

  timeout := time.Duration(1 * time.Second)
  client := http.Client{
    Transport: transport,
    Timeout:   timeout,
  }
  resp, clientErr := client.Get("https://my-service-with-nginx/")

  if clientErr != nil {
    fmt.Printf("client error: %s", clientErr.Error())
  } else {
    defer resp.Body.Close()

    contents, contErr := ioutil.ReadAll(resp.Body)
    if contErr != nil {
      fmt.Printf("contents error: %s", contErr.Error())
    }

    fmt.Printf("\n\ncontents:\n\n%+v", string(contents))
  }
}

func configureTLS() (*http.Transport, error) {
  certPath := "/path/to/client.crt"
  keyPath := "/path/to/client.key"
  caPath := "/path/to/ca.crt"

  // Load client cert
  cert, err := tls.LoadX509KeyPair(certPath, keyPath)
  if err != nil {
    return nil, err
  }

  // Load CA cert
  caCert, err := ioutil.ReadFile(caPath)
  if err != nil {
    return nil, err
  }
  caCertPool := x509.NewCertPool()
  caCertPool.AppendCertsFromPEM(caCert)

  // Setup HTTPS client
  tlsConfig := &tls.Config{
    Certificates:       []tls.Certificate{cert},
    RootCAs:            caCertPool,
    InsecureSkipVerify: true,
  }
  tlsConfig.BuildNameToCertificate()

  return &http.Transport{TLSClientConfig: tlsConfig}, nil
}

有谁知道为什么会这样?

我认为这可能是 Go 具有的重新协商错误(从 1.6 开始),但我认为这里不是这种情况,否则当 运行 在本地安装应用程序时它会失败(但是它没有,使用我自己的开发证书和 运行ning 在本地工作正常 - 只有当 运行 在具有不同证书的远程实例上时才会出现问题;并且这些证书在工作时不是问题curl)

使用时很好

所以这里的实际问题部分与我的组织基础设施有关,部分与 nginx 如何使用有关 ssl_client_certificate

我们有 int、test 和 live 环境

我被引导相信环境可以相互通信。

所以我在 int 上设置了我的服务,并且我能够从那里使用 curl 与实时环境中的另一个服务设置进行通信。

使用Go跨环境通信时出现问题

我的快速解决方案是确保当我对其他服务进行 GET 时,而不是使用:

https://service.live.me.com

我会使用:

https://service.int.me.com

或:

https://service.test.me.com

根据环境,我的代码在 运行 内。

显然,对于有类似问题但没有相同设置的其他人来说,这不是解决方案。


所以对于那些仍然需要解决方案的人...


我还打算尝试(并且显然为 this guy 工作)是获取我试图与之通信的服务来修改他们的 nginx conf,以便他们将 ssl_client_certificate 设置为不仅指向一个客户端证书,还指向一个 combined 证书(包含整个 CA 链的证书)。

这是因为显然 Go 不会通过其响应发回任何证书除非服务器已通过 CA 及其响应。

我最初怀疑这是 Go 1.6 及更低版本中的经典重新协商错误,但在这种情况下情况并非如此。

希望这对同一条船上的其他人有所帮助。