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.crtandserver.key. - Port Issues: Ensure port 443 is not in use and you have permissions.
- Client Connection: For testing, use
openssl s_clientto debug TLS handshakes.