Using encrypted private keys with Golang HTTPS server

Yes, this is an Owl, protecting my traffic

Coming from a Java world (I am embarrassed enough), this hit me like a wall. Default golang http.Server does not have a way to accept private keys that are protected by a passphrase. If you look up the documentation for server.ListenAndServerTLS, it needs 2 parameters, the certFile and keyFile strings, that represent the location of PEM encoded format for certificate and private key. There is no option to supply the password !!

Another weird thing I saw is there is field in the http.Server struct, it has a tls.Config parameter that too has a field to configure the tls.Certificate object and that is not just for Certificates but for PrivateKey as well. I thought, why does it need the parameters in server.ListenAndServerTLS when it already has it. I got into the messy part because I can’t read documentation.

func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error {
// Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig
// before we clone it and create the TLS Listener.
if err := srv.setupHTTP2_ServeTLS(); err != nil {
return err
config := cloneTLSConfig(srv.TLSConfig)
if !strSliceContains(config.NextProtos, "http/1.1") {
config.NextProtos = append(config.NextProtos, "http/1.1")
configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil
if !configHasCert || certFile != "" || keyFile != "" {
var err error
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
tlsListener := tls.NewListener(l, config)
return srv.Serve(tlsListener)

It is first checking the TLSConfig that we might have supplied, if not then it would use the supplied cert and key file names.

It occurred to me, why not we try to build the tls.Config.Certificate with the encrypted key file that we have.

Let’s get to some action

How do you load a plain cert and key files to build a Certificate Object

tls.LoadX509KeyPair(certFile, keyFile string) or tls.X509KeyPair(certPEMBlock, keyPEMBlock []byte)

The first one takes file names and second one takes PEM encoded blocks. First is no use, as it is the same as the Server interface. The second one looks interesting. Let’s try to get the private key as a PEM block.

But first, What is PEM encoding. There is a very interesting and intimidating thing called ASN.1 that can be used to serialise structures and cryptographic implementation uses the DER encoding rules to store the ASN.1 structures.

A basic guide for layman can be found here: ASN.1 and surprisingly here: DER encoding for ASN.1

For now just think of those as serialization techniques and know that DER is a binary encoding scheme or it produces binary output. Which is ok to store on disk. But when you have to transfer it over emails, for which PEM (Privacy enhanced mails) was originally built, which is plain text, you need to make it a little simpler.

Here comes the base64 cannon. Another encoding scheme on top of an encoding scheme (I know, right !!). It can represent any binary data as printable characters. eg:

$ echo hello | xxd
00000000: 6865 6c6c 6f0a hello.
$ echo -ne "\x68\x65\x6c\x6c\x6f\x0a" | base64
$ echo "aGVsbG8K" | base64 -D

Yay ! So now you can transmit binary data in a printable format.

PEM is the fancy name for this base64 encoding of DER format. Try this for fun, it’s pure pleasure :D

$ wget
$ cat Root-R1.crt | base64
$ openssl x509 -in Root-R1.crt -inform DER -outform PEM

Look at both of the outputs, They are the same, with a better formatting though. And the


part is according to the RFC which tells about the type of the PEM block.

Coming back to our problem,

If we can get golang a PEM block that has the private key unencrypted, bingo. First let us get the PEM block in golang Native structs.

encoding/pem has a useful function: Decrypt(data []bytes)(p *pem.Block, rest []byte). This function goes through the bytes and tries to find the marker of the PEM block and returns the first block as a pointer to pem.Block

-----BEGIN Type-----
base64-encoded Bytes
-----END Type-----

Let’s try to see how to write this thing.

Not bad, but we still haven’t reached the magic yet.

Another amazing function, a pair actually, that golang’s crypto library provides is

Check if a PEM block is encrypted, if yes, decrypt it. Yes, it’s that awesome.

All you need now is check if the detected Private key block is encrypted, If yes, decrypt it.

And, that was it.

If you would have seen the signature of the tls.X509KeyPair(certPEMBlock, keyPEMBlock []byte) all they need is a PEM block as bytes. And pem.Block already has a Block.Byte Value. What’s this for ?

This is the DER encoding of the ASN.1 structure and we just don’t need bytes in the function, but a well formed PEM block with all the markers and headers (yes, that took me a couple of hours and some facepalms to find out, as I read documentation as carefully as people think about their passwords.)

Now, how do you protect the password to the encrypted private key, is another problem altogether and my face already has enough finger marks.




Software Engineer — infosec @WalmartlabsIndia

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to: Get all process templates, work item types and fields using REST API on Azure DevOps

Everything I learned | Week 16| Encora/Nearsoft Academy

Cross-Origin Resource Sharing In Django Rest API

Normalization in SQL

How to view the error log in cPanel

Multipeer Сonnectivity Framework in Swift: tutorial

Academic Bureau Project with Docker

How Open Listings Is Using MongoDB Atlas to Make Buying Your Dream Home a Reality

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Prateek Nischal

Prateek Nischal

Software Engineer — infosec @WalmartlabsIndia

More from Medium

How I got “Hot reloading” to work with Golang and nodemon (UNIX)

2 simple steps to speed up your Docker build for Golang projects tremendously

Binary protection using go embed and plugins