Go语言实现AES加密解密:字符串与文件处理详解

竹里
竹里
发布时间:2026-10-15 | 4人浏览过
原创

在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)
}

注意事项:

  1. 密钥长度: Go的aes包支持128、192和256位密钥。传入key长度需严格对应(16、24或32字节)。
  2. 初始向量(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()
}

优化建议与排错

虽然分块读取能解决内存问题,但上述简易实现存在以下改进空间:

  1. IV独立性: 每个分块的加密最好拥有独立生成的IV,防止分块间的相关性分析攻击。
  2. HMAC验证: 建议在加密流中加入HMAC-SHA256签名,在解密时验证完整性,防止密文被篡改。
  3. 错误处理: 原文中大量使用了 `_= ` 忽略错误,在生产环境中必须显式捕获并返回错误,防止静默失败导致数据不一致。

以上就是Go语言实现AES加密解密及文件处理的详细内容,更多关于Go AES加密实践的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表