TLS

Go

Certificates

The certificates used in this section were discussed in the section TL: General . They are

Client talking to CA validated server

A Go client talking to an HTTPS server with a valid certificate is TLSGetHead.go:


/* TLSGetHead
 */
package main

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	service := "www.verisign.com:443"

	conn, err := tls.Dial("tcp", service, nil)
	checkError(err)

	_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
	checkError(err)

	result, err := ioutil.ReadAll(conn)
	checkError(err)

	fmt.Println(string(result))

	conn.Close()
	os.Exit(0)
}

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}

When run by


      go run TLSGetHead.go
  
it produces essentially the same results as the Java client above.

Client and server using private CA

If the server uses its own CA, then it needs to load its CA signed certificate and set this into its TLS configuration. It can then listen for client connections.

The server is TLSEchoServer.go:


/* TLSEchoServer
 */
package main

import (
	"crypto/rand"
	"crypto/tls"
	"fmt"
	"net"
	"os"
	"time"
)

func main() {

	cert, err := tls.LoadX509KeyPair("../certs/CA-signed-cert.pem", "../certs/key.pem")
	//cert, err := tls.LoadX509KeyPair("../certs/self-signed-cert.pem", "../certs/key.pem")
	checkError(err)
	config := tls.Config{Certificates: []tls.Certificate{cert}}

	now := time.Now()
	config.Time = func() time.Time { return now }
	config.Rand = rand.Reader

	service := "0.0.0.0:1200"

	listener, err := tls.Listen("tcp", service, &config)
	checkError(err)
	fmt.Println("Listening")
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println(err.Error())
			continue
		}
		fmt.Println("Accepted")
		go handleClient(conn)
	}
}

func handleClient(conn net.Conn) {
	defer conn.Close()

	var buf [512]byte
	for {
		fmt.Println("Trying to read")
		n, err := conn.Read(buf[0:])
		if err != nil {
			fmt.Println(err)
			return
		}
		_, err = conn.Write(buf[0:n])
		if err != nil {
			return
		}
	}
}

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}

The client that talks to this server needs to have the private CA certificate. It reads this and then appends it to a new CertificatePool. If successful, it can then read and write to a server using certificates signed by that CA.

The client is TLSEchoClient.go:


/* TLSEchoClient
 */
package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"os"
)

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: ", os.Args[0], "host:port")
		os.Exit(1)
	}
	service := os.Args[1]

	certPemFile, err := os.Open("../certs/CA-cert.pem")
	//certPemFile, err := os.Open("../certs/self-signed-cert.pem")
	checkError(err)
	pemBytes := make([]byte, 10000) // bigger than the file
	_, err = certPemFile.Read(pemBytes)
	checkError(err)
	certPemFile.Close()

	// Create a new certificate pool
	certPool := x509.NewCertPool()
	// and add our certificate
	ok := certPool.AppendCertsFromPEM(pemBytes)
	if !ok {
		fmt.Println("PEM read failed")
	} else {
		fmt.Println("PEM read ok")
	}

	// Dial, using a config with root cert set to ours
	conn, err := tls.Dial("tcp", service, &tls.Config{RootCAs: certPool})
	checkError(err)

	// Now write and read lots
	for n := 0; n < 10; n++ {
		fmt.Println("Writing...")
		conn.Write([]byte("Hello " + string(n+48)))

		var buf [512]byte
		n, err := conn.Read(buf[0:])
		checkError(err)

		fmt.Println(string(buf[0:n]))
	}
	conn.Close()
	os.Exit(0)
}

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}

Client and server using self-signed certificates

The client and server in the last section had lines to load the appropriate certificates from the private CA. Following these were commented lines to load the self-signed certificates instead. If these are uncommented and the others commented out, they both talk to each other using the self-signed certificates. No other changes are needed.

Certificate chains

If you get a certificate request signed by a CA, you will probably get two certificates

If you want to build your own server, you need to concatenate these two files into a single one for the call to tls.LoadX509KeyPair(). This can be done by e.g.


    cat <signed-cert> <intermediate-cert> &tg; combined-cert
  
That is all that is needed, and go will do the rest. (In my case, the signed certificate did not have a newline terminator so I had to add one using an editor - an advantage of PEM format!).


Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia" by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/NetworkProgramming/ .

If you like this book, please contribute using PayPal