Skip to content

Instantly share code, notes, and snippets.

@john-yuan
Last active January 23, 2024 20:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save john-yuan/4e2b55f093aa6a963517ec0fb0a3c416 to your computer and use it in GitHub Desktop.
Save john-yuan/4e2b55f093aa6a963517ec0fb0a3c416 to your computer and use it in GitHub Desktop.
Golang AES example. AES-128-CBC, AES-192-CBC, AES-256-CBC encryption and decryption functions written in golang. https://go.dev/play/p/Kj3oeHmz0zA
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
// Key length must be 16 or 24 or 32.
// 16 for AES-128
// 24 for AES-192
// 32 for AES-256
// @see https://pkg.go.dev/crypto/aes#NewCipher
func AesCbcEncryptIv(key []byte, iv []byte, data []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// Add padding: make sure that the length of the data to be
// an integral multiple of the aes.BlockSize.
padding := aes.BlockSize - len(data)%aes.BlockSize
data = append(data, bytes.Repeat([]byte{byte(padding)}, padding)...)
output := make([]byte, len(data))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(output, data)
return output, nil
}
// Decrypt the data encrypted with AesCbcEncryptIv
func AesCbcDecryptIv(key []byte, iv []byte, data []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
size := len(data)
output := make([]byte, size)
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(output, data)
// Remove the padding
return output[:size-int(output[size-1])], nil
}
// Unlike AesCbcEncryptIv, this function will create a random iv automatically.
// Key length must be 16 or 24 or 32.
// 16 for AES-128
// 24 for AES-192
// 32 for AES-256
// @see https://pkg.go.dev/crypto/aes#NewCipher
func AesCbcEncrypt(key []byte, data []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// Add padding: make sure that the length of the data to be
// an integral multiple of the aes.BlockSize.
padding := aes.BlockSize - len(data)%aes.BlockSize
data = append(data, bytes.Repeat([]byte{byte(padding)}, padding)...)
// Create a random iv and include it at the beginning of output.
output := make([]byte, aes.BlockSize+len(data))
iv := output[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(output[aes.BlockSize:], data)
return output, nil
}
// Decrypt the data encrypted with AesCbcEncrypt
func AesCbcDecrypt(key []byte, data []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
size := len(data) - aes.BlockSize
output := make([]byte, size)
iv := data[:aes.BlockSize]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(output, data[aes.BlockSize:])
// Remove the padding
return output[:size-int(output[size-1])], nil
}
// For more details. Please see the function
// ExampleNewCBCDecrypter() and ExampleNewCBCEncrypter()
// in the page https://go.dev/src/crypto/cipher/example_test.go
func main() {
fmt.Println("testIv():")
testIv()
fmt.Println()
fmt.Println("test():")
test()
}
func testIv() {
iv, _ := hex.DecodeString("0dcdfa18e9b7d84a55af95d2b37e469a")
key, _ := hex.DecodeString("412b0d8f10fb2bb46ae06e869caf2c90")
plaintext := "The plain text to be encrypted"
encrypted, err := AesCbcEncryptIv(key, iv, []byte(plaintext))
fmt.Println("Encrypted:", hex.EncodeToString(encrypted), err)
decrypted, err := AesCbcDecryptIv(key, iv, encrypted)
fmt.Println("Decrypted:", string(decrypted), err)
fmt.Println("Passed:", err == nil && string(decrypted) == plaintext)
// The output is:
// Encrypted: 956a77a04ea0328015e79aa3d48671a1434f88fc778940b50680441548e367b0 <nil>
// Decrypted: The plain text to be encrypted <nil>
// Passed: true
}
func test() {
key, _ := hex.DecodeString("412b0d8f10fb2bb46ae06e869caf2c90")
plaintext := "The plain text to be encrypted"
encrypted, err := AesCbcEncrypt(key, []byte(plaintext))
fmt.Println("Encrypted:", hex.EncodeToString(encrypted), err)
decrypted, err := AesCbcDecrypt(key, encrypted)
fmt.Println("Decrypted:", string(decrypted), err)
fmt.Println("Passed:", err == nil && string(decrypted) == plaintext)
// The output is something like below (the encrypted text changes every time):
// Encrypted: 45fcaeb3dc414bfc215f1ec4eb7a21a9fd93ef4edebf0308aaf148ec2a3bd938ca61c240dbf9dc4ce4370486ec6de1f4 <nil>
// Decrypted: The plain text to be encrypted <nil>
// Passed: true
}
@john-yuan
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment