Last active
January 23, 2024 20:59
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Run the code with The Go Playground online: https://go.dev/play/p/Kj3oeHmz0zA