Go lang AES encryption/Decryption

by Anish

Posted on Wednesday April 3 , 2019

This sample chapter extracted from the book, Go Lang Cryptography for Developers . The Book theme isCryptography is for EveryOne. Learn from Crypto Principle to Applied Cryptography With Practical Example Grab a Copy


Advanced Encryption Standard (AES) a symmetric block cipher that can process data blocks of 128 bits, using cipher keys with lengths of 128, 192, and 256 bits. AES ( Formerly Rijndael) was designed to handle additional block sizes and key lengths, however they are not adopted in this standard.

Key-Block-Round Combinations.

The only Key-Block-Round combinations that conform to this standard are given in the below table.

Algo key length Block Size Number of Rounds
AES-128 4 4 10
AES-192 6 4 12
AES-256 8 4 14

Go lang standard library aes implements AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197

func NewCipher creates and returns a new cipher.Block. The key argument should be the AES key, either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256

func NewCipher(key []byte) (cipher.Block, error)

The following example will show how to perform AES encryption and decryption of the given plain text message

  • The key argument should be the AES key, either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
package main  
  
import (  
   "crypto/aes"  
   "encoding/hex"
   "fmt"
  )  
  
func main() {  
  
   key := "myverystrongpasswordo32bitlength"  
   plainText := "Hello 8gwifi.org"  
   ct := Encrypt([]byte(key),plainText)  
   fmt.Printf("Original Text:  %s\n",plainText)  
   fmt.Printf("AES Encrypted Text:  %s\n", ct)  
   Decrypt([]byte(key),ct)  
}  
  
func Encrypt(key []byte, plaintext string) string {  
   c, err := aes.NewCipher(key)  
   if err != nil {  
      fmt.Errorf("NewCipher(%d bytes) = %s", len(key), err)  
      panic(err)  
   }  
   out := make([]byte, len(plaintext))  
   c.Encrypt(out, []byte(plaintext))  
  
   return hex.EncodeToString(out)  
}  

func Decrypt(key []byte, ct string) {    
   ciphertext, _ := hex.DecodeString(ct)  
   c, err := aes.NewCipher(key)  
   if err != nil {  
      fmt.Errorf("NewCipher(%d bytes) = %s", len(key), err)  
      panic(err)  
   }  
   plain := make([]byte, len(ciphertext))  
   c.Decrypt(plain, ciphertext)  
   s := string(plain[:])  
   fmt.Printf("AES Decrypyed Text:  %s\n", s)  
}

The Output

$go run aesencdec.go
Original Text:  Hello 8gwifi.org
AES Encrypted Text:  3ed419795150ede4820f19d104d1654a
AES Decrypyed Text:  Hello 8gwifi.org

AES File Encryption/Decryption

This example will show how to encrypt and decrypt a given file using AES key.

  • The IV value is fixed in this example, it can be randomized, then the user need to write/read the same IV value in the file for encryption and decryption.
  • This example uses OFB block Cipher mode, it can be tuned with the programming need.
package main  
  
import (  
   "crypto/aes"  
   "crypto/cipher"
    "fmt"
    "io"
    "os"
    )  
  
func main() {  
   // Store/Load this Key secretrly  
   // This Shounot be hardcoded in the code.  
   key := "myverystrongpasswordo32bitlength"  
   fmt.Println("====File Encryption/ Decryption====")  
   infileName := "/tmp/hello.txt"  
   encfileName := "/tmp/hello.txt.enc"  
   decfileName := "/tmp/hello.txt.dec"  
   FileEncryption(key,infileName,encfileName)  
   FileDecryption(key,encfileName,decfileName)  
}  
  
func FileEncryption(key string, infileName string, encfileName string)  {  
   inFile, err := os.Open(infileName)  
   if err != nil {  
      panic(err)  
   }  
   defer inFile.Close()  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   // If the key is unique for each ciphertext, then it's ok to use a zero  
   // IV.  var iv [aes.BlockSize]byte  
   stream := cipher.NewOFB(block, iv[:])  
   outFile, err := os.OpenFile(encfileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)  
   if err != nil {  
      panic(err)  
   }  
   defer outFile.Close()  
  
   writer := &cipher.StreamWriter{S: stream, W: outFile}  
   // Copy the input file to the output file, encrypting as we go.  
  if _, err := io.Copy(writer, inFile); err != nil {  
      panic(err)  
   }  
}  
  
  
func FileDecryption(key string, encfileName string, decfileName string)  {  
   inFile, err := os.Open(encfileName)  
   if err != nil {  
      panic(err)  
   }  
   defer inFile.Close()  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   // If the key is unique for each ciphertext, then it's ok to use a zero  
   // IV.  var iv [aes.BlockSize]byte  
   stream := cipher.NewOFB(block, iv[:])  
   outFile, err := os.OpenFile(decfileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)  
   if err != nil {  
      panic(err)  
   }  
   defer outFile.Close()  
   reader := &cipher.StreamReader{S: stream, R: inFile}  
   // Copy the input file to the output file, decrypting as we go.  
  if _, err := io.Copy(outFile, reader); err != nil {  
      panic(err)  
   }  
}
$go run aesfileencdec.go
====File Encryption/ Decryption====

$ cat /tmp/hello.txt
Hello 8gwifi.org
$ cat /tmp/hello.txt.enc
!/=)0??7?PIS????0A
$ cat /tmp/hello.txt.dec
Hello 8gwifi.org

AES-GCM Encryption/Decryption

GCM is a block cipher counter mode with authentication. A Counter mode effectively turns a block cipher into a stream cipher, and therefore many of the rules for stream ciphers still apply.

  • GCM mode provides both privacy (encryption) and integrity.
  • GCM uses an IV (or Nonce)
  • Same(Key) + Same (IV) will always produce same PRNG stream, so for best security practices always re-generate the IV while performing encryption and use the same IV for decryption.
  • GCM is authenticated encryption (both encryption and message integrity)

The following example will show how to perform encryption and decryption in GCM Mode with and without AAD data.

package main  
  
import (  
   "crypto/aes"  
   "crypto/cipher" 
   "crypto/rand" 
   "encoding/hex" 
   "fmt" 
   "io"
   )  
  
func main() {  
  
  // Must Kept Secret No Hardcoding , This is for Demo purpose.
   key := "myverystrongpasswordo32bitlength"  
   plainText := "Hello 8gwifi.org"  
   fmt.Printf("Original Text:  %s\n",plainText)  
   fmt.Println()  
   fmt.Println("====GCM Encryption/ Decryption Without AAD====")  
  
   // Never use more than 2^32 random nonces with a given key because of the risk of a repeat.  
  iv := make([]byte, 12)  
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {  
      panic(err.Error())  
   }  
   ciphertext := GCM_encrypt(key,plainText,iv,nil)  
   fmt.Printf("GCM Encrypted Text:  %s\n", ciphertext)  
   ret := GCM_decrypt(key,ciphertext,iv,nil)  
   fmt.Printf("GCM Decrypted Text:  %s\n", ret)  
   fmt.Println()  
  
   fmt.Println("====GCM Encryption/ Decryption Using AAD====")  
  
   // Never Use Same IV or Nonce  
   iv = make([]byte, 12)  
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {  
      panic(err.Error())  
   }  
   additionalData := "Not Secret AAD Value"  
   ciphertext = GCM_encrypt(key,plainText,iv, []byte(additionalData))  
   fmt.Printf("GCM Encrypted Text:  %s\n", ciphertext)  
   ret = GCM_decrypt(key,ciphertext,iv, []byte(additionalData))  
  
   fmt.Printf("GCM Decrypted Text:  %s\n", ret)  
  
}  

func GCM_encrypt(key string, plaintext string, iv []byte, additionalData []byte) string {  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err.Error())  
   }  
   aesgcm, err := cipher.NewGCM(block)  
   if err != nil {  
      panic(err.Error())  
   }  
   ciphertext := aesgcm.Seal(nil, iv, []byte(plaintext), additionalData)  
   return hex.EncodeToString(ciphertext)  
}  
  
func GCM_decrypt(key string,ct string,iv []byte, additionalData []byte) string {  
   ciphertext, _ := hex.DecodeString(ct)  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err.Error())  
   }  
   aesgcm, err := cipher.NewGCM(block)  
   if err != nil {  
      panic(err.Error())  
   }  
   plaintext, err := aesgcm.Open(nil, iv, ciphertext, additionalData)  
   if err != nil {  
      panic(err.Error())  
   }  
   s := string(plaintext[:])  
   return s  
}

The Output

$ go run aesgcm-encdec.go
Original Text:  Hello 8gwifi.org

====GCM Encryption/ Decryption Without AAD====
GCM Encrypted Text:  00cefd9fb96388fcec4ba862164e9ff97428cfa01cdb962c041f825e06c40659
GCM Decrypted Text:  Hello 8gwifi.org

====GCM Encryption/ Decryption Using AAD====
GCM Encrypted Text:  bb0b4485d9fb0874d1e051eab6a93931d2817d855a23a4446dd0e94211a92afb
GCM Decrypted Text:  Hello 8gwifi.org

AES-CBC Encryption/Decryption

In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted.

  • In CBC Mode the given plaintext should be multiple of AES block size.
  • If the original plaintext lengths are not a multiple of the block size, padding would have to be added when encrypting
  • The IV value should be equal to AES block size.
  • CBC is block ciphers modes, encryption but not message integrity

The following example will show how to perform encryption and decryption in CBC Mode

package main  
import (  
   "crypto/aes"  
   "crypto/cipher" 
   "crypto/rand"
   "encoding/hex" 
   "fmt"
   "io"
   )  
  
func main() {  

  // Must Kept Secret No Hardcoding , This is for Demo purpose.
   key := "myverystrongpasswordo32bitlength"  
  
  // IN CBC Must be Block Size of AES (Multiple of 16)  
  // Other WIse Paddign needs to be perfomed  
   plainText := "Hello 8gwifi.org"   
  
   if len(plainText)%aes.BlockSize != 0 {  
      panic("plaintext is not a multiple of the block size")  
   }  
  
   fmt.Printf("Original Text:  %s\n",plainText)  
   fmt.Println("====CBC Encryption/ Decryption====")  
   // IV Length Must be equal to Block Size.  
   iv := make([]byte, aes.BlockSize)  
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {  
      panic(err.Error())  
   }  
   ciphertext := CBCEncrypter(key,plainText,iv,nil)  
   fmt.Printf("CBC Encrypted Text:  %s\n", ciphertext)  
   ret := CBCDecrypter(key,ciphertext,iv,nil)  
   fmt.Printf("CBC Decrypted Text:  %s\n", ret)  
}  

func CBCEncrypter(key string, plaintext string, iv []byte, additionalData []byte) string {  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }   
   // include it at the beginning of the ciphertext.  
   ciphertext := make([]byte, aes.BlockSize+len(plaintext))  
   mode := cipher.NewCBCEncrypter(block, iv)  
   mode.CryptBlocks(ciphertext[aes.BlockSize:], []byte(plaintext))  
   return hex.EncodeToString(ciphertext)  
}  

func CBCDecrypter(key string,ct string,iv []byte, additionalData []byte) string {  
   ciphertext, _ := hex.DecodeString(ct)  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext = ciphertext[aes.BlockSize:]  
   // CBC mode always works in whole blocks.  
  if len(ciphertext)%aes.BlockSize != 0 {  
      panic("ciphertext is not a multiple of the block size")  
   }  
   mode := cipher.NewCBCDecrypter(block, iv)  
   // CryptBlocks can work in-place if the two arguments are the same.  
   mode.CryptBlocks(ciphertext, ciphertext)  
   s := string(ciphertext[:])  
  
   return s  
}

The Output

$ go run aescbc-encdec.go
Original Text:  Hello 8gwifi.org
====CBC Encryption/ Decryption====
CBC Encrypted Text:  000000000000000000000000000000000c22a1fa436a571e6b16dd258b56dcd4
CBC Decrypted Text:  Hello 8gwifi.org

AES-CFB Encryption/Decryption

The Cipher Feedback (CFB) mode, a close relative of CBC, makes a block cipher into a self-synchronizing stream cipher. Operation is very similar; in particular, CFB decryption is almost identical to CBC encryption performed in reverse:

  • CFB's pseudo random stream depends on the plaintext
  • A different nonce or random IV is needed for every message.
  • CFB is block ciphers modes, encryption but not message integrity
package main  
  
import (  
   "crypto/aes"  
   "crypto/cipher" 
   "crypto/rand" 
   "encoding/hex" 
   "fmt" 
   "io"
   )  
  
func main() {  
  
   // 32 bit length key, for demo pupose hardcoded  
  // Secure this key in production No hardcoding  key := "myverystrongpasswordo32bitlength"  
   plainText := "Hello 8gwifi.org "  
   fmt.Printf("Original Text:  %s\n",plainText)  
   fmt.Println("====CFB Encryption/ Decryption====")  
  
   // IV Length Must be equal to Block Size.  
   iv := make([]byte, aes.BlockSize)  
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {  
      panic(err.Error())  
   }  
   ciphertext := CFBEncrypter(key,plainText,iv,nil)  
   fmt.Printf("CFB Encrypted Text:  %s\n", ciphertext)  
   ret := CFBDecrypter(key,ciphertext,iv,nil)  
   fmt.Printf("CFB Decrypted Text:  %s\n", ret)  
}  

func CFBEncrypter(key string, plaintext string, iv []byte, additionalData []byte) string {  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext := make([]byte, aes.BlockSize+len(plaintext))  
   stream := cipher.NewCFBEncrypter(block, iv)  
   stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))  
   return hex.EncodeToString(ciphertext)  
}  

func CFBDecrypter(key string,ct string,iv []byte, additionalData []byte) string {  
  
   ciphertext, _ := hex.DecodeString(ct)  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext = ciphertext[aes.BlockSize:]  
  
   stream := cipher.NewCFBDecrypter(block, iv)  
   // XORKeyStream can work in-place if the two arguments are the same.  
  stream.XORKeyStream(ciphertext, ciphertext)  
   s := string(ciphertext[:])  
  
   return s  
}

The Output

$ go run aescfb-encdec.go
Original Text:  Hello 8gwifi.org 
====CFB Encryption/ Decryption====
CFB Encrypted Text:  00000000000000000000000000000000966561e216b0757f9e0d6093511fba9b6b
CFB Decrypted Text:  Hello 8gwifi.org 

AES-CTR Encryption/Decryption

CTR mode has similar characteristics to OFB, but also allows a random access property during decryption. CTR mode is well suited to operate on a multi-processor machine where blocks can be encrypted in parallel. Furthermore, it does not suffer from the short-cycle problem that can affect OFB.

  • In CTR Mode the given plaintext should be multiple of AES block size.
  • If the original plaintext lengths are not a multiple of the block size, padding would have to be added when encrypting
  • The IV value should be equal to AES block size.
  • CTR is block ciphers modes, encryption but not message integrity
  • Because of the symmetry of the XOR operation, encryption and decryption are exactly the same

The following example will show how to perform encryption and decryption in CTR Mode

package main  
  
import (  
   "crypto/aes"  
   "crypto/cipher" 
   "crypto/rand" 
   "encoding/hex" 
   "fmt" 
   "io"
   )  
  
func main() {  
  
   // 32 bit length key, Most be secured in production ENV  
   // No Hardcoding of the key  
   key := "myverystrongpasswordo32bitlength"  
   
  // IN CTR Mode the plaintext shoun't be Must be Block Size of AES (Multiple of 16)  
   // Other WIse Paddign needs to be perfomed  
   plainText := "Hello 8gwifi.org"   
   if len(plainText)%aes.BlockSize != 0 {  
      panic("plaintext is not a multiple of the block size")  
   }  
   fmt.Printf("Original Text:  %s\n",plainText)  
   fmt.Println("====CTR Encryption/ Decryption====")  
   // IV Length Must be equal to Block Size.  
   iv := make([]byte, aes.BlockSize)  
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {  
      panic(err.Error())  
   }  
   ciphertext := CTREncrypter(key,plainText,iv)  
   fmt.Printf("CTR Encrypted Text:  %s\n", ciphertext)  
   ret := CTRDecrypter(key,ciphertext,iv)  
   fmt.Printf("CTR Decrypted Text:  %s\n", ret)  
}  

func CTREncrypter(key string, plaintext string, iv []byte) string {  
  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext := make([]byte, aes.BlockSize+len(plaintext))  
   stream := cipher.NewCTR(block, iv)  
   stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))  
   return hex.EncodeToString(ciphertext)  
}  

func CTRDecrypter(key string,ct string,iv []byte) string {  
   ciphertext, _ := hex.DecodeString(ct)  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext = ciphertext[aes.BlockSize:]  
   // CBC mode always works in whole blocks.  
  if len(ciphertext)%aes.BlockSize != 0 {  
      panic("ciphertext is not a multiple of the block size")  
   }  
   mode := cipher.NewCTR(block, iv)  
   mode.XORKeyStream(ciphertext, ciphertext)  
   s := string(ciphertext[:])  
  
   return s  
}

The output

$ go run aesctr-encdec.go
Original Text:  Hello 8gwifi.org
====CTR Encryption/ Decryption====
CTR Encrypted Text:  00000000000000000000000000000000df59bc82ae31c833a58fc8641a7ef119
CTR Decrypted Text:  Hello 8gwifi.org

AES-OFB Encryption/ Decryption

The Output Feedback (OFB) mode makes a block cipher into a synchronous stream cipher. It generates keystream blocks, which are then XORed with the plaintext blocks to get the ciphertext. Just as with other stream ciphers, flipping a bit in the ciphertext produces a flipped bit in the plaintext at the same location

  • In OFB Mode the given plaintext should be multiple of AES block size.
  • If the original plaintext lengths are not a multiple of the block size, padding would have to be added when encrypting
  • The IV value should be equal to AES block size.
  • OFB is block ciphers modes, encryption but not message integrity
  • Because of the symmetry of the XOR operation, encryption and decryption are exactly the same

The following example will show how to perform encryption and decryption in OFB Mode

package main  
import (  
   "crypto/aes"  
   "crypto/cipher" 
   "crypto/rand" 
   "encoding/hex" 
   "fmt" 
   "io"
   )  
  
func main() {  
   // 32 bit length key, Most be secured in production ENV  
   // No Hardcoding of the key  
   key := "myverystrongpasswordo32bitlength"  
   
   // IN OFB Mode the plaintext shoun't be Must be Block Size of AES (Multiple of 16)  
   // Other WIse Paddign needs to be perfomed  
   plainText := "Hello 8gwifi.org"   
   if len(plainText)%aes.BlockSize != 0 {  
      panic("plaintext is not a multiple of the block size")  
   }  
   fmt.Printf("Original Text:  %s\n",plainText)  
   fmt.Println("====OFB Encryption/ Decryption====")  
  
   // IV Length Must be equal to Block Size.  
   iv := make([]byte, aes.BlockSize)  
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {  
      panic(err.Error())  
   }  
   ciphertext := OFBEncrypter(key,plainText,iv)  
   fmt.Printf("OFB Encrypted Text:  %s\n", ciphertext)  
   ret := OFBDecrypter(key,ciphertext,iv)  
   fmt.Printf("OFB Decrypted Text:  %s\n", ret)  
}  
  
  
func OFBEncrypter(key string, plaintext string, iv []byte) string {  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext := make([]byte, aes.BlockSize+len(plaintext))  
   stream := cipher.NewOFB(block, iv)  
   stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))  
   return hex.EncodeToString(ciphertext)  
}  
  
  
  
func OFBDecrypter(key string,ct string,iv []byte) string {  
   ciphertext, _ := hex.DecodeString(ct)  
   block, err := aes.NewCipher([]byte(key))  
   if err != nil {  
      panic(err)  
   }  
   ciphertext = ciphertext[aes.BlockSize:]  
   // CBC mode always works in whole blocks.  
   if len(ciphertext)%aes.BlockSize != 0 {  
      panic("ciphertext is not a multiple of the block size")  
   }  
   mode := cipher.NewOFB(block, iv)  
   mode.XORKeyStream(ciphertext, ciphertext)  
   s := string(ciphertext[:])  
   return s  
}

The Output

$ go run aesofb-encdec.go
Original Text:  Hello 8gwifi.org
====OFB Encryption/ Decryption====
OFB Encrypted Text:  00000000000000000000000000000000eb53ce7fe03a5cd2ea4551acbe5ea2b7
OFB Decrypted Text:  Hello 8gwifi.org

Thanku for reading !!! Give a Share for Support


Your Support Matters!

Instead of directly asking for donations, I'm thrilled to offer you all nine of my books for just $9 on leanpub By grabbing this bundle you not only help cover my coffee, beer, and Amazon bills but also play a crucial role in advancing and refining this project. Your contribution is indispensable, and I'm genuinely grateful for your involvement in this journey!

Any private key value that you enter or we generate is not stored on this site, this tool is provided via an HTTPS URL to ensure that private keys cannot be stolen, for extra security run this software on your network, no cloud dependency




python Cryptography Topics
Topics
For Coffee/ Beer/ Amazon Bill and further development of the project Support by Purchasing, The Modern Cryptography CookBook for Just $9 Coupon Price

Kubernetes for DevOps

Hello Dockerfile

Cryptography for Python Developers

Cryptography for JavaScript Developers

Go lang ryptography for Developers

Here