9.6 Encrypting and decrypting data

The previous section describes how to securely store passwords, but sometimes it might be neccessary to modify some sensitive encrypted data that has already been stored into our database. When data decryption is required, we should use a symmetric encryption algorithm instead of the one-way hashing techniques we’ve previously covered.

Advanced encryption and decryption

The Go language supports symmetric encryption algorithms in its crypto package. Do not use anything except AES in GCM mode if you don’t know what you’re doing!

  • crypto/aes package: AES (Advanced Encryption Standard), also known as Rijndael encryption method, is used by the U.S. federal government as a block encryption standard.

In the following example we demonstrate how to encrypt data using AES in GCM mode:

  1. package main
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "crypto/rand"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "log"
  10. )
  11. func main() {
  12. text := []byte("My name is Astaxie")
  13. key := []byte("the-key-has-to-be-32-bytes-long!")
  14. ciphertext, err := encrypt(text, key)
  15. if err != nil {
  16. // TODO: Properly handle error
  17. log.Fatal(err)
  18. }
  19. fmt.Printf("%s => %x\n", text, ciphertext)
  20. plaintext, err := decrypt(ciphertext, key)
  21. if err != nil {
  22. // TODO: Properly handle error
  23. log.Fatal(err)
  24. }
  25. fmt.Printf("%x => %s\n", ciphertext, plaintext)
  26. }
  27. func encrypt(plaintext []byte, key []byte) ([]byte, error) {
  28. c, err := aes.NewCipher(key)
  29. if err != nil {
  30. return nil, err
  31. }
  32. gcm, err := cipher.NewGCM(c)
  33. if err != nil {
  34. return nil, err
  35. }
  36. nonce := make([]byte, gcm.NonceSize())
  37. if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
  38. return nil, err
  39. }
  40. return gcm.Seal(nonce, nonce, plaintext, nil), nil
  41. }
  42. func decrypt(ciphertext []byte, key []byte) ([]byte, error) {
  43. c, err := aes.NewCipher(key)
  44. if err != nil {
  45. return nil, err
  46. }
  47. gcm, err := cipher.NewGCM(c)
  48. if err != nil {
  49. return nil, err
  50. }
  51. nonceSize := gcm.NonceSize()
  52. if len(ciphertext) < nonceSize {
  53. return nil, errors.New("ciphertext too short")
  54. }
  55. nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
  56. return gcm.Open(nil, nonce, ciphertext, nil)
  57. }

Calling the above function aes.NewCipher (whose []byte key parameter must be 16, 24 or 32, corresponding to the AES-128, AES-192 or AES-256 algorithms, respectively), returns a cipher.Block Interface that implements three functions:

  1. type Block interface {
  2. // BlockSize returns the cipher's block size.
  3. BlockSize() int
  4. // Encrypt encrypts the first block in src into dst.
  5. // Dst and src may point at the same memory.
  6. Encrypt(dst, src []byte)
  7. // Decrypt decrypts the first block in src into dst.
  8. // Dst and src may point at the same memory.
  9. Decrypt(dst, src []byte)
  10. }

These three functions implement encryption and decryption operations; see the Go documentation for a more detailed explanation.

Summary

This section describes encryption algorithms which can be used in different ways according to your web application’s encryption and decryption needs. For applications with even basic security requirements it is recommended to use AES in GCM mode.