GO Lang AES Encrypt/Decrypt
From time to time there is a need to encrypt/decrypt something
Based on previous notes here is small helper
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
)
func main() {
text := "Hello, World!"
cryptographer, err := New()
if err != nil {
panic(err)
}
encrypted, err := cryptographer.Encrypt(text)
if err != nil {
panic(err)
}
decrypted, err := cryptographer.Decrypt(encrypted)
if err != nil {
panic(err)
}
if text != decrypted {
panic("decrypted text does not match original")
}
println("Original:", text)
println("Encrypted:", encrypted)
println("Decrypted:", decrypted)
}
type Cryptographer struct {
block cipher.Block
}
// func NewWithKey(secret []byte) (*Cryptographer, error) {
// if len(secret) != 32 {
// return nil, errors.New("cryptographer: invalid key size")
// }
// block, err := aes.NewCipher(secret)
// if err != nil {
// return nil, err
// }
// return &Cryptographer{block: block}, nil
// }
func New() (*Cryptographer, error) {
secret := make([]byte, 32)
_, err := rand.Read(secret)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(secret)
if err != nil {
return nil, err
}
return &Cryptographer{block: block}, nil
}
func (c *Cryptographer) Encrypt(input string) (string, error) {
if input == "" {
return "", errors.New("cryptographer: empty input")
}
bytes := []byte(input)
output := make([]byte, aes.BlockSize+len(bytes))
iv := output[:aes.BlockSize] // get first N (aes.BlockSize) bytes
_, err := io.ReadFull(rand.Reader, iv) // fill the iv with random bytes
if err != nil {
return "", err
}
// encrypt
stream := cipher.NewCTR(c.block, iv)
stream.XORKeyStream(output[aes.BlockSize:], bytes)
// encode
return base64.URLEncoding.EncodeToString(output), nil
}
func (c *Cryptographer) Decrypt(input string) (string, error) {
if input == "" {
return "", errors.New("cryptographer: empty input")
}
// decode
bytes, err := base64.URLEncoding.DecodeString(input)
if err != nil {
return "", err
}
if len(bytes) < aes.BlockSize {
return "", errors.New("cryptographer: ciphertext too short")
}
// split bytes into iv and encrypted
iv := bytes[:aes.BlockSize]
encrypted := bytes[aes.BlockSize:]
output := make([]byte, len(encrypted))
// decrypt
stream := cipher.NewCTR(c.block, iv)
stream.XORKeyStream(output, encrypted)
return string(output), nil
}
Notes:
- in my case, it is used to encrypt/decrypt short living cookies, so by intent, i want
key
to be generated each time - aka it will rotate eventually and there is no need to hide/steal it - in case of persistance is required just pass key from outside
- for AES key must be 32 bytes and IV - 16 bytes
- to simplify things we are writing/reading IV first, and then dealing with rest of the bytes, otherwise they should be passed separate