Golang AES-CBC-128解密后二进制数据如何正确保存为tar.gz归档文件
问题修复方案
已知错误点梳理
- 初始版本核心错误1:密钥处理逻辑错误,
aes.NewCipher需要传入16字节长度的原始二进制密钥,你写的key := hex.EncodeToString(decryptedKey)会把原始密钥转为32字符的十六进制字符串,传入后解密逻辑完全失效。 - 初始版本核心错误2:错误切掉密文前16字节,代码中
f = f[aes.BlockSize:]直接删除了密文第一块数据,而你使用的OpenSSL命令是显式传IV、没有把IV存在密文开头,这步操作直接导致解密内容从根源错误。 - 第二版本核心错误1:循环起始偏移错误,初始
ReadProg设为16相当于直接跳过了文件前16字节密文,解密后文件天然少16字节内容。 - 第二版本核心错误2:填充处理不严谨,仅读取最后一个字节作为填充长度,没有做1-16区间的合法性校验,异常情况下会多删有效数据,你遇到的少20字节报错就是填充计算错误导致。
- 第二版本核心错误3:未处理
Read方法的有效返回字节数,os.File.Read不保证每次都读满16字节,直接用整块缓冲区解密会引入无效数据。
完整正确实现
对应你给出的OpenSSL命令逻辑,适配AES-128-CBC解密、PKCS7填充的完整代码如下:
package main import ( "crypto/aes" "crypto/cipher" "encoding/hex" "log" "os" "path/filepath" ) // PKCS7Unpadding 移除PKCS7填充,适配OpenSSL enc默认填充规则 func PKCS7Unpadding(data []byte) ([]byte, error) { length := len(data) if length == 0 { return nil, nil } padLen := int(data[length-1]) // 校验填充长度合法性 if padLen < 1 || padLen > aes.BlockSize { return data, nil } // 校验所有填充字节是否等于填充长度,可选校验逻辑,增强兼容性 for i := length - padLen; i < length; i++ { if data[i] != byte(padLen) { return data, nil } } return data[:length-padLen], nil } func main() { // 替换为你实际的参数值 hexKey := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // AES-128对应32位十六进制字符串 hexIV := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // IV固定为32位十六进制字符串 encryptedFilePath := "foo.tar.gz.enc" outputPath := filepath.Join(".", "foo.tar.gz") // 1. 十六进制密钥、IV转二进制 key, err := hex.DecodeString(hexKey) if err != nil { log.Fatalf("密钥解码失败: %s", err) } iv, err := hex.DecodeString(hexIV) if err != nil { log.Fatalf("IV解码失败: %s", err) } // 2. 读取加密文件内容,大文件可自行修改为流式读写逻辑 encData, err := os.ReadFile(encryptedFilePath) if err != nil { log.Fatalf("读取加密文件失败: %s", err) } // 3. 初始化AES解密器 block, err := aes.NewCipher(key) if err != nil { log.Fatalf("初始化AES失败: %s", err) } // 4. CBC模式解密 mode := cipher.NewCBCDecrypter(block, iv) plainData := make([]byte, len(encData)) mode.CryptBlocks(plainData, encData) // 5. 移除填充 plainData, err = PKCS7Unpadding(plainData) if err != nil { log.Fatalf("去填充失败: %s", err) } // 6. 输出最终tar.gz文件 err = os.WriteFile(outputPath, plainData, 0644) if err != nil { log.Fatalf("写入输出文件失败: %s", err) } log.Println("解密完成,输出文件路径:", outputPath) }
内容的提问来源于stack exchange,提问作者TempestBreach
相关产品推荐
相关产品推荐

