美文网首页
Go socket服务器使用TLS

Go socket服务器使用TLS

作者: Go语言由浅入深 | 来源:发表于2021-11-09 06:48 被阅读0次

    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: []
    

    相关文章

      网友评论

          本文标题:Go socket服务器使用TLS

          本文链接:https://www.haomeiwen.com/subject/ljqmzltx.html