Encrypt and decrypt strings in Go with AES-GCM

Encrypt and decrypt byte slices or strings in Go using AES-256 with GCM for confidentiality and integrity, a random nonce per message, and base64 for storage; extend the same pattern to files by reading and writing raw ciphertext bytes.

Published

Updated

Read time 2 min read

Reviewed byDeepak Prasad

Encrypt and decrypt strings in Go with AES-GCM

This guide shows golang encrypt decrypt flows for small payloads using AES-256-GCM: a random nonce per message, Seal to produce ciphertext, Open to verify and recover plaintext, and base64 when you need a string transport. The same pattern applies to files by reading and writing []byte instead of string literals. Use keys from a secret manager or KDF in real systems—never hard-code production keys.

Tested with Go 1.24 on Linux.


Encrypt and decrypt a string

go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
)

func encrypt(plaintext, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}
	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

func decrypt(enc string, key []byte) ([]byte, error) {
	data, err := base64.StdEncoding.DecodeString(enc)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}
	ns := gcm.NonceSize()
	if len(data) < ns {
		return nil, fmt.Errorf("ciphertext too short")
	}
	return gcm.Open(nil, data[:ns], data[ns:], nil)
}

func main() {
	key := make([]byte, 32)
	if _, err := rand.Read(key); err != nil {
		panic(err)
	}
	msg := []byte("secret data")
	s, err := encrypt(msg, key)
	if err != nil {
		panic(err)
	}
	out, err := decrypt(s, key)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(out))
}
Output

You should see secret data. The base64 string changes every run because the nonce is random.


Files and JSON

For files, os.ReadFile / os.WriteFile swap in place of string conversion; for JSON APIs, store ciphertext as base64 in a string field and parse after transport. Combine with JSON patterns when wrapping wire formats.


Summary

Modern golang encrypt decrypt code should use AES-GCM with 32-byte keys, random nonces, and authenticated Open on decrypt. Treat keys and KDF choice as part of your threat model; this article only demonstrates the crypto primitive shape.


References


Frequently Asked Questions

1. Why AES-GCM instead of older modes like CFB?

GCM provides authenticated encryption so tampering is detected on decrypt; prefer it for new designs unless you must interoperate with a legacy protocol.

2. How long should an AES-256 key be?

32 bytes for AES-256; derive keys from passwords with a proper KDF such as Argon2 or scrypt, not a single SHA-256 alone, for human-chosen secrets.

3. Can I reuse a GCM nonce?

Never reuse a nonce with the same key; generate a fresh random nonce per message with crypto/rand.

4. How do I encrypt a file?

Read the file into a byte slice, run the same seal/open helpers, and write ciphertext plus nonce layout to disk; keep keys out of source control.

5. Where is the standard library crypto documentation?

See the crypto packages on pkg.go.dev for aes, cipher, and rand.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …