How to Create a HTTPS and TLS Server in Go

HTTPS (HTTP Secure) and TLS (Transport Layer Security) are essential for securing web communications. This guide demonstrates how to create TLS and HTTPS servers in Go, including generating certificates and handling secure connections.

Getting RSA Public and Private Keys

There are two ways to obtain RSA public and private keys: generating self-signed key pairs or obtaining them from a Certificate Authority (CA).

Generating Self-Signed Keys

Self-signed certificates are useful for development and testing but not recommended for production due to lack of trust.

Generate Private Key:

openssl genrsa -out server.key 2048

Generate Self-Signed Public Key (Certificate):

openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650

This creates server.crt (certificate) and server.key (private key).

Obtaining a Free Certificate from a CA

For production, use a trusted CA like Let’s Encrypt, which provides free, automated certificates.

  • Visit Let’s Encrypt for setup instructions.
  • Use tools like Certbot to automate certificate issuance and renewal.

TLS (Transport Layer Security) Server

A TLS server encrypts TCP connections using RSA.

Server Code

package main

import (
    "bufio"
    "crypto/tls"
    "log"
    "net"
)

func main() {
    log.SetFlags(log.Lshortfile)

    cer, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Println(err)
        return
    }

    config := &tls.Config{Certificates: []tls.Certificate{cer}}
    ln, err := tls.Listen("tcp", ":443", config)
    if err != nil {
        log.Println(err)
        return
    }
    defer ln.Close()

    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Println(err)
            continue
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    r := bufio.NewReader(conn)
    for {
        msg, err := r.ReadString('\n')
        if err != nil {
            log.Println(err)
            return
        }

        println(msg)

        n, err := conn.Write([]byte("world\n"))
        if err != nil {
            log.Println(n, err)
            return
        }
    }
}

Client Code

package main

import (
    "crypto/tls"
    "log"
)

func main() {
    log.SetFlags(log.Lshortfile)

    conf := &tls.Config{
        // For self-signed certificates, skip verification
        InsecureSkipVerify: true,
    }

    conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    n, err := conn.Write([]byte("hello\n"))
    if err != nil {
        log.Println(n, err)
        return
    }

    buf := make([]byte, 100)
    n, err = conn.Read(buf)
    if err != nil {
        log.Println(n, err)
        return
    }

    println(string(buf[:n]))
}

HTTPS Server

An HTTPS server runs HTTP over TLS.

Server Code

package main

import (
    "log"
    "net/http"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Write([]byte("This is an example server.\n"))
}

func main() {
    http.HandleFunc("/hello", HelloServer)
    err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

Testing the Client

curl -sL https://localhost:443/hello

For self-signed certificates, add -k to skip verification: curl -k https://localhost:443/hello.

Alternative Deployment: Reverse Proxy

TLS can be handled by a reverse proxy like Nginx, allowing the Go backend to run on HTTP.

  • Configure Nginx to terminate TLS and proxy requests to your Go server.
  • This simplifies certificate management and allows load balancing.

Important Considerations

  • Security: Never use self-signed certificates in production. Enable HSTS headers for better security.
  • Certificate Renewal: Automate renewal with tools like Certbot for Let’s Encrypt.
  • Error Handling: Always check for errors in TLS operations.
  • Performance: TLS adds overhead; consider session resumption.
  • CORS and Headers: Ensure proper headers for web applications.

Troubleshooting

  • Certificate Errors: Verify paths and permissions for server.crt and server.key.
  • Port Issues: Ensure port 443 is not in use and you have permissions.
  • Client Connection: For testing, use openssl s_client to debug TLS handshakes.

References