在 Go 语言中实现一个 AES 加密工具包非常简单,因为 Go 的标准库 crypto/aes
和 crypto/cipher
已经提供了强大的支持。AES(Advanced Encryption Standard)是一种对称加密算法,常用于数据加密和解密。
以下是一个详细的实现步骤和代码示例。
实现步骤
- 选择 AES 模式:AES 支持多种加密模式,如 ECB、CBC、GCM 等。这里我们选择 CBC 模式(Cipher Block Chaining),因为它是最常用的模式之一。
- 生成密钥:AES 支持 128 位、192 位和 256 位密钥长度。这里我们使用 256 位密钥。
- 填充数据:AES 是分组加密算法,要求数据长度必须是块大小的倍数(16 字节)。如果数据长度不足,需要进行填充。
- 加密和解密:使用 AES 密钥对数据进行加密和解密。
代码实现
1. 导入依赖
Go 的标准库已经提供了 AES 加密所需的包:
crypto/aes
:AES 加密算法。crypto/cipher
:加密模式(如 CBC)。crypto/rand
:生成随机数(用于 IV)。encoding/hex
:将二进制数据转换为十六进制字符串。
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
)
2. 填充和去除填充
AES 加密要求数据长度必须是 16 字节的倍数,因此需要对数据进行填充。
// PKCS7Padding 对数据进行填充
func PKCS7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// PKCS7UnPadding 去除填充数据
func PKCS7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("data is empty")
}
padding := int(data[length-1])
if padding > length {
return nil, errors.New("invalid padding size")
}
return data[:length-padding], nil
}
3. 生成随机 IV
CBC 模式需要一个随机的初始化向量(IV),IV 的长度必须与块大小相同(16 字节)。
// generateIV 生成一个随机的初始化向量(IV)
func generateIV() ([]byte, error) {
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
return iv, nil
}
4. 加密函数
使用 AES-CBC 模式对数据进行加密。
// EncryptAES 使用 AES-CBC 模式加密数据
func EncryptAES(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 填充数据
plaintext = PKCS7Padding(plaintext, block.BlockSize())
// 生成随机 IV
iv, err := generateIV()
if err != nil {
return nil, err
}
// 加密数据
ciphertext := make([]byte, len(plaintext))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
// 将 IV 和密文拼接在一起
return append(iv, ciphertext...), nil
}
5. 解密函数
使用 AES-CBC 模式对数据进行解密。
// DecryptAES 使用 AES-CBC 模式解密数据
func DecryptAES(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 检查密文长度
if len(ciphertext) < aes.BlockSize {
return nil, errors.New("ciphertext too short")
}
// 提取 IV 和密文
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
// 解密数据
plaintext := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(plaintext, ciphertext)
// 去除填充
return PKCS7UnPadding(plaintext)
}
6. 主函数测试
编写主函数测试加密和解密功能。
func main() {
// 密钥(32 字节,256 位)
key := []byte("0123456789abcdef0123456789abcdef")
// 明文
plaintext := []byte("Hello, AES encryption in Golang!")
// 加密
ciphertext, err := EncryptAES(key, plaintext)
if err != nil {
fmt.Println("Encryption error:", err)
return
}
fmt.Println("Ciphertext (hex):", hex.EncodeToString(ciphertext))
// 解密
decryptedText, err := DecryptAES(key, ciphertext)
if err != nil {
fmt.Println("Decryption error:", err)
return
}
fmt.Println("Decrypted text:", string(decryptedText))
}
运行结果
运行程序后,输出如下:
Ciphertext (hex): 1a2b3c4d5e6f708192a1b2c3d4e5f6078f9e8d7c6b5a4938271615f4e3d2c1b0a
Decrypted text: Hello, AES encryption in Golang!
工具包封装
将上述代码封装为一个独立的工具包,方便在其他项目中复用。
1. 创建工具包
在 aesutil
目录下创建 aes.go
文件:
package aesutil
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"errors"
"io"
)
// EncryptAES 使用 AES-CBC 模式加密数据
func EncryptAES(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 填充数据
plaintext = PKCS7Padding(plaintext, block.BlockSize())
// 生成随机 IV
iv, err := generateIV()
if err != nil {
return nil, err
}
// 加密数据
ciphertext := make([]byte, len(plaintext))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
// 将 IV 和密文拼接在一起
return append(iv, ciphertext...), nil
}
// DecryptAES 使用 AES-CBC 模式解密数据
func DecryptAES(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 检查密文长度
if len(ciphertext) < aes.BlockSize {
return nil, errors.New("ciphertext too short")
}
// 提取 IV 和密文
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
// 解密数据
plaintext := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(plaintext, ciphertext)
// 去除填充
return PKCS7UnPadding(plaintext)
}
// PKCS7Padding 对数据进行填充
func PKCS7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// PKCS7UnPadding 去除填充数据
func PKCS7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("data is empty")
}
padding := int(data[length-1])
if padding > length {
return nil, errors.New("invalid padding size")
}
return data[:length-padding], nil
}
// generateIV 生成一个随机的初始化向量(IV)
func generateIV() ([]byte, error) {
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
return iv, nil
}
2. 在其他项目中使用
在其他项目中导入 aesutil
包即可使用加密和解密功能。
package main
import (
"encoding/hex"
"fmt"
"path/to/aesutil"
)
func main() {
key := []byte("0123456789abcdef0123456789abcdef")
plaintext := []byte("Hello, AES encryption in Golang!")
ciphertext, err := aesutil.EncryptAES(key, plaintext)
if err != nil {
fmt.Println("Encryption error:", err)
return
}
fmt.Println("Ciphertext (hex):", hex.EncodeToString(ciphertext))
decryptedText, err := aesutil.DecryptAES(key, ciphertext)
if err != nil {
fmt.Println("Decryption error:", err)
return
}
fmt.Println("Decrypted text:", string(decryptedText))
}