【问题标题】:TLS : Handshake Failure Using GoLang tls clientTLS:使用 GoLang tls 客户端的握手失败
【发布时间】:2020-02-14 21:27:35
【问题描述】:

我正在尝试使用 golang http/tsl 客户端通过 SSL/TLS 连接到服务器,这会导致“Handshake Faliure(40)”错误,但由于某种原因,这个相同的端点适用于 CURL 命令。经过一番调试,我收集到如下数据。

func PrepCerts(certMap map[string]string) (*http.Transport, bool) {
         ok := false
         tlsConfig := &tls.Config{}

         if len(certMap["ca"]) > 0 {
             caCert, err := ioutil.ReadFile(certMap["ca"])
             fmt.Println("caCert : ", caCert)

             if err != nil {
             log.Fatal(err)
          } else {
             caCertPool := x509.NewCertPool()
             caCertPool.AppendCertsFromPEM(caCert)
             (*tlsConfig).RootCAs = caCertPool
             ok = true
          }
     }

     if len(certMap["cert"]) > 0 && len(certMap["key"]) > 0 {
         cert, err := tls.LoadX509KeyPair(certMap["cert"], certMap["key"])
         fmt.Println("cert : ", cert)

         if err != nil {
            log.Fatal(err)
         } else {
            (*tlsConfig).Certificates = []tls.Certificate{cert}
            ok = true
         }
     }

     tlsConfig.BuildNameToCertificate()
     return &http.Transport{TLSClientConfig: tlsConfig}, ok
  }

使用上述函数的代码

  client := &http.Client{
      Timeout: timeout,
  }

  //certMap = map[string]string{
  // ca : "filelocation",
  // cert : "filelocation",
  // key " "filelocation",
  //}

  if transport, ok := PrepCerts(certMap); ok {
      (*client).Transport = transport
  }
  resp, err := client.Do(req)

【问题讨论】:

  • "大密码,请!"如果我们看不到正在发生的事情,我们甚至应该如何开始验证和验证。握手失败 40 似乎表明服务器和客户端无法就密码套件达成一致。奇怪...
  • @MarkusWMahlberg - “服务器问候”数据包(第 5 张图片)表示他们同意 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384。如果您需要,我仍然可以添加代码?
  • @MarkusWMahlberg - 我添加了代码,希望对您有所帮助。
  • @MarkusWMahlberg:“握手失败 40 似乎表明服务器和客户端无法就密码套件达成一致” - 警报 40 只是一般的“握手失败”。当找不到共享密码时,它通常从服务器发送到客户端,因为没有特定的警报类型,但不限于这种情况。见RFC 5246 section 7.2
  • 如果客户端从证书请求中发送了 0 个证书,则意味着没有兼容的签名方案,或者证书不是由可接受的 CA 之一颁发的。检查证书请求信息的最简单方法是使用GetClientCertificate 加载证书。

标签: go ssl tls1.2 go-http


【解决方案1】:

从捕获的数据包中可以看出,服务器正在向客户端(Certificate Request)请求证书。从问题中包含的详细图像还可以看出,客户端没有发送任何证书(Certificate 记录与Certificate Length 0)。

还可以看到,在客户端发送(可能为空)证书后,服务器会发出警报并发出警告,因此它可能不喜欢客户端发送的内容。因此,同意密码肯定不是问题(服务器已经同意),也不是客户端不喜欢服务器证书的问题(警报是由服务器而不是客户端发送的)。

根据您的代码,您正在尝试使用客户端证书做某事,但基于 pcap,您似乎没有成功使用它。所以哪里有问题。

【讨论】:

  • 在最后第二张图片中,TLSv1.2:记录层:握手协议:证书部分 - 证书长度为 0,我们可以从中得出没有证书通过的结论吗?
  • @utkarshsharma:你说得对,我没有看到这个细节。我已经修改了这个问题。
【解决方案2】:

正如 Github 上的 FiloSottile 所述。

我认为这里发生的是证书请求应用了您的证书不满足的约束(RSA 与 ECDSA 或特定颁发者)。

使用他的建议,您可以覆盖客户端传输tls.Config.GetClientCertificate()) 方法。

在遵循此建议后,我得出结论,Go 不会在响应证书请求数据包时同时显示 tls.Config.RootCAstls.Config.Certificates

要解决此问题,请在调用 x509.LoadX509KeyPair() 之前将客户端证书及其 CA 捆绑包合并到一个文件中。注意文件中证书的顺序很重要。如果客户端证书不是捆绑包中的第一个证书,您将收到 tls: private key does not match public key 错误。

一旦您将客户端证书与其 CA 捆绑包组合在一起,您就可以像这样将它们加载到您的客户端中。

package main

import (
    "crypto/tls"
    "io/ioutil"
    "net/http"
    "time"

    log "github.com/sirupsen/logrus"
)

const (
    certFile = "/location/of/client_cert.bundle.pem"
    keyFile  = "/location/of/client_cert.key.pem"
    testURL  = "https://mtls-site"
)

func main() {
    clientCert, err := tls.LoadX509KeyPair(certFile, keyFile)
    if err != nil {
        panic(err)
    }

    client := http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                Certificates: []tls.Certificate{clientCert},
            },
        },
    }

    resp, err := client.Get(testURL)
    if err != nil {
        panic(err)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    log.Infof("%s %s", resp.Status, body)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-28
    • 1970-01-01
    • 2019-08-18
    • 2015-12-08
    • 1970-01-01
    • 2019-12-16
    • 2015-01-04
    • 1970-01-01
    相关资源
    最近更新 更多