Go语言AES-CBC对称加密实战:原理、代码与注意事项
针对项目中手机号、身份证号等敏感接口的传输安全,本文详细介绍了如何在 Go 语言中使用 AES 算法的 CBC 模式进行对称加密。通过封装 `AESCbcEncrypt` 和 `AESCbcDecrypt` 函数,结合 PKCS7 填充标准与随机 IV 机制,实现高效且安全的密文交互方案。
AES 对称加密基础
对称加密指加密和解密使用同一个密钥,相比非对称加密(公钥/私钥对),其计算效率更高,适合大数据量或高频调用场景。在 Go 的标准库中,我们主要关注 AES(Advanced Encryption Standard)算法。
AES 支持 128、192、256 三种密钥长度,对应的块大小固定为 128 bits(16 字节)。虽然 Golang 的 `crypto/aes` 包支持所有长度,但在实际工程中,**AES-128(16 byte 密钥)**因性能与安全的平衡而被广泛采用。本文示例将基于 AES-128 进行演示。
CBC 模式工作原理
AES 是分组密码,需配合工作模式使用。其中 CBC(Cipher Block Chaining,密文反馈模式)通过将前一个密文块与当前明文块异或后再加密,有效隐藏了明文的统计规律,比 ECB 模式更安全。
第1步:准备加密块与密钥处理
首先创建 Cipher 对象,并对传入的密钥进行处理。若密钥长度超过 16 字节,默认截取前 16 字节以保证兼容性;若不足,在实际生产环境中建议报错或补充填充,此处为简化演示直接取前缀。
func AESCbcEncrypt(secretKey, src string) string {
key := []byte(secretKey)
if len(key) > 16 {
key = key[:16]
}
plaintext := []byte(src)
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// ...后续步骤见下文第2步:数据填充(PKCS7)
CBC 模式要求输入数据的长度必须是块大小(16字节)的整数倍。当明文长度不足时,需进行填充。这里采用标准的 PKCS7 填充方式,即补入的字节值为所缺少的字节数。
func Padding(plainText []byte, blockSize int) []byte {
padding := blockSize - len(plainText)%blockSize
char := []byte{byte(padding)}
newPlain := bytes.Repeat(char, padding)
return append(plainText, newPlain...)
}第3步:生成随机初始化向量(IV)
IV 用于打破相同明文产生相同密文的规律。**每次加密必须生成不同的随机 IV**,但加密和解密时必须使用相同的 IV。在 CBC 模式下,通常将生成的 IV 放在密文的最前面一并传输。
// 预留空间给IV
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}第4步:执行加密操作
使用 `cipher.NewCBCEncrypter` 创建加密器,并将填充后的明文填入剩余空间进行加密。最后对包含 IV 和密文的整体数据进行 Base64 编码,便于在网络传输中作为字符串处理。
mode := cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext) return base64.StdEncoding.EncodeToString(ciphertext)
完整加密实现
结合上述步骤,完整的 Go 语言 AES-CBC 加密函数如下:
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
)
func AESCbcEncrypt(secretKey, src string) (string, error) {
key := []byte(secretKey)
if len(key) > 16 {
key = key[:16]
}
// 确保密钥长度足够
if len(key) < 16 {
return "", errors.New("key too short")
}
plaintext := []byte(src)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
plaintext = Padding(plaintext, block.BlockSize())
if len(plaintext)%aes.BlockSize != 0 {
return "", errors.New("plaintext is not a multiple of the block size")
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}解密流程与实现
解密是加密的逆过程。首先解码 Base64 数据,分离出 IV 和真实密文,验证密文长度合法性后,使用 `NewCBCDecrypter` 进行解密,最后去除填充。
第1步:Base64 解码与结构分离
func AESCbcDecrypt(secretKey, src string) (string, error) {
key := []byte(secretKey)
if len(key) > 16 {
key = key[:16]
}
if len(key) < 16 {
return "", errors.New("key too short")
}
ciphertext, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return "", err
}第2步:提取 IV 并验证长度
if len(ciphertext) < aes.BlockSize {
return "", errors.New("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:] // 去掉IV头
if len(ciphertext)%aes.BlockSize != 0 {
return "", errors.New("ciphertext is not a multiple of the block size")
}第3步:解密与去填充
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
ciphertext = UnPadding(ciphertext)
return string(ciphertext), nil
}取消填充逻辑
根据最后一个字节的值确定填充长度,截取原始数据:
func UnPadding(plainText []byte) []byte {
length := len(plainText)
if length == 0 {
return plainText
}
padding := int(plainText[length-1])
if padding > length {
return plainText
}
return plainText[:length-padding]
}总结
本文展示了在 Go 语言中通过 `crypto/aes` 和 `crypto/cipher` 标准库实现 AES-CBC 对称加密的完整路径。关键在于:1. 严格处理密钥长度;2. 正确实现 PKCS7 填充;3. 确保 IV 的唯一性与一致性。这种方案适用于绝大多数需要前后端协同加密敏感信息的业务场景。以上就是 Go语言使用对称加密的详细内容,更多关于Golang AES加密的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













