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

Posted

技术标签:

【中文标题】如何在不强制断开连接的情况下使用 Go TLS 手动验证客户端证书?【英文标题】:How can client-certificate be manually validated with Go TLS without forcing disconnection? 【发布时间】:2017-07-28 14:20:03 【问题描述】:

我们正在使用 Golang 构建一个通过 SSL 打开 TCP 端口的服务器。

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

请明确一点:我们坚持要求通过 SSL 将数据返回给未能与服务器进行相互身份验证的客户端。我们不想断开它们。

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

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

我们怎样才能知道:

    是否提供了客户端证书? 如果是,它是否通过了 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.Certificatecert,
    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())

下面是我们客户端的代码:

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.Certificatecert,
    RootCAs:      caCertPool,
  
  config.BuildNameToCertificate()
  // Connect to server
  conn, _ := tls.Dial("tcp", "127.0.0.1:443", config)
  defer conn.Close()

输出:

0 false false 0  false  [] [] [] [] []

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

提前谢谢你。感谢您抽出宝贵时间帮助我们。

亲切的问候,

朱利安

【问题讨论】:

我不知道 Go 但有三种情况需要考虑,这应该告知您的“坚定要求”:(1)客户端没有证书; (2) 客户端没有由服务器接受的签名者签名的证书,在这种情况下它不会发送它; (3)客户端确实有这样的证书,并发送了它,但它是无效/过期/撤销/无论如何。您不应该希望对这三种情况一视同仁。 你在这方面有什么进展吗? 【参考方案1】:

我也度过了我最好的下午。

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

你可以看一个例子here。

【讨论】:

您好,乔治,感谢您的回复。我试过打电话给tlscon.Handshake(),但服务器报告remote error: tls: bad certificate。在您的示例中,我看不到您加载客户端证书的位置?

以上是关于如何在不强制断开连接的情况下使用 Go TLS 手动验证客户端证书?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我可以键入别名函数并在不强制转换的情况下使用它们?

如何在不使用 c# 中的内置函数的情况下获得投球手的最低和最高分数

如何在不确认的情况下强制 cp 覆盖

C#检查套接字是不是断开连接?

在不物理断开的情况下断开外部附件

如何在不通过 jQuery 提交的情况下强制进行 html5 表单验证