如何在不强制断开连接的情况下使用 Go TLS 手动验证客户端证书?

jsc*_*jsc 6 ssl go tls1.2

我们正在 Golang 中构建一个服务器,它通过 SSL 打开一个 TCP 端口。

我们希望在客户端和服务器之间启用相互身份验证。但还要检测客户端何时在没有有效客户端证书的情况下尝试连接到我们的服务器,并通过 SSL 向他们返回错误消息 - 例如“检测到无效的客户端证书,请联系 ABC 公司寻求帮助”。

需要明确的是:我们坚持要求通过 SSL 将数据返回给无法与服务器进行相互验证的客户端。我们不想断开它们。

我们采用的方法是使用 TLS 的“VerifyClientCertIfGiven”配置设置。

因此,如果提供了客户端证书,我们将对其进行验证,但如果没有,我们仍然允许建立 SSL 连接。

我们怎样才能知道:

  1. 是否提供了客户端证书?
  2. 如果是,它是否通过了 TLS 执行的相互身份验证检查?

下面是我们服务器的代码:

package main

import(
  "fmt"
  "io/ioutil"
  "crypto/tls"
  "crypto/x509"
)

func main(){
  // Configure SSL
  cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
  caCert, _ := ioutil.ReadFile("client.crt")
  caCertPool := x509.NewCertPool()
  caCertPool.AppendCertsFromPEM(caCert)
  config := &tls.Config{
    Certificates: []tls.Certificate{cert},
    ClientCAs: caCertPool,
    ClientAuth: tls.VerifyClientCertIfGiven,
  }
  config.BuildNameToCertificate()
  // Listen on port 443
  listener, _ := tls.Listen("tcp", ":443", config)
  defer listener.Close()
  // Accept incoming connection
  conn, _ := listener.Accept()
  defer conn.Close()
  // Print ConnectionState
  tlscon, _ := conn.(*tls.Conn)
  fmt.Println(tlscon.ConnectionState())
}
Run Code Online (Sandbox Code Playgroud)

以下是我们客户的代码:

package main
import (
  "io/ioutil"
  "crypto/tls"
  "crypto/x509"
)

func main(){
  //Configure SSL
  cert, _ := tls.LoadX509KeyPair("client.crt", "client.key"
  caCert, _ := io.util.ReadFile("server.crt")
  caCertPool := x509.NewCertPool()
  caCertPool.AppendCertsFromPEM(caCert)
  config := &tls.Config{
    Certificates: []tls.Certificate{cert},
    RootCAs:      caCertPool,
  }
  config.BuildNameToCertificate()
  // Connect to server
  conn, _ := tls.Dial("tcp", "127.0.0.1:443", config)
  defer conn.Close()
}
Run Code Online (Sandbox Code Playgroud)

输出:

{0 false false 0  false  [] [] [] [] []}
Run Code Online (Sandbox Code Playgroud)

我们已经尝试在服务器上实现 conn.ConnectionState().PeerCertificates 但在我们所有的尝试中,它都是一个空字节数组。

先感谢您。我们感谢您花时间帮助我们。

亲切的问候,

朱利安

Geo*_*dis 0

我也在这件事上度过了最美好的一个下午。

一旦服务器接受连接,您将需要显式调用tlscon.Handshake()以获取客户端的证书,否则 tls 包将在第一次 i/o 之后自动执行此操作。https://golang.org/pkg/crypto/tls/#Conn.Handshake

您可以在此处查看示例。