TLS:使用GoLang tls客户端的握手失败

时间:2019-10-17 22:18:31

标签: go ssl tls1.2 go-http

我正在尝试使用golang http / tsl客户端通过SSL / TLS连接到服务器,这会导致“握手失败(40)”错误,但是由于某些原因,该端点可以与CURL命令一起使用。经过一些调试后,我收集了以下数据。

Openssl command output Wireshark packets

client hello client hello 2 server hello Change Cipher Spec Handshake Failure

boost::hex

使用上述功能的代码

boost::unhex

2 个答案:

答案 0 :(得分:2)

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

还可以看到,在客户端发送(可能为空)证书之后服务器抱怨警报,因此服务器可能不喜欢客户端发送的证书。因此,可以肯定的是,同意密码(服务器已同意一个密码)不是问题,也不是客户端不喜欢服务器证书(警报是由服务器而非客户端发送的)的问题。

基于您的代码,您正在尝试使用客户端证书进行某些操作,但是基于pcap,您似乎无法成功使用它。所以哪里有问题。

答案 1 :(得分:1)

FiloSottile 在 Github 上所述。

<块引用>

我认为这里发生的是证书请求应用了您的证书不满足的约束(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)
}