AES(高级加密标准)是一种分组密码算法,每次只能处理固定长度的数据块,在Go语言中可以通过标准库快速实现加密功能,但需要先理解块密码的特性和正确的使用方式。

AES块密码的核心特性
AES属于对称加密算法,加密和解密使用相同的密钥,其分组长度固定为128位,密钥长度支持128位、192位、256位三种规格。由于它只能处理固定长度的块,当待加密数据长度超过128位时,就需要通过工作模式来拼接多个块的加密结果。
常见的工作模式包括ECB、CBC、CFB、OFB、CTR等,其中ECB模式因为相同的明文块会生成相同的密文块,安全性较低,实际开发中更推荐使用CBC或CTR模式。
关键概念说明
- 初始向量(IV):在CBC等模式中需要使用,作用是避免相同的明文生成相同的密文,长度需要和AES块长度一致,即128位(16字节)。
- 数据填充:如果待加密数据长度不是128位的整数倍,需要按照PKCS7等标准进行填充,解密后再移除填充内容。
Go语言AES加密实现步骤
Go语言的crypto/aes包提供了AES算法的核心实现,结合crypto/cipher包可以完成不同工作模式的加密解密操作,下面以CBC模式为例演示完整流程。
1. 密钥和IV的准备
AES密钥的长度需要符合规范,128位密钥对应16字节,192位对应24字节,256位对应32字节。IV需要随机生成,且不需要保密,可以和密文一起存储或传输。
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
)
// 生成指定长度的随机密钥,这里以256位密钥为例
func generateKey() ([]byte, error) {
key := make([]byte, 32) // 32字节=256位
_, err := io.ReadFull(rand.Reader, key)
if err != nil {
return nil, err
}
return key, nil
}
// 生成16字节的随机IV
func generateIV() ([]byte, error) {
iv := make([]byte, aes.BlockSize) // aes.BlockSize固定为16
_, err := io.ReadFull(rand.Reader, iv)
if err != nil {
return nil, err
}
return iv, nil
}
2. PKCS7填充与去填充实现
因为CBC模式要求数据长度是块大小的整数倍,所以需要实现PKCS7填充逻辑,填充的字节值为需要填充的字节数。
// PKCS7填充
func pkcs7Pad(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := make([]byte, padding)
for i := range padText {
padText[i] = byte(padding)
}
return append(data, padText...)
}
// PKCS7去填充
func pkcs7Unpad(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, fmt.Errorf("数据长度不能为0")
}
padding := int(data[length-1])
if padding > length || padding == 0 {
return nil, fmt.Errorf("填充数据非法")
}
return data[:length-padding], nil
}
3. CBC模式加密实现
使用cipher包创建CBC模式的加密器,完成数据加密操作。
func aesCBCEncrypt(plaintext, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 填充数据
paddedPlaintext := pkcs7Pad(plaintext, aes.BlockSize)
// 创建CBC加密器
mode := cipher.NewCBCEncrypter(block, iv)
ciphertext := make([]byte, len(paddedPlaintext))
mode.CryptBlocks(ciphertext, paddedPlaintext)
return ciphertext, nil
}
4. CBC模式解密实现
解密过程需要使用相同的密钥和IV,先解密再去填充得到原始数据。
func aesCBCDecrypt(ciphertext, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 创建CBC解密器
mode := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
// 去填充
return pkcs7Unpad(plaintext)
}
5. 完整调用示例
下面是完整的调用流程,演示从密钥生成到加密解密的整个过程。
func main() {
// 生成密钥和IV
key, err := generateKey()
if err != nil {
panic(err)
}
iv, err := generateIV()
if err != nil {
panic(err)
}
fmt.Printf("密钥(十六进制): %xn", key)
fmt.Printf("IV(十六进制): %xn", iv)
// 待加密的明文
plaintext := []byte("这是一段需要加密的测试数据")
fmt.Printf("原始明文: %sn", plaintext)
// 加密
ciphertext, err := aesCBCEncrypt(plaintext, key, iv)
if err != nil {
panic(err)
}
fmt.Printf("密文(十六进制): %xn", ciphertext)
// 解密
decrypted, err := aesCBCDecrypt(ciphertext, key, iv)
if err != nil {
panic(err)
}
fmt.Printf("解密后明文: %sn", decrypted)
}
常见实现误区提醒
- 不要使用ECB模式,该模式无法隐藏明文的模式特征,安全性较差。
- IV需要每次加密都随机生成,不要重复使用相同的IV。
- 密钥需要妥善保管,不要硬编码在代码中,建议通过环境变量或密钥管理服务存储。
- 加密后的IV可以和密文拼接在一起存储,解密时先拆分出IV再执行解密操作。
如果需要处理流式数据,可以考虑使用CFB或CTR模式,这两种模式不需要填充操作,更适合处理长度不固定的数据流。