Complete TLS Workflow in Golang Made Simple: Full Process Explained
Complete TLS Workflow in Golang Made Simple: Full Process Explained
Leapcell: The Best of Serverless Web Hosting
Explanation of the TLS Handshake Process
The TLS (Transport Layer Security) handshake is a vital procedure that enables secure communication between a client (such as a web browser) and a server (such as a web server). Below is a detailed breakdown of the entire TLS handshake process:
Client Hello
The client starts the handshake by sending a "Client Hello" message to the server.
This message contains:
The TLS versions supported by the client.
A list of cipher suites (encryption algorithms) it supports.
A random byte string (referred to as Client Random).
Server Hello
The server replies with a "Server Hello" message.
This message includes:
The selected TLS version.
The chosen cipher suite.
A random byte string (known as Server Random).
The server's digital certificate (issued by a trusted Certificate Authority, CA).
Certificate Verification
The client verifies the server's certificate through the certificate authority (CA) chain.
It ensures that the certificate is valid, not expired, and issued to the correct domain.
Pre - Master Secret Generation
The client generates a "Pre - Master Secret" using the server's public key (extracted from the certificate).
This secret is encrypted and sent to the server.
Master Secret Derivation
Both the client and the server generate the "Master Secret" using the following:
The Client Random.
The Server Random.
The Pre - Master Secret.
The Master Secret is used to derive session keys for encryption and integrity checks.
Session Keys Creation
Using the Master Secret, both parties create:
Encryption keys for symmetric encryption.
MAC (Message Authentication Code) keys for integrity checks.
Client Finished
The client sends a "Finished" message, which is encrypted with the session keys.
This confirms that the handshake was successful and that future messages will be encrypted.
Server Finished
The server sends its own "Finished" message, encrypted with the session keys.
This marks the end of the handshake and the start of encrypted communication.
Data Transfer
All subsequent communication is encrypted using the derived session keys.
Data is sent in encrypted packets with integrity checks.
Diagram of the TLS Handshake Process
+----------------------------------------+ +----------------------------------------+
| Client | | Server |
+----------------------------------------+ +----------------------------------------+
| | | |
| ClientHello |----->| |
| [TLS Version, Cipher Suites, Random] | | |
| | | |
| | | ServerHello |
| |<-----| [TLS Version, Cipher Suite, Random] |
| | | |
| |<-----| Certificate |
| | | [Server's Public Key] |
| | | |
| |<-----| ServerHelloDone |
| | | |
| CertificateVerify | | |
| [Verify Server's Certificate] | | |
| | | |
| ClientKeyExchange |----->| |
| [Encrypted Pre-Master Secret] | | |
| | | |
| ChangeCipherSpec |----->| |
| [Start Using Encryption] | | |
| | | |
| Finished |----->| |
| [Verifies Handshake Integrity] | | |
| | | |
| |<-----| ChangeCipherSpec |
| | | [Start Using Encryption] |
| | | |
| |<-----| Finished |
| | | [Verifies Handshake Integrity] |
| | | |
| Secure Communication |<--->| Secure Communication |
| [Encrypted Data Transfer] | | [Encrypted Data Transfer] |
+----------------------------------------+ +----------------------------------------+
Obtaining the TLS Client Hello Message with GoLang
Here's how to implement a server that captures all ClientHello messages using GoLang:
Certificate Generation
First, generate the necessary SSL certificates:
# Generate a private key
openssl genrsa -out server.key 2048
# Generate a public key (certificate)
openssl req -new -x509 -key server.key -out server.pem -days 3650
Server Implementation
The following is the complete server code for capturing ClientHello information:
package main
import (
"bufio"
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"sync"
"time"
)
type CollectInfos struct {
ClientHellos []*tls.ClientHelloInfo
sync.Mutex
}
var collectInfos CollectInfos
var currentClientHello *tls.ClientHelloInfo
func (c *CollectInfos) collectClientHello(clientHello *tls.ClientHelloInfo) {
c.Lock()
defer c.Unlock()
c.ClientHellos = append(c.ClientHellos, clientHello)
}
func (c *CollectInfos) DumpInfo() {
c.Lock()
defer c.Unlock()
data, err := json.Marshal(c.ClientHellos)
if err != nil {
log.Fatal(err)
}
ioutil.WriteFile("hello.json", data, os.ModePerm)
}
func getCert() *tls.Certificate {
cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Println(err)
return nil
}
return &cert
}
func buildTlsConfig(cert *tls.Certificate) *tls.Config {
cfg := &tls.Config{
Certificates: []tls.Certificate{*cert},
GetConfigForClient: func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
collectInfos.collectClientHello(clientHello)
currentClientHello = clientHello
return nil, nil
},
}
return cfg
}
func serve(cfg *tls.Config) {
ln, err := tls.Listen("tcp", ":443", cfg)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handler(conn)
}
}
func handler(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
log.Println(err)
return
}
fmt.Println(msg)
data, err := json.Marshal(currentClientHello)
if err != nil {
log.Fatal(err)
}
_, err = conn.Write(data)
if err != nil {
log.Println(err)
return
}
}
}
func main() {
go func() {
for {
collectInfos.DumpInfo()
time.Sleep(10 * time.Second)
}
}()
cert := getCert()
if cert != nil {
serve(buildTlsConfig(cert))
}
}
Client Implementation
The corresponding client code is as follows:
func main() {
conn, err := tls.Dial("tcp", "localhost:443", &tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, err = conn.Write([]byte("hello\n"))
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1000)
n, err := conn.Read(buf)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(buf[:n]))
}
This implementation enables you to capture detailed information from ClientHello messages during the TLS handshake process. The server periodically exports this information to a JSON file for analysis.
Leapcell: The Best of Serverless Web Hosting
Finally, recommend a platform that is most suitable for deploying Go services: Leapcell
🚀 Build with Your Favorite Language
Develop effortlessly in JavaScript, Python, Go, or Rust.
🌍 Deploy Unlimited Projects for Free
Only pay for what you use—no charges when there are no requests.
⚡ Pay - as - You - Go, No Hidden Costs
No idle fees, just seamless scalability.
🔹 Follow us on Twitter: @LeapcellHQ