Key pinning in Golang

Jun 13, 2016

Key pinning is a technique that can protect clients from rogue or compromised certificate authorities [1, 2, 3]. If you have control over the client and the server, you can bake the server’s public key into the client and bypass (or supplement) trust in certificate authorities.

Many mobile applications on iOS and Android do this using these libraries:

The Chrome and Firefox web browsers also allow pinning with pre-loaded pins and support of the HTTP Public Key Pinning (HPKP) protocol.

There are a number of CLI utilities I use that interact with HTTP APIs, and thought it would be nice if they could support pins. I started looking into how one might do certificate pinning in Golang, and the first option was to add the exact certificate to the tls.Config trust store:

pemData := []byte(`-----BEGIN CERTIFICATE-----
certs := x509.NewCertPool()

client = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			RootCAs: certs,
resp, err := client.Get("")

Or you could inspect the certificate chain following the TLS handshake:

client := &http.Client{}
tlsConfig := &tls.Config{InsecureSkipVerify: true}
client.Transport = &http.Transport{
	DialTLS: func(network, addr string) (net.Conn, error) {
		conn, err := tls.Dial(network, addr, tlsConfig)
		if err != nil {
			return conn, err

		// inspect conn.ConnectionState() here

		return conn, nil

resp, err := client.Get("")

But knowing what to inspect can be tricky. This gist and xmpp-client provide some examples of what to pin, but it is cumbersome to do so on your own.

I couldn’t find any Golang libraries that make key pinning any easier, so I decided to start my own library for writing HPKP aware clients. This library is aimed at providing:

  1. HPKP related tools (generate pins, inspect servers)
  2. A convenience functions for writing clients that support pin verification

To inspect the HPKP headers from the server:

$ hpkp-headers

And generate pins from the certs a server presents:

$ hpkp-pins

Or generate a pin from a PEM-encoded certificate file:

$ hpkp-pins -file=cert.pem

And finally, how to use the hpkp package to verify pins as part of your application:

s := hpkp.NewMemStorage()

s.Add("", &hpkp.Header{
    Permanent: true,
    Sha256Pins: []string{

client := &http.Client{}
client.Transport = &http.Transport{
    DialTLS: hpkp.PinOnlyDialer(s),
resp, err := client.Get("")

If you’re interested in using or improving this package please check it out on Github.