在Golang中实现简单的文件加密与解密,可以基于对称加密算法完成,其中AES算法因为安全性和性能的平衡,是常用的选择。下面将通过完整的代码示例,介绍从文件读取、加密处理到写入,再到解密还原的全流程实现。

核心依赖与加密原理
实现文件加密解密需要用到Golang标准库中的crypto/aes、crypto/cipher、io/ioutil等包。AES算法属于分组加密算法,需要将明文按固定分组长度处理,同时为了保证安全性,通常会结合随机生成的初始化向量IV使用。
加密的核心流程为:生成随机密钥和IV,读取原始文件内容,对内容进行AES加密,将IV和加密后的内容写入新文件。解密时则反向操作,先从加密文件中读取IV,再用密钥和IV解密内容,还原为原始文件。
完整实现代码
加密函数实现
下面是文件加密的具体实现代码,使用AES的CBC模式进行加密:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
"os"
)
// EncryptFile 加密文件
// srcPath 原始文件路径,dstPath 加密后文件路径,key 加密密钥(长度需为16、24、32字节对应AES-128、AES-192、AES-256)
func EncryptFile(srcPath, dstPath string, key []byte) error {
// 读取原始文件内容
plaintext, err := os.ReadFile(srcPath)
if err != nil {
return fmt.Errorf("读取原始文件失败: %v", err)
}
// 创建AES块
block, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("创建AES块失败: %v", err)
}
// 生成随机的初始化向量IV,长度等于块大小
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return fmt.Errorf("生成IV失败: %v", err)
}
// 创建CBC模式加密器
mode := cipher.NewCBCEncrypter(block, iv)
// 对明文进行填充,保证长度是块大小的整数倍
padding := aes.BlockSize - len(plaintext)%aes.BlockSize
padtext := make([]byte, len(plaintext)+padding)
copy(padtext, plaintext)
for i := len(plaintext); i < len(padtext); i++ {
padtext[i] = byte(padding)
}
// 加密数据
ciphertext := make([]byte, len(padtext))
mode.CryptBlocks(ciphertext, padtext)
// 将IV和加密后的内容写入目标文件,前aes.BlockSize字节为IV
dstFile, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("创建加密文件失败: %v", err)
}
defer dstFile.Close()
if _, err := dstFile.Write(iv); err != nil {
return fmt.Errorf("写入IV失败: %v", err)
}
if _, err := dstFile.Write(ciphertext); err != nil {
return fmt.Errorf("写入加密内容失败: %v", err)
}
return nil
}
解密函数实现
对应的文件解密实现代码如下:
// DecryptFile 解密文件
// srcPath 加密文件路径,dstPath 解密后原始文件路径,key 解密密钥
func DecryptFile(srcPath, dstPath string, key []byte) error {
// 读取加密文件内容
encryptedData, err := os.ReadFile(srcPath)
if err != nil {
return fmt.Errorf("读取加密文件失败: %v", err)
}
// 校验加密文件长度是否合法
if len(encryptedData) < aes.BlockSize {
return fmt.Errorf("加密文件长度非法")
}
// 提取前aes.BlockSize字节作为IV
iv := encryptedData[:aes.BlockSize]
ciphertext := encryptedData[aes.BlockSize:]
// 创建AES块
block, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("创建AES块失败: %v", err)
}
// 创建CBC模式解密器
mode := cipher.NewCBCDecrypter(block, iv)
// 解密数据
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
// 去除填充内容
padding := int(plaintext[len(plaintext)-1])
if padding > aes.BlockSize || padding == 0 {
return fmt.Errorf("填充内容非法")
}
for i := len(plaintext) - padding; i < len(plaintext); i++ {
if plaintext[i] != byte(padding) {
return fmt.Errorf("填充内容校验失败")
}
}
plaintext = plaintext[:len(plaintext)-padding]
// 将解密后的内容写入目标文件
if err := os.WriteFile(dstPath, plaintext, 0644); err != nil {
return fmt.Errorf("写入解密文件失败: %v", err)
}
return nil
}
测试调用示例
可以通过下面的main函数测试加密解密功能:
func main() {
// 密钥长度需要为16、24、32字节,对应不同的AES加密强度
key := []byte("1234567890123456") // 16字节,AES-128
// 加密文件
err := EncryptFile("./test.txt", "./test_encrypted.bin", key)
if err != nil {
fmt.Printf("加密失败: %vn", err)
return
}
fmt.Println("文件加密成功")
// 解密文件
err = DecryptFile("./test_encrypted.bin", "./test_decrypted.txt", key)
if err != nil {
fmt.Printf("解密失败: %vn", err)
return
}
fmt.Println("文件解密成功")
}
注意事项
- 密钥的安全管理非常重要,不要将密钥硬编码在代码中,生产环境建议使用密钥管理服务存储密钥。
- AES的CBC模式需要随机IV,每次加密的IV都应该是随机生成的,IV不需要保密,但需要和加密内容一起存储。
- 如果加密的文件体积较大,上面的实现会一次性读取全部内容到内存,可以改为流式读写的方式处理大文件,避免内存占用过高。
- 解密时需要校验填充内容的合法性,避免解密被篡改的文件导致程序异常。