logo
Public
0
0
WeChat Login
chore: 切换模块路径为 cnb.cool/svn/zip

Go ZIP 加密与高级压缩库

Go Reference Go Report Card License Go Version

cnb.cool/svn/zip 是面向生产交付的 Go archive/zip 增强分支,支持密码保护、WinZip AES、ZipCrypto 兼容、LZMA、Zstandard、fs.FS、原始数据复制 API,以及更严格的路径与加密配置校验。

特性

加密

  • AES 加密:使用 WinZip AES 格式,支持 AES-128、AES-192、AES-256。
  • ZipCryptoStandardEncryption 用于兼容 Info-ZIP zip / unzip 等遗留工具。
  • 安全默认值:只调用 SetPassword 且未显式指定方法时,默认使用 AES-256。
  • PBKDF2 控制
    • DefaultPBKDF2Iterations = 100000 用于新写出的 AES 文件。
    • MinPBKDF2Iterations = 1000,写入与读取私有元数据时都会校验。
    • LegacyPBKDF2Iterations = 1000 用于严格兼容 WinZip AES / 7-Zip。
  • 自动 PBKDF2 元数据:非 Legacy 迭代次数会写入 cnb.cool/svn/zip 私有 extra field,本库读取时无需额外配置即可恢复。
  • 认证保护:AES 数据使用 HMAC 认证,默认启用立即认证。

压缩

方法ID说明
Store0不压缩
Deflate8标准 ZIP 默认压缩
LZMA14压缩率更高,速度较慢
Zstandard93速度快,压缩率良好

LZMA 与 Zstandard 可由本库读写;外部 ZIP 工具是否支持取决于具体工具能力。

现代 API

  • Go 1.23+ 迭代器:Reader.Files()Reader.EncryptedFiles()
  • Go 1.24+ fs.FS:通过 Reader.Open 集成标准文件系统接口。
  • 原始数据 API:File.OpenRawWriter.CreateRawWriter.Copy
  • 文件系统导入:Writer.AddFS
  • 实例级 compressor / decompressor 注册。
  • 通过 FileHeader.Modified 支持扩展时间戳。
  • 对归档条目名进行加固校验。

安装

go get cnb.cool/svn/zip

要求:

  • Go 1.24 或更高版本
  • github.com/ulikunitz/xz 用于 LZMA
  • github.com/klauspost/compress 用于 Zstandard
  • golang.org/x/crypto/pbkdf2 用于 AES 密钥派生

快速开始

AES-256 加密

package main

import (
    "log"
    "os"

    "cnb.cool/svn/zip"
)

func main() {
    f, err := os.Create("secret.zip")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    zw := zip.NewWriter(f)
    defer zw.Close()

    w, err := zw.Encrypt("secret.txt", "strong-password", zip.AES256Encryption)
    if err != nil {
        log.Fatal(err)
    }
    if _, err := w.Write([]byte("confidential data")); err != nil {
        log.Fatal(err)
    }
}

高迭代 AES 与自动元数据

fh := &zip.FileHeader{
    Name:             "confidential.txt",
    Method:           zip.Deflate,
    PBKDF2Iterations: 100000,
}
fh.SetPassword("strong-password")
fh.SetEncryptionMethod(zip.AES256Encryption)

w, err := zw.CreateHeader(fh)
if err != nil {
    log.Fatal(err)
}
w.Write([]byte("secret"))

新写出的 AES 文件若未设置 PBKDF2Iterations,cnb.cool/svn/zip 会使用 DefaultPBKDF2Iterations 并自动写入私有元数据。读取没有该元数据的第三方 AES 归档时,会回退到 LegacyPBKDF2Iterations

面向系统 unzip 的遗留 ZipCrypto

fh := &zip.FileHeader{Name: "legacy.txt", Method: zip.Deflate}
fh.SetPassword("password")
fh.SetEncryptionMethod(zip.StandardEncryption)

w, err := zw.CreateHeader(fh)
if err != nil {
    log.Fatal(err)
}
w.Write([]byte("compatible with Info-ZIP unzip"))

StandardEncryption 只应在必须兼容旧工具时使用。

读取加密文件

r, err := zip.OpenReader("secret.zip")
if err != nil {
    log.Fatal(err)
}
defer r.Close()

for _, f := range r.File {
    if f.IsEncrypted() {
        f.SetPassword("strong-password")
        // 对没有 cnb.cool/svn/zip 元数据、但使用非标准迭代次数的第三方 AES 文件,
        // 如果已知迭代次数,可显式设置 f.PBKDF2Iterations。
    }
    rc, err := f.Open()
    if err != nil {
        log.Fatal(err)
    }
    data, err := io.ReadAll(rc)
    rc.Close()
    if err != nil {
        log.Fatal(err)
    }
    _ = data
}

高级用法

迭代器

r, _ := zip.OpenReader("archive.zip")
defer r.Close()

for f := range r.Files() {
    fmt.Println(f.Name)
}

for f := range r.EncryptedFiles() {
    f.SetPassword("password")
}

fs.FS

r, _ := zip.OpenReader("archive.zip")
defer r.Close()

var fsys fs.FS = &r.Reader
fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
    fmt.Println(path)
    return err
})

不重压的原始复制

src, _ := zip.OpenReader("source.zip")
defer src.Close()

dstFile, _ := os.Create("copy.zip")
defer dstFile.Close()

zw := zip.NewWriter(dstFile)
defer zw.Close()

for _, f := range src.File {
    if err := zw.Copy(f); err != nil {
        log.Fatal(err)
    }
}

添加文件系统目录树

zw := zip.NewWriter(file)
defer zw.Close()

if err := zw.AddFS(os.DirFS("./myproject")); err != nil {
    log.Fatal(err)
}

实例级 compressor 注册

const CustomMethod = 99

type nopWriteCloser struct{ io.Writer }

func (nopWriteCloser) Close() error { return nil }

zw := zip.NewWriter(file)
zw.RegisterCompressor(CustomMethod, func(w io.Writer) (io.WriteCloser, error) {
    return nopWriteCloser{w}, nil
})

r, _ := zip.OpenReader("archive.zip")
r.RegisterDecompressor(CustomMethod, func(r io.Reader) io.ReadCloser {
    return io.NopCloser(r)
})

校验与错误

本库会对不安全或不一致的输入尽早失败:

  • 不安全条目名返回 ErrInsecurePath
  • 缺少密码返回 ErrPassword
  • 不支持的加密配置返回 ErrEncryption
  • PBKDF2 迭代次数低于 MinPBKDF2Iterations 返回 ErrWeakPBKDF2
  • PBKDF2 迭代次数无法编码返回 ErrInvalidPBKDF2
  • 认证失败返回 ErrAuthentication
  • 不支持的压缩方法返回 ErrAlgorithm

条目名必须是相对路径、使用 / 分隔。空名称、绝对路径、..、反斜杠和 Windows 盘符路径都会被拒绝。

兼容性

各工具加密支持

工具ZipCrypto / StandardEncryption使用 LegacyPBKDF2Iterations 的 AEScnb.cool/svn/zip 高迭代 AES
cnb.cool/svn/zip
Info-ZIP zip / unzip
7-Zip
WinZip
Windows 资源管理器
macOS Archive Utility

本机互操作验证

当前代码已使用本机 zip / unzip 做过双向验证:

  • cnb.cool/svn/zip 写出的普通归档可被 unzip 测试与解压。
  • cnb.cool/svn/zip 写出的 StandardEncryption 归档可被 unzip -P 解密。
  • Info-ZIP 写出的普通归档可被 cnb.cool/svn/zip 读取。
  • Info-ZIP 写出的 ZipCrypto 归档可被 cnb.cool/svn/zip 通过 SetPassword 读取。

安全说明

  1. StandardEncryption / ZipCrypto 已不具备现代密码学安全性,只用于遗留互通。
  2. cnb.cool/svn/zip 内部流转优先使用 AES-256 与 DefaultPBKDF2Iterations 或更高迭代次数。
  3. 只有在外部 WinZip AES 工具必须解密时,才使用 LegacyPBKDF2Iterations
  4. 除非调用方明确接受未认证明文流,否则保持 DeferAuth 默认值 false
  5. 读取器会拒绝不安全条目名,但真正落盘时调用方仍需负责目标路径策略。

限制

  • 不支持 multi-disk / spanned archives。
  • 不支持 split archives。
  • AES 高迭代元数据是 cnb.cool/svn/zip 私有格式。
  • LZMA 与 Zstandard 的外部工具支持度不一。

文档

许可证

BSD 3-Clause License。见 LICENSE

致谢