53. Configuring TLS Certificates with Golang
title: “53. Configuring TLS Certificates with Golang” date: 2024-06-20 tags: [golang, tls, security, certificate, programming]
TLS (Transport Layer Security) has become the primary standard for securing communication on the internet. With cyber threats on the rise, making sure your application’s connections run over an encrypted protocol is absolutely critical. Golang, as a modern and efficient language, provides a robust native library for integrating TLS into our applications.
In this 53rd article of the “Learning Golang” series, I’ll walk through how to configure TLS certificates in a Go application. We’ll dive into implementing it on both the server and client sides, along with tips for certificate management and TLS handshake monitoring. As a bonus at the end, I’ve included a reference table and a TLS handshake flow diagram using mermaid.
Why Does TLS Matter?
TLS protects data in transit through two mechanisms:
- Encryption — Prevents third parties from snooping on your data.
- Authentication — Ensures communication only happens between the intended parties.
A proper TLS configuration in a Go application is a solid foundation for building modern backend systems.
A Simple TLS Architecture
TLS Handshake Flow Diagram (mermaid)
sequenceDiagram
autonumber
participant Client
participant Server
Client->>Server: ClientHello
Server->>Client: ServerHello
Server->>Client: Certificate
Server->>Client: ServerHelloDone
Client->>Server: ClientKeyExchange
Client->>Server: ChangeCipherSpec
Client->>Server: Finished
Server->>Client: ChangeCipherSpec
Server->>Client: Finished
Note over Client,Server: Encrypted communication starts
The diagram above shows the most common TLS handshake – the phase where the client and server exchange information, validate certificates, and then establish encrypted communication.
Preparation: Generating a Self-Signed Certificate
For local testing and early integration, we typically use self-signed certificates. In a production deployment, do not use self-signed certificates – use ones issued by a trusted CA such as Let’s Encrypt.
Creating the Certificate and Private Key:
1openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
2 -keyout server.key -out server.crt \
3 -subj "/CN=localhost"server.key: Private keyserver.crt: Public certificate
Configuring TLS on a Go Server
Let’s jump straight into a code example!
1package main
2
3import (
4 "crypto/tls"
5 "log"
6 "net/http"
7)
8
9func helloHandler(w http.ResponseWriter, r *http.Request) {
10 w.Write([]byte("Hello over TLS!"))
11}
12
13func main() {
14 mux := http.NewServeMux()
15 mux.HandleFunc("/", helloHandler)
16
17 // load the certificate and private key
18 cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
19 if err != nil {
20 log.Fatalf("Failed to load certificate: %v", err)
21 }
22
23 // Configure TLS explicitly
24 tlsConfig := &tls.Config{
25 Certificates: []tls.Certificate{cert},
26 MinVersion: tls.VersionTLS12, // Only accept TLS >= 1.2
27 CipherSuites: []uint16{
28 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
29 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
30 },
31 }
32
33 server := &http.Server{
34 Addr: ":8443",
35 Handler: mux,
36 TLSConfig: tlsConfig,
37 }
38
39 log.Println("TLS server running at https://localhost:8443")
40 if err := server.ListenAndServeTLS("", ""); err != nil {
41 log.Fatalf("Failed to start server: %v", err)
42 }
43}Highlights:
- Loading the certificate with
tls.LoadX509KeyPair(PEM file processing). MinVersionenforces security with a minimum of TLS 1.2.CipherSuitescan be customized to match your company’s compliance requirements.
Configuring a TLS Client
One of Go’s strengths is how easy it is to make custom HTTPS requests. Here’s an example client that trusts a custom CA (for example, for a self-signed certificate).
1package main
2
3import (
4 "crypto/tls"
5 "crypto/x509"
6 "io/ioutil"
7 "log"
8 "net/http"
9)
10
11func main() {
12 // Load CA certificate
13 caCert, err := ioutil.ReadFile("server.crt")
14 if err != nil {
15 log.Fatalf("Failed to read CA: %v", err)
16 }
17 caPool := x509.NewCertPool()
18 caPool.AppendCertsFromPEM(caCert)
19
20 tlsConfig := &tls.Config{
21 RootCAs: caPool,
22 MinVersion: tls.VersionTLS12,
23 }
24 transport := &http.Transport{TLSClientConfig: tlsConfig}
25
26 client := &http.Client{Transport: transport}
27 resp, err := client.Get("https://localhost:8443")
28 if err != nil {
29 log.Fatalf("HTTP GET failed: %v", err)
30 }
31 defer resp.Body.Close()
32
33 body, _ := ioutil.ReadAll(resp.Body)
34 log.Printf("Response: %s", body)
35}Table: Commonly Used TLS Configuration Options
| Property | Description | Example |
|---|---|---|
| Certificates | X.509 certificate + private key | []tls.Certificate{cert} |
| MinVersion | Minimum accepted TLS version | tls.VersionTLS12 |
| CipherSuites | List of allowed cipher suites | []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} |
| ClientAuth | Client certificate requirement for mutual TLS | tls.RequireAndVerifyClientCert |
| RootCAs | Trusted CAs (client side) | A pool from x509.CertPool |
| PreferServerCipherSuites | Prioritize the server’s cipher preference | true |
Bonus: Mutual TLS (mTLS)
For B2B applications that need extra authentication (for example, internal APIs between services), mutual TLS is the solution.
mTLS Flow Diagram
sequenceDiagram
Client->>Server: ClientHello
Server->>Client: ServerHello + RequestClientCertificate
Client->>Server: ClientCertificate + ClientKeyExchange
Server->>Client: ServerCertificate,ChangeCipherSpec,Finished
Client->>Server: ChangeCipherSpec,Finished
Note over Client,Server: Both parties validate each other's certificates
To enable mTLS in Golang:
1tlsConfig := &tls.Config{
2 Certificates: []tls.Certificate{serverCert},
3 ClientAuth: tls.RequireAndVerifyClientCert, // Require a client certificate!
4 ClientCAs: clientCAPool, // Pool of trusted client CAs
5}Debugging and Monitoring TLS
Use TLS handshake logging in Go:
1os.Setenv("GODEBUG", "tls13=1,tls13keylogfile=./keylog.txt")This produces a log that can be inspected via Wireshark to analyze encrypted traffic by reading the session key.
Conclusion
Configuring TLS certificates in Go is both practical and powerful, whether for small applications or enterprise systems. Start by understanding certificate processing, minimize your exposure with a high MinVersion, and harden security further with mTLS. Go provides a full suite of enterprise-grade security tooling while staying simple to use — as long as we write the configuration correctly.
Do you have a unique experience with TLS in Go? Share it in the comments!
See you in the next post. If this article helped you, support me with a clap on Medium! 🚀