Go语言实现AES加密解密:字符串与文件处理详解
在Go语言开发中,AES是一种广泛使用的对称加密算法。本文详细介绍如何基于crypto/aes和crypto/cipher包,通过CBC工作模式和PKCS7填充标准,实现可靠的字符串与文件级加解密逻辑。同时提供处理大文件时的分块策略与完整代码示例。
AES对称加密算法原理
AES(Advanced Encryption Standard)是业界标准的对称加密方案。其核心特点在于使用单一密钥进行加密和解密,具有计算量小、执行效率高、安全性强的优势。相比非对称加密,它更适合处理大量数据的传输或存储保护。
关键特性:
- 密钥管理: 发送方与接收方需严格共享并妥善保管同一把密钥,一旦泄露即失去安全性。
- 应用场景: 常用于数据库敏感字段存储、API通信内容加密及本地文件安全存储。
- 模式选择: 常见工作模式包括ECB和CBC。其中CBC模式引入初始向量(IV),有效防止相同明文生成相同密文,安全性更高。
基础代码实现
实现AES加密通常包含数据填充、加密实例初始化、CBC模式运算及Base64编码转换四个环节。以下代码展示了完整的辅助函数库。
package helpers
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
)
// PKCS7填充:当数据长度不是块大小的整数倍时进行补足
func pkcs7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// PKCS7去填充:还原原始数据
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("加密字符串错误!")
}
unPadding := int(data[length-1])
return data[:(length - unPadding)], nil
}
// AesEncrypt:使用AES-CBC模式加密
func AesEncrypt(data []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
encryptBytes := pkcs7Padding(data, blockSize)
crypted := make([]byte, len(encryptBytes))
// 使用密钥的前16个字节作为IV(对于AES-128/192/256通用)
// 注意:生产环境建议使用独立的随机IV
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
blockMode.CryptBlocks(crypted, encryptBytes)
return crypted, nil
}
// AesDecrypt:使用AES-CBC模式解密
func AesDecrypt(data []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
// CBC解密也需要正确的IV
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
crypted := make([]byte, len(data))
blockMode.CryptBlocks(crypted, data)
// 去除填充
crypted, err = pkcs7UnPadding(crypted)
if err != nil {
return nil, err
}
return crypted, nil
}
// EncryptByAes:加密后转Base64字符串
func EncryptByAes(data []byte, key []byte) (string, error) {
res, err := AesEncrypt(data, key)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(res), nil
}
// DecryptByAes:Base64解码后进行解密
func DecryptByAes(data string, key []byte) ([]byte, error) {
dataByte, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return nil, err
}
return AesDecrypt(dataByte, key)
}注意事项:
- 密钥长度: Go的aes包支持128、192和256位密钥。传入key长度需严格对应(16、24或32字节)。
- 初始向量(IV): 上述示例简化处理,直接使用Key前几位作为IV。在生产中,每次加密应生成随机IV并与密文一起存储(如拼接在密文头部)。
第1步:字符串加解密调用
直接对内存中的文本数据进行加解密,适用于配置信息、用户令牌等场景。
func testStringEncryption() {
key := []byte("ABCDABCDABCDABCD") // 16字节密钥
text := []byte("Sensitive Data")
// 加密
encStr, _ := EncryptByAes(text, key)
fmt.Printf("Original: %s\nEncrypted: %s\n", text, encStr)
// 解密
decBytes, _ := DecryptByAes(encStr, key)
fmt.Printf("Decrypted: %s\n", string(decBytes))
}大文件加解密策略
对于GB级别的大文件,一次性读入内存会导致OOM。应采用分块读取(Chunking)或流式处理的方式。以下方法将大文件按固定大小分块加密,追加写入新文件。
第2步:实现文件分块加密
设定分块大小(如10MB),逐块读取原文,加密后以Base64形式每行写入目标文件,确保解密时可按行区分。
const MaxChunkSize = 10 * 1024 * 1024 // 10MB per chunk
var DefaultKey = []byte("YOUR_SECURE_16BYTE_KEY")
func EncryptFile(filePath, outName string) (err error) {
fIn, err := os.Open(filePath)
if err != nil {
return err
}
defer fIn.Close()
fInfo, _ := fIn.Stat()
fileSize := fInfo.Size()
fOut, err := os.OpenFile("encrypt_"+outName, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer fOut.Close()
bufWriter := bufio.NewWriter(fOut)
buffer := make([]byte, MaxChunkSize)
for {
n, err := fIn.Read(buffer)
if n > 0 {
chunk := buffer[:n]
encrypted, _ := EncryptByAes(chunk, DefaultKey)
// Base64换行标记,便于后续按行分割
bufWriter.WriteString(encrypted + "\n")
}
if err == io.EOF {
break
}
if err != nil {
return err
}
}
bufWriter.Flush()
return nil
}第3步:实现文件逆向解密
逐行读取密文文件,执行Base64解码及AES解密,重新拼装到原文件路径。
func DecryptFile(filePath, outName string) (err error) {
fIn, err := os.Open(filePath)
if err != nil {
return err
}
defer fIn.Close()
fOut, err := os.OpenFile("decrypt_"+outName, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer fOut.Close()
scanner := bufio.NewScanner(fIn)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
decrypted, err := DecryptByAes(line, DefaultKey)
if err != nil {
// 遇到无法解密的行可能意味着损坏,此处可记录日志
continue
}
_, err = fOut.Write(decrypted)
if err != nil {
return err
}
}
return scanner.Err()
}优化建议与排错
虽然分块读取能解决内存问题,但上述简易实现存在以下改进空间:
- IV独立性: 每个分块的加密最好拥有独立生成的IV,防止分块间的相关性分析攻击。
- HMAC验证: 建议在加密流中加入HMAC-SHA256签名,在解密时验证完整性,防止密文被篡改。
- 错误处理: 原文中大量使用了 `_= ` 忽略错误,在生产环境中必须显式捕获并返回错误,防止静默失败导致数据不一致。
以上就是Go语言实现AES加密解密及文件处理的详细内容,更多关于Go AES加密实践的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









