程序员社区

Go socket服务器使用TLS

TLS(以前称为SSL)最著名的功能是启用HTTPS,HTTP的安全版本。然而,正如TLS的名字(传输层安全)所暗示的那样,它实际上比HTTP更深入。TLS被认为是TCP的安全版本;换句话说,只要是通过套接字来实现通信的协议都可以使用TLS来实现安全通信,比如gRPC也可以基于TLS来实现安全通信。

在之前的一篇文章中,我们已经看到了如何使用TLS在Go中设置HTTPS服务器和客户端。本文,将展示如何创建加密的套接字服务器,它可以作为其他网络协议的基础。这篇文章的所有代码都可以在GitHub上找到。

TLS 套接字服务器端

下面是一个使用TLS的简单socket服务器:

func main() {
  port := flag.String("port", "4040", "listening port")
  certFile := flag.String("cert", "cert.pem", "certificate PEM file")
  keyFile := flag.String("key", "key.pem", "key PEM file")
  flag.Parse()

  cert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
  if err != nil {
    log.Fatal(err)
  }
  config := &tls.Config{Certificates: []tls.Certificate{cert}}

  log.Printf("listening on port %s\n", *port)
  l, err := tls.Listen("tcp", ":"+*port, config)
  if err != nil {
    log.Fatal(err)
  }
  defer l.Close()

  for {
    conn, err := l.Accept()
    if err != nil {
      log.Fatal(err)
    }
    log.Printf("accepted connection from %s\n", conn.RemoteAddr())

    go func(c net.Conn) {
      io.Copy(c, c)
      c.Close()
      log.Printf("closing connection from %s\n", conn.RemoteAddr())
    }(conn)
  }
}

上面的代码接收来自客户端的多个(并发)连接,并返回客户端发送的所有数据,直到客户端套接字关闭。它与非tls服务器的代码非常相似,除了net.Listen被tls.Listen取代了,后者需要tls.Config(已经在前一篇文章中遇到过)。可以使用前文中描述的证书生成工具,或者mkcert工具为这个服务器生成一个证书/密钥对。

TLS套接字客户端

func main() {
  port := flag.String("port", "4040", "port to connect")
  certFile := flag.String("certfile", "cert.pem", "trusted CA certificate")
  flag.Parse()

  cert, err := os.ReadFile(*certFile)
  if err != nil {
    log.Fatal(err)
  }
  certPool := x509.NewCertPool()
  if ok := certPool.AppendCertsFromPEM(cert); !ok {
    log.Fatalf("unable to parse cert from %s", *certFile)
  }
  config := &tls.Config{RootCAs: certPool}

  conn, err := tls.Dial("tcp", "localhost:"+*port, config)
  if err != nil {
    log.Fatal(err)
  }

  _, err = io.WriteString(conn, "Hello simple secure Server\n")
  if err != nil {
    log.Fatal("client write error:", err)
  }
  if err = conn.CloseWrite(); err != nil {
    log.Fatal(err)
  }

  buf := make([]byte, 256)
  n, err := conn.Read(buf)
  if err != nil && err != io.EOF {
    log.Fatal(err)
  }

  fmt.Println("client read:", string(buf[:n]))
  conn.Close()
}

同样,与非tls客户端的区别只是将net.Dial替换成tls.Dial,并附带tls.Config配置,添加客户端可以信任的证书(这可以是服务器自己的证书,也可以是权威机构签署的CA证书等等)。

查看服务器证书

下面是一个简单的程序,可以用来检查任何服务器的证书:

func main() {
  addr := flag.String("addr", "localhost:4040", "dial address")
  flag.Parse()

  cfg := tls.Config{}
  conn, err := tls.Dial("tcp", *addr, &cfg)
  if err != nil {
    log.Fatal("TLS connection failed: " + err.Error())
  }
  defer conn.Close()

  certChain := conn.ConnectionState().PeerCertificates
  for i, cert := range certChain {
    fmt.Println(i)
    fmt.Println("Issuer:", cert.Issuer)
    fmt.Println("Subject:", cert.Subject)
    fmt.Println("Version:", cert.Version)
    fmt.Println("NotAfter:", cert.NotAfter)
    fmt.Println("DNS names:", cert.DNSNames)
    fmt.Println("")
  }
}

给定一个IP地址,该程序与对应服务端建立TLS连接,并打印它使用的证书。我们可以先在自己的TLS套接字服务器上试试;注意,该程序没有任何预信任证书,因此它将拒绝自签名证书。但是,如果我们使用mkcert为服务器生成证书,它就可以工作了。
打开终端,使用mkcert生成的证书来运行TLS套接字服务器:

$ mkcert localhost

Created a new certificate valid for the following names ?
 - "localhost"

The certificate is at "./localhost.pem" and the key at "./localhost-key.pem" ✅

It will expire on 7 July 2023 ?

$ go run tls-socket-server.go -cert localhost.pem -key localhost-key.pem
2021/04/07 06:27:20 listening on port 4040

在另一个终端,运行前面的代码:

$ go run tls-dial-port.go -addr localhost:4040
0
Issuer: CN=mkcert eliben@salami (Eli Bendersky),OU=eliben@salami (Eli Bendersky),O=mkcert development CA
Subject: OU=eliben@salami (Eli Bendersky),O=mkcert development certificate
Version: 3
NotAfter: 2023-07-07 13:27:12 +0000 UTC
DNS names: [localhost]

可以看到生成的证书mkcert。由于mkcert将此证书添加到系统根存储,因此tls.Dial默认使用该证书。

可以将该程序访问其他公共网站的443端口(HTTPS的默认端口);例如:

➜  go run tls-dial-port.g -addr baidu.com:443 
0
Issuer: CN=DigiCert Secure Site Pro CN CA G3,O=DigiCert Inc,C=US
Subject: CN=www.baidu.cn,O=BeiJing Baidu Netcom Science Technology Co.\, Ltd,ST=北京市,C=CN
Version: 3
NotAfter: 2022-02-24 23:59:59 +0000 UTC
DNS names: [www.baidu.cn baidu.cn baidu.com baidu.com.cn w.baidu.com ww.baidu.com www.baidu.com.cn www.baidu.com.hk www.baidu.hk www.baidu.net.au www.baidu.net.ph www.baidu.net.tw www.baidu.net.vn wwww.baidu.com wwww.baidu.com.cn]

1
Issuer: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
Subject: CN=DigiCert Secure Site Pro CN CA G3,O=DigiCert Inc,C=US
Version: 3
NotAfter: 2030-03-13 12:00:48 +0000 UTC
DNS names: []
赞(0) 打赏
未经允许不得转载:IDEA激活码 » Go socket服务器使用TLS

一个分享Java & Python知识的社区