How to Build TLS-Encrypted Socket Server and Client in Go

This article explains the fundamentals of TLS as a transport‑layer security protocol, demonstrates how to create a TLS‑encrypted socket server and client in Go, and shows how to generate and verify certificate chains using tools like mkcert.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
How to Build TLS-Encrypted Socket Server and Client in Go

TLS (formerly SSL) is a Transport Layer Security protocol that operates independently of HTTP; it can be seen as a secure version of TCP, providing encryption and signing for socket communication. In everyday development, protocols such as gRPC are often run over TLS to ensure security.

Below we demonstrate how to create a TLS‑encrypted socket service in Go.

1. TLS socket server

Server example:

func main() {
  port := flag.String("port", "8360", "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
", *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
", conn.RemoteAddr())
    go func(c net.Conn) {
      io.Copy(c, c)
      c.Close()
      log.Printf("closing connection from %s
", conn.RemoteAddr())
    }(conn)
  }
}

The server accepts concurrent client connections and echoes received data. Compared with a non‑TLS server, the only change is replacing net.Listen with tls.Listen and providing a tls.Config. Certificates and keys can be generated with the mkcert command.

2. TLS socket client

Client example:

func main() {
  port := flag.String("port", "8360", "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
")
  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()
}

The client replaces net.Dial with tls.Dial and supplies a tls.Config containing trusted root CAs. The certificates can be CA‑signed or self‑signed.

3. Certificate chain

Typically a CSR is submitted to a signing authority; an intermediate CA signs it, and the Root CA signs the intermediate’s CSR, forming a hierarchical chain. When validating a certificate, the verifier walks up the chain until it reaches a trusted root certificate.

Program to inspect any server’s certificate chain:

func main() {
  addr := flag.String("addr", "localhost:8360", "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("")
  }
}

If a self‑signed certificate is used without proper trust, the TLS server verification will fail. Therefore a certificate issued by a trusted CA or one generated with mkcert (which adds the certificate to the system’s root store) should be used.

Generate a certificate with mkcert:

➜  ./mkcert localhost

➜  go run tls-socket-server.go -cert localhost.pem -key localhost-key.pem

In another terminal, run the client:

➜  go run tls-dial-port.go -addr localhost:4040

Because mkcert adds the generated certificate to the system’s trusted store, tls.Dial will automatically trust it.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TLSCertificateSocketmkcert
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.