Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
31 Jul 2025 · 5 min read ·Article 53 / 110
Go

53. Configuring TLS Certificates with Golang

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

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:

  1. Encryption — Prevents third parties from snooping on your data.
  2. 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)

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:

bash
1openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
2    -keyout server.key -out server.crt \
3    -subj "/CN=localhost"
  • server.key: Private key
  • server.crt: Public certificate

Configuring TLS on a Go Server

Let’s jump straight into a code example!

go
 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).
  • MinVersion enforces security with a minimum of TLS 1.2.
  • CipherSuites can 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).

go
 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}
Danger
Note: In a production environment, you should use a proper CA. The code above assumes a self-signed certificate for experimentation.


Table: Commonly Used TLS Configuration Options

PropertyDescriptionExample
CertificatesX.509 certificate + private key[]tls.Certificate{cert}
MinVersionMinimum accepted TLS versiontls.VersionTLS12
CipherSuitesList of allowed cipher suites[]uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
ClientAuthClient certificate requirement for mutual TLStls.RequireAndVerifyClientCert
RootCAsTrusted CAs (client side)A pool from x509.CertPool
PreferServerCipherSuitesPrioritize the server’s cipher preferencetrue

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

MERMAID
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:

go
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}
This is commonly used in banking systems, P2P APIs, and CI/CD automation scenarios.


Debugging and Monitoring TLS

Use TLS handshake logging in Go:

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.

Danger
Tip: For production, set up automated renewal via ACME (Let’s Encrypt), and always audit your TLS options at least every 6 months to keep up with best-practice updates.

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! 🚀

Related Articles

💬 Comments