Basic Encryption in Golang

Login to Access Code

Security is becoming ever more important as technology progresses. DDOS, bashing, XSS, and many other attacks happen all the time, everywhere. Many developers in the past have had the mindset that security only needs to match interest levels of hackers, but that mantra is dying quickly as more and more script kiddies sit back in IRC rooms and do an eeny-meeny-miny-mo tactic for picking targets and deploying a full scale assault. Thus, almost every application should have some level of their own security layer which helps cover the almost certain vulnerabilities in other open source tools.

In this tutorial, we are going to break down the basic components of a secure web layer that would ideally sit underneath existing layers like SSL to share only encrypted payloads back and forth from a client to a server. The client and server will both know a couple pieces of information: a public and private key in which the data can be retrieved and then unlocked with. We will use a standard and modern AES algorithm to do the heavy lifting (comes with the Golang standard library) and net package to both the client app and the server.

First we’ll create a simple application server. This code should be pretty straight forward. We will listen for requests at “localhost:3000/server” and if since our application will use a public key and payload, we’ll handle errors if those requirements are not fulfilled.

package main

import (
  "crypto/aes"
  "crypto/cipher"
  "encoding/base64"
  "fmt"
  "io/ioutil"
  "log"
  "net/http"
)

func main() {
  /**
   * GET or POST /server
   *
   * All requests will come through the /server route
   * Each request should have a public key header and payload body
   */
  http.HandleFunc("/server", root)

  log.Println("Listening for connections...")
  log.Fatal(http.ListenAndServe(":3000", nil))
}

func root(w http.ResponseWriter, r *http.Request) {
  /**
   * Public Key
   *
   * If we don't have an api key in the header, then we can't fulfill this request
   *
   * Normally we would lookup and retrieve our private key from a database
   * using the public key to find the record, but for now, we'll just make certain
   * that a public key is passed and use a fixed private key and initialization vector
   */
  publicKey := r.Header.Get("publicKey")
  privateKey := []byte("53d07a9c3566360002020001")
  initializationVector := []byte("53a1c59f62393700")[:aes.BlockSize]

  if len(publicKey) == 0 {
    Responder(w, r, 412, "The 'publicKey' request header was not found")
    return
  }

  /**
   * Received Payload
   *
   * Ensure that we received a payload and that
   * the payload is encrypted with the private key
   */
  payload, err := ioutil.ReadAll(r.Body)

  if err != nil || string(payload) == "" {
    Responder(w, r, 422, "The request body was empty or unprocessable")
    return
  }

  fmt.Println("\n\nPayload: ", string(payload))

  /**
   * Encrypt the insecure payload
   *
   * We allocate the bytes array that represent both the encrypted and decrypted data
   */
  encrypted := make([]byte, len(payload))
  err = encrypt(encrypted, payload, privateKey, initializationVector)

  if err != nil {
    panic(err)
  }

  fmt.Println("\nEncrypted Payload: ", encode(encrypted))

  /**
   * Decrypt the encrypted payload
   */
  decrypted := make([]byte, len(payload))
  err = decrypt(decrypted, encrypted, privateKey, initializationVector)

  if err != nil {
    panic(err)
  }

  fmt.Println("\nDecrypted Payload: ", string(decrypted))

  // Return the encrypted data to the sender
  fmt.Fprintf(w, encode(encrypted))
}

/**
 * HTTP Error Handler
 *
 * Set the http status code and provide an error message
 */
func Responder(w http.ResponseWriter, r *http.Request, status int, message string) {
  w.WriteHeader(status)
  log.Println(message)
  fmt.Fprintf(w, message)
}

func encode(b []byte) string {
  return base64.StdEncoding.EncodeToString(b)
}

func decode(s string) []byte {
  data, err := base64.StdEncoding.DecodeString(s)
  if err != nil {
    panic(err)
  }
  return data
}

func encrypt(dst, src, key, iv []byte) error {
  aesBlockEncryptor, err := aes.NewCipher([]byte(key))

  if err != nil {
    return err
  }

  aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncryptor, iv)
  aesEncrypter.XORKeyStream(dst, src)

  return nil
}

func decrypt(dst, src, key, iv []byte) error {
  aesBlockEncryptor, err := aes.NewCipher([]byte(key))

  if err != nil {
    return err
  }

  aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncryptor, iv)
  aesEncrypter.XORKeyStream(dst, src)

  return nil
}

To test our application: go run server.go on command line and browse to http://localhost:3000/server. To test each of our error handlers, we’ll use Postman (a chrome extension for making http requests):

Basic Encryption in Golang

Our server works! Now, let’s test the publicKey header:

Testing Custom Header that was set in Golang

That is passing and being set properly, so let’s put in a payload now. Keep in mind your terminal will be showing you the results of the raw payload, encrypted payload and decrypted payload in terminal also.

Inspecting the request payload in terminal

That’s all there is to it! The code above has all you need to start creating your own secure transport layer. You would probably want a third party database option that uses the public key to retrieve private keys from. This way your application never stores the private key, but can still encrypt and decrypt on the fly. You can then use this to make the requests over socket or build a public API. Alternatively, you can also use the pnthr app server alongside the pnthr go package and pnthr API web service to have everything out of the box and on the cloud.