logo
Public
0
0
WeChat Login

Go ZIP with Encryption and Advanced Compression

Go Reference Go Report Card License Go Version

cnb.cool/svn/zip is a production-oriented fork of Go's archive/zip with password protection, WinZip AES support, ZipCrypto compatibility, LZMA, Zstandard, fs.FS, raw copy APIs, and hardened path/encryption validation.

Features

Encryption

  • AES encryption: AES-128, AES-192, AES-256 using WinZip AES format.
  • ZipCrypto: StandardEncryption for legacy tools such as Info-ZIP zip / unzip.
  • Secure defaults: SetPassword without an explicit method defaults to AES-256.
  • PBKDF2 controls:
    • DefaultPBKDF2Iterations = 100000 for new AES files.
    • MinPBKDF2Iterations = 1000 enforced on write/read metadata.
    • LegacyPBKDF2Iterations = 1000 for strict WinZip AES / 7-Zip interoperability.
  • Automatic PBKDF2 metadata: non-legacy iteration counts are stored in a private cnb.cool/svn/zip extra field so this library can read them without out-of-band configuration.
  • Authentication: AES data is HMAC-authenticated; immediate authentication remains the default.

Compression

MethodIDNotes
Store0No compression
Deflate8Standard ZIP default
LZMA14Better ratio, slower
Zstandard93Fast compression with good ratio

LZMA and Zstandard are supported by this library; external ZIP tools may require matching support.

Modern APIs

  • Go 1.23+ iterators: Reader.Files() and Reader.EncryptedFiles().
  • Go 1.24+ fs.FS support via Reader.Open.
  • Raw data APIs: File.OpenRaw, Writer.CreateRaw, and Writer.Copy.
  • Filesystem import: Writer.AddFS.
  • Instance-level compressor/decompressor registration.
  • Extended timestamp support via FileHeader.Modified.
  • Hardened path validation for archive entry names.

Installation

go get cnb.cool/svn/zip

Requirements:

  • Go 1.24 or later
  • github.com/ulikunitz/xz for LZMA
  • github.com/klauspost/compress for Zstandard
  • golang.org/x/crypto/pbkdf2 for AES key derivation

Quick Start

AES-256 encryption

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

High-iteration AES with automatic metadata

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"))

When PBKDF2Iterations is not set for new AES files, cnb.cool/svn/zip uses DefaultPBKDF2Iterations and writes private metadata automatically. When reading third-party AES archives without that metadata, the reader falls back to LegacyPBKDF2Iterations.

Legacy ZipCrypto for system unzip

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"))

Use StandardEncryption only when compatibility with old tools is required.

Reading encrypted files

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")
        // For third-party AES files with non-standard iterations and no cnb.cool/svn/zip
        // metadata, set f.PBKDF2Iterations explicitly if known.
    }
    rc, err := f.Open()
    if err != nil {
        log.Fatal(err)
    }
    data, err := io.ReadAll(rc)
    rc.Close()
    if err != nil {
        log.Fatal(err)
    }
    _ = data
}

Advanced Usage

Iterators

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

Raw copy without recompression

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

Add a filesystem tree

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

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

Instance-level compressor registration

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

Validation and Errors

The package now fails early for unsafe or inconsistent inputs:

  • Unsafe member names return ErrInsecurePath.
  • Missing password returns ErrPassword.
  • Unsupported encryption configuration returns ErrEncryption.
  • PBKDF2 iterations below MinPBKDF2Iterations return ErrWeakPBKDF2.
  • PBKDF2 iterations that cannot be encoded return ErrInvalidPBKDF2.
  • Authentication failures return ErrAuthentication.
  • Unsupported compression methods return ErrAlgorithm.

Entry names must be relative slash-separated ZIP names. Empty names, absolute paths, .., backslashes, and Windows drive paths are rejected.

Compatibility

Encryption support by tool

ToolZipCrypto / StandardEncryptionAES with LegacyPBKDF2IterationsAES with cnb.cool/svn/zip high iterations
cnb.cool/svn/zip
Info-ZIP zip / unzip
7-Zip
WinZip
Windows Explorer
macOS Archive Utility

Local interoperability check

The current code was verified with the host zip / unzip tools in both directions:

  • cnb.cool/svn/zip plain archives can be tested and extracted by unzip.
  • cnb.cool/svn/zip StandardEncryption archives can be decrypted by unzip -P.
  • Info-ZIP plain archives can be read by cnb.cool/svn/zip.
  • Info-ZIP ZipCrypto archives can be read by cnb.cool/svn/zip with SetPassword.

Security Notes

  1. StandardEncryption / ZipCrypto is cryptographically weak. Use it only for legacy interoperability.
  2. Prefer AES-256 with DefaultPBKDF2Iterations or higher for cnb.cool/svn/zip-only workflows.
  3. Use LegacyPBKDF2Iterations only when external WinZip AES tools must decrypt the archive.
  4. Keep DeferAuth at its default false unless the caller deliberately accepts unauthenticated plaintext streaming.
  5. Do not extract entries without checking destination paths; the reader rejects unsafe names, but callers still own filesystem write policy.

Limitations

  • Multi-disk / spanned archives are not supported.
  • Split archives are not supported.
  • AES high-iteration metadata is cnb.cool/svn/zip-specific.
  • External support for LZMA and Zstandard varies by tool.

Documentation

License

BSD 3-Clause License. See LICENSE.

Credits

About

Go 的 archive/zip 用于添加受密码保护的 zip 文件的读/写

Language
Go100%