简体   繁体   中英

TLS : Handshake Failure Using GoLang tls client

I'm trying to connect to a server over SSL/TLS using golang http/tsl client which is resulting in 'Handshake Faliure(40)' error, but for some reason, this same endpoint works with CURL command. After some debugging, I have collected the following data.

Openssl 命令输出 Wireshark 数据包

客户你好 客户你好 2 服务器你好 更改密码规范 握手失败

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
  }

Code that uses above function

  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)

From the captured packets it can be seen that the server is requesting a certificate from the client ( Certificate Request ). From the the detailed images included in the question it can also be seen that no certificates are sent by the client ( Certificate record with Certificate Length 0).

What can also be seen is that the server complains with an Alert after the client has send the (possible empty) certificate so it likely does not like what the client has sent. So it is for sure not a problem of agreeing to a cipher (server agreed on one already) and not a problem that the client does not like the servers certificate (alert is send by server not client).

Based on your code you are trying to do something with client certificates but based on the pcap it looks like you don't succeed in using one. So somewhere there is the problem.

As stated by FiloSottile on Github.

What I think is happening here is that the Certificate Request applies constraints (RSA vs ECDSA, or a specific issuer) which are not satisfied by your certificate.

Using his suggestions you can override the client transports tls.Config.GetClientCertificate() ) method.

After following this advice I came to the conclusion that Go will not present tls.Config.RootCAs along with tls.Config.Certificates in response to a certificate request packet.

To solve this issue combine the client certificate and its CA bundle into a single file before calling x509.LoadX509KeyPair() . Taking note that the order of certificates in the file matters. If the client certificate isn't the first one in the bundle you will get a tls: private key does not match public key error.

Once you have combined the client certificate with its CA bundle you can load them into your client like so.

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)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM