Table of Contents
You'll be able to by the end of this tutorial:
- Understand the basic concept encrypt and decrypt
- Utilize Go to encrypt text/other data types using the AES encryption standard.
- Finally, we will examine how to use a shared secret to decrypt a message.
You should be able to use this to create your own simple encryption systems that can accomplish various tasks, such as encrypting files on your file system and protecting them with a passphrase unique to you or incorporating simple encryption into different system components that you are developing.
Encrypt a binary or string using AES in Golang
Data can be hidden through encryption, making it unusable if it gets into the wrong hands. We'll use the Advanced Encryption Standard, which crypto/aes offers, to encrypt in Go.
Package aes implements AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197.
The AES operations in this package are not implemented using constant-time algorithms. An exception is when running on systems with enabled hardware support for AES that makes these operations constant-time.
In its crypto
package, the Go programming language provides symmetric encryption techniques.
The first step is to generate a 32-byte key for use in encrypting and decrypting data. Because the key must be saved somewhere in real-world applications, I encoded it to a string for this purpose:
key := make([]byte, 32) //generate a random 32 byte key for AES-256
if _, err := rand.Read(key); err != nil {
panic(err.Error())
}
keyStr := hex.EncodeToString(key) //convert to string for saving
Encryption
To encrypt data, we need :
- key (32-bytes for AES-256 encryption)
- data to encrypt - plaintext in this example
Create a new Cipher Block from the key:
block, err := aes.NewCipher(key)
Using NewCFBEncrypter()
function to encrypt data:
func NewCFBEncrypter(block Block, iv []byte) Stream
: NewCFBEncrypter returns a Stream which encrypts with cipher feedback mode, using the given Block. The iv must be the same length as the Block's block size.
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
Because ciphertext is slice of byte, we have to convert the encrypted data to base64:
return base64.URLEncoding.EncodeToString(ciphertext)
Decryption
To decrypt data, we need to convert the key and the string to decrypt to bytes:
key, _ := hex.DecodeString(keyString)
ciphertext, _ := base64.URLEncoding.DecodeString(stringToDecrypt)
Create a new Cipher Block from the key:
block, err := aes.NewCipher(key)
Decrypt data using NewCFBDecrypter()
function:
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)
The entire code for encrypt and decrypt string in Golang:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
)
func encrypt(keyString string, stringToEncrypt string) (encryptedString string) {
// convert key to bytes
key, _ := hex.DecodeString(keyString)
plaintext := []byte(stringToEncrypt)
//Create a new Cipher Block from the key
block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// convert to base64
return base64.URLEncoding.EncodeToString(ciphertext)
}
// decrypt from base64 to decrypted string
func decrypt(keyString string, stringToDecrypt string) string {
key, _ := hex.DecodeString(keyString)
ciphertext, _ := base64.URLEncoding.DecodeString(stringToDecrypt)
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < aes.BlockSize {
panic("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
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)
return fmt.Sprintf("%s", ciphertext)
}
func main() {
originalText := "Hello GoLinuxCloud members!"
fmt.Println(originalText)
key := []byte("this's secret key.enough 32 bits")
if _, err := rand.Read(key); err != nil {
panic(err.Error())
}
keyStr := hex.EncodeToString(key) //convert to string for saving
fmt.Println("Encrypting.....")
// encrypt value to base64
cryptoText := encrypt(keyStr, originalText)
fmt.Println(cryptoText)
fmt.Println("Decrypting.....")
// encrypt base64 crypto to original value
text := decrypt(keyStr, cryptoText)
fmt.Println(text)
}
Output:
Hello GoLinuxCloud members!
Encrypting.....
jhaPlM3OHjJKDBSaB-25-2sat1IcPJ6iwL7lkRgA2eZvzhUriCzb1tK7ow==
Decrypting.....
Hello GoLinuxCloud members!
Encrypt and decrypt a file using AES in Golang
With the same ideal, now we can read text file and key file and then encrypt it. For decryption, it’s pretty similar.
func main() {
plainText, err := ioutil.ReadFile("./plaintext.txt")
if err != nil {
log.Fatalf("read file err: %v", err.Error())
}
key, err := ioutil.ReadFile("./key.txt")
if err != nil {
log.Fatalf("read file err: %v", err.Error())
}
fmt.Println("Encrypting.....")
// encrypt value to base64
cryptoText := encrypt(string(key[:]), string(plainText[:]))
fmt.Println(cryptoText)
err = ioutil.WriteFile("./ciphertext.bin", []byte(cryptoText), 0777)
if err != nil {
log.Fatalf("write file err: %v", err.Error())
}
fmt.Println("Decrypting.....")
cryptoFile, err := ioutil.ReadFile("./ciphertext.bin")
if err != nil {
log.Fatalf("read file err: %v", err.Error())
}
// encrypt base64 crypto to original value
text := decrypt(string(key[:]), string(cryptoFile[:]))
fmt.Println(text)
}
Output:
Encrypting.....
clJy-R9MOvbUa9Qx4hxDrGKEjDv8-YCIalmA5oss1J8VzpNZZAxUVXjZUhEE8_aFvTAqGqSYOiBMcb1ST6s0Ffe8XfzlGX1rgcDcjX_qJOjLAntDPfW1GE6wFI-DK5Zw4JmHILPrBFQHSmJHNrJwcUJy_lrgVl4Z6VgNliqbqmgOQYVko1dYxbYDXW4UWJcqIKiYqNKUdUDzPRstFFXtVtLZ-bo6Rv944x-SB5mvgUuYyzuCzg1AIgZnbsnMPht6_RAEOnQ3D0FrgZpWEHrpuDoYSX33M7ljmjb6BUmmtNhUPXlMRJMSVw95t8fbCoYGTtNvwFM86HDsT4DUCiy18SExmKEuecDzlA0iYWFBWgokxUU9UJQCxzBdRnwahtu-jTaJc6QxS5ShzcgQw5-3GxE0jCN2j3UNelbooz80nxst2BNH5J3G8bZ19TXjo1xDLeyvgLDEnWEKEZFQH3sn86VvWRtk2DxkI-WpIn0NyN9Fo6iNOX-jMtvdXoAANMB56ViDI8FTes5JOm_q9E8RQY3kcFTH-iMHBKsTG91om9ttQg8mnUo65R15yTUUeYRv7C6KDFJQPOoPGEUoWcbsc7B5egzZJ-n35hrA4xodTbw0wJAEYtcbMUElxh4HIggY9ilKaURYJOr_XuIH3ysbqJT1wunA3kd_6BJvSRz4dW9X1C5o3FGqxojdMAXQiE6Zq2mTIZ1ZH41dw5qnxM8VUndhgrntOdbM3L3edyThKvXFiu6fc4xQ_7mFEdZZ_HeVZ9r3rYfXgu13MCe8-0Aa_XOC0r3U8i0NprXlUh6asvnkv1MRqsmQaeF8ALw6AeESggdfnSNIWqWAflgajVTTYtyEfSx6mxGPRmV-nVyzb-SWumrWpkhuBSlPoTQ2yExCB9jdQjoO233fe8Q8xzM15rveCgq0OjGZhD6aO2Sv-4zITaaa7c9tG4QipzxTqf2coiAC0jLtq8sZQYVyGrekuJMTl9-3aBJFm1GkHKoe9UodQSIWdMxJVNtkvckxo5vfHdDnQxqaPMak-hH3eLpycilwNM47ftB_UDYwTaHabAe_zT-mPHsB1wXo_nq3q4S-sCMBWi_4xr_M3_-gdu4S9EfTIVNwmK6fjyS_GXMno4S8aMS_oXSdtWGzJJRnmRZEH4fJ-mC8Y7NrwCcsHb_Z7lhbrhGVKRVLeVem-M5JyNY4I_xMRHtRLn8xkkoZQvdFN4Fu12eEz10clL1nUaAdvIRK_zYI8t-v2AMPd6VcgfyM1T4INK9Lv8810ZR8Opl8dXA9VQBVaeiKdf41TD7o3dl0oS8hd9kWjpckE-8d1xMN0vHoUB7c0UQc3XC8O553fnCMYEAjbOdE4A-gXg==
Decrypting.....
" Y'ARE very snug in here," piped old Mr. Woodifield, and he peered out of the great, green leather armchair by his friend the boss's desk as a baby peers out of its pram. His talk was over; it was time for him to be off. But he did not want to go. Since he had retired, since his... stroke, the wife and the girls kept him boxed up in the house every day of the week except Tuesday. On Tuesday he was dressed and brushed and allowed to cut back to the City for the day. Though what he did there the wife and girls couldn't imagine. Made a nuisance of himself to his friends, they supposed ... Well, perhaps so. All the same, we cling to our last pleasures as the tree clings to its last leaves. So there sat old Woodifield, smoking a cigar and staring almost greedily at the boss, who rolled in his office chair, stout, rosy, five years older than he, and still going strong, still at the helm. It did one good to see him. Wistfully, admiringly, the old voice added, " It's snug in here, upon my word ! "
Encrypt and decrypt a struct, json in Golang
The problem is how we can convert a struct to a byte array. The encoding/gob standard package is one possible solution. The gob package implements an encoder/decoder that can convert any struct into an array of bytes and then back into a struct. With json
file, we can convert it to a string or unmarshall it to a struct and then encrypt and decrypt it. Here is an example of encrypt a struct in Golang:
type Person struct {
Name string
Age int
}
func main() {
var network bytes.Buffer // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
err := enc.Encode(Person{"Harry Potter", 1000})
if err != nil {
log.Fatal("encode error:", err)
}
key := []byte("this's secret key.enough 32 bits")
if _, err := rand.Read(key); err != nil {
panic(err.Error())
}
keyStr := hex.EncodeToString(key) //convert to string for saving
fmt.Println("Encrypting.....")
// encrypt value to base64
cryptoText := encrypt(keyStr, string(network.Bytes()))
fmt.Println(cryptoText)
if err != nil {
log.Fatalf("write file err: %v", err.Error())
}
fmt.Println("Decrypting.....")
// encrypt base64 crypto to original value
text := decrypt(keyStr, cryptoText)
byteBuffer := bytes.NewBuffer([]byte(text))
dec := gob.NewDecoder(byteBuffer) // Will read from byteBuffer
var person Person
err = dec.Decode(&person)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Printf("%q: %d\n", person.Name, person.Age)
}
Output:
Encrypting.....
KJSu0JrEdqOKpB6pR_37bqoMSPsUzRF_u5LTULLtAaOv4HziwvCosRRDM9FRNzgXJAGf9OBXlxcWWIFL32_rW-TcFo8SbJm-rgxepQ==
Decrypting.....
"Harry Potter": 1000
Summary
In this article I have introduced examples of encrypt, decrypt string, byte, file and struct in Golang using crypto package. There is always a need to safeguard your data, particularly online data. We can prevent unauthorized access by converting information into computer code using encryption.
References
https://pkg.go.dev/crypto/aes#NewCipher
https://en.wikipedia.org/wiki/Base64
https://pkg.go.dev/encoding/gob