TLS Config in Golang

Posted on

Below is an example of how to generate a private key, private key, and the root CA certificate.

Let us become a CA (Certificate Authority) by creating our CA private key, called rootCA.key

openssl genrsa -des3 -out rootCA.key 2048

Now using that private key we can create our Root CA Certificate called rootCA.pem

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1825 -out rootCA.pem

Let us now create the keys for our client (we will be using the rootCA above to generate these)

Let us create our clients private key

openssl genrsa -out client.key 2048

Create a Certificate Signing Request for our client

openssl req -new -key client.key -out client.csr

Sign our Certificate Signing Request with our rootCA to give us our Certificate

openssl x509 -req -in client.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out client.crt -days 825 -sha256

The Go Code

package main

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

var (
	rootCA     = mustOpenAndReadFile("./secrets/rootCA.pem")
	cert       = mustOpenAndReadFile("./secrets/app.crt")
	privateKey = mustOpenAndReadFile("./secrets/app.key")
)

func main() {
	// generate the client certificate, that our app will use with our own certificate and our private key
	clientCert, err := tls.X509KeyPair(cert, privateKey)
	if err != nil {
		panic(err)
	}

	rootCAPool := x509.NewCertPool()
	
	// here we will provide our rootCA, the certificate which was used to create our app cert (see above)
	ok := rootCAPool.AppendCertsFromPEM(rootCA)
	if !ok {
		panic("unable to append supplied cert into tls.Config, are you sure it is a valid certificate")
	}

	config := tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCAPool,
	}

	// to prevent an unused variable
	var _ = config
}

// mustOpenAndReadFile opens and reads the supplied file into memory
// since this is a "must" function, if any part of this op fails,
// FailNow is called as we are unable to proceed with the test
func mustOpenAndReadFile(path string) []byte {
	f, err := os.Open(path)
	if err != nil {
		panic(fmt.Sprintf("unable to open test file %s: %s", path, err))
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		panic(fmt.Sprintf("unable to ReadAll of test file %s: %s", path, err))
	}
	return b
}