Skip to content

feat: add WinZip AES CTR zip encryption#91

Closed
ayueyang wants to merge 7 commits into
traceless:mainfrom
ayueyang:feature/winzip-aes-only
Closed

feat: add WinZip AES CTR zip encryption#91
ayueyang wants to merge 7 commits into
traceless:mainfrom
ayueyang:feature/winzip-aes-only

Conversation

@ayueyang

@ayueyang ayueyang commented Jun 9, 2026

Copy link
Copy Markdown

这个 PR 做什么

新增一种算法:winzip-aes-ctr

命中该算法的路径上传文件时,会把原文件包装成标准单文件 WinZip AES ZIP:

  • ZIP 使用 Store,不压缩,方便把播放器 Range 映射到 ZIP 内部 payload。
  • 加密使用 WinZip AES 标准字段,7-Zip、WinRAR 等常规解压软件输入密码后可以解出正常文件。
  • 受管文件的 raw 存储名为 加密后的完整原文件名.zip,例如 视频.mp4 不再存成 .mp4.zip,raw 侧不暴露原扩展名。
  • ZIP 内部文件名为 payload.ext,例如 payload.mp4,保证解压出来能直接播放,同时不暴露原标题。
  • 在线播放时不缓存整个视频,只解析 ZIP 字段,按 Range 读取 payload 密文区并流式解密。

未命中 winzip-aes-ctr 的路径继续走原来的 aesctrrc4 等逻辑。

ZIP 识别方式

受管文件:

  • 文件名必须是 .zip
  • .zip 前面的部分能用当前路径密码和 winzip-aes-ctr 解出完整原文件名。
  • 代理列表显示解密后的原名,raw AList 仍只看到加密名加 .zip

外部压缩软件制作的 ZIP:

  • 例如 7-Zip、WinRAR 选择 WinZip AES、仅存储/不压缩、密码加密后得到的 abc.zipabc.mp4.zip
  • 默认不会在目录列表阶段解析这类外部 ZIP,列表里仍按普通 ZIP 显示,避免打开文件夹就批量读取 ZIP 头尾字段。
  • 用户点击具体 abc.zip 时才懒识别;命中后本次详情页按内部文件类型播放,不命中就保持普通 ZIP 下载页。
  • 如果配置里显式开启 zipAutoCache,列表请求只把外部 ZIP 放入低优先级后台队列,后台单并发探测并写缓存,列表响应本身不等待、不改名、不改 type。
  • 内部文件名只用于判断类型、MIME 和 Range 播放,例如内部是 电影.mp4 时按视频处理。

是否可播放不只看文件名。代码会解析 ZIP 原生字段,并且同时满足这些条件才按可播放 ZIP 处理:

  • EOCD / central directory 显示只有一个 entry。
  • entry 带加密标志。
  • ZIP method 是 WinZip AES method 99
  • 存在标准 WinZip AES extra field 0x9901,vendor 为 AE
  • AES extra field 里的 actual method 是 Store 0
  • compressed size = plain size + salt + password verifier + auth code,确认是可直接 Range 映射的 Store AES 数据。

普通 ZIP、多文件 ZIP、非 WinZip AES、或压缩方式不是 Store 的 ZIP 会保持普通 ZIP 处理,不进入在线播放解密逻辑;失败探测会写短期 negative cache,避免同一个普通 ZIP 被反复探测。

主要代码位置

  • node-proxy/src/utils/winZipAesZip.js

    • 生成和解析 WinZip AES ZIP。
    • 解析 EOCD、ZIP64、central directory、local header、AES extra field。
    • 计算 payloadOffset,把明文 Range 映射到 ZIP payload 密文 Range。
    • 重写响应头,让播放器看到明文 content-rangecontent-lengthcontent-type
  • node-proxy/src/utils/winZipAesZipCache.js

    • 外部 ZIP 懒识别缓存、negative cache 和可选后台探测队列。
    • 缓存用真实 ZIP size 校验,size 变化后重新探测。
  • node-proxy/src/utils/flowEnc.js

    • 接入新的 winzip-aes-ctr 类型。
    • 上传时输出 ZIP 包装流,下载时输出 AES-CTR 解密流。
  • node-proxy/app.js

    • /api/fs/get 点击文件时懒识别外部 ZIP,命中后返回明文大小和代理 raw_url。
    • /redirect/:key 和下载代理按 Range 解密返回。
    • WebDAV GET / HEAD 命中时返回明文媒体流。
  • node-proxy/src/encNameRouter.js

    • AList API 的列表、详情、上传、下载、重命名、移动/复制文件名映射。
    • 受管 .zip 在列表中恢复原名;外部 raw .zip 默认列表不解析,只在 zipAutoCache 开启时入队。
  • node-proxy/src/encDavHandle.js

    • WebDAV PROPFIND 显示虚拟文件名和明文大小。
    • 外部 ZIP 的 PROPFIND 默认不解析;GET / HEAD 时再懒识别。
  • node-proxy/src/dao/fileDao.js

    • 列表缓存刷新时保留同 size 的 ZIP 字段缓存,避免已解析信息被普通列表覆盖。
  • node-proxy/src/utils/commonUtil.ts

    • convertRealName() / convertShowName() 的 WinZip AES 文件名映射。
    • isEncryptedZipName() / isRawZipName() 区分受管文件和外部 ZIP。
    • getAListFileTypeByName() 用虚拟名或内部名还原 AList 类型。
  • enc-webui/src/views/setting-alist/index.vue

  • enc-webui/src/views/setting-webdav/index.vue

    • 设置页增加 WinZip-AES-CTR 选项,配置值为 winzip-aes-ctr
    • WinZip-AES-CTR 下增加 ZIP缓存 开关,保存到 zipAutoCache,默认关闭。
  • node-proxy/test/winZipAesZipTest.js

    • 覆盖受管文件名隐藏、ZIP 字段解析、Range 解密、pyzipper/外部 WinZip AES Store ZIP 兼容、缓存 size 校验。

性能测试

测试口径:从 /api/fs/get 开始计时,到 raw_urlRange: bytes=0-31 真正拿到包含 ftyp 的视频字节为止。每个视频 10 次,每次间隔 10 秒。

文件 ZIP字段缓存 总平均 /api/fs/get 平均 Range 拿到视频字节平均 min / max
1920-1080-4205-h264.mp4 1.448s 0.029s 1.418s 0.835 / 4.811s
2026-01-08 17-59-30.mp4 1.037s 0.024s 1.012s 0.811 / 1.412s
1920-1080-4205-h264.mp4 1.861s 0.947s 0.913s 1.624 / 2.110s
2026-01-08 17-59-30.mp4 2.018s 1.029s 0.988s 1.663 / 3.511s

结果:受管 WinZip AES ZIP 开启 zipInfoCache 后,/api/fs/get 不再重复解析 ZIP 字段,从约 0.95s - 1.03s 降到约 0.02s - 0.03s。总耗时主要剩远端 Range 请求;以上请求均返回 206,首段都包含 ftyp

验证

本地已验证:

npm exec -- ts-node -r tsconfig-paths/register test/winZipAesZipTest.js
git diff --check
npm run webpack

结果:

  • winZipAesZipTest ok
  • git diff --check 通过
  • npm run webpack 通过,仅有原项目 log4js 动态依赖 warning

也做过本地手动验证:

  • raw AList 只显示加密后的 .zip
  • 代理列表显示原始 .mp4
  • 网页播放 Range 返回 206 video/mp4,首段包含 ftyp
  • WebDAV PROPFIND 显示明文大小,GET Range 返回可播放 MP4 数据。

@ayueyang ayueyang closed this Jun 9, 2026
@ayueyang ayueyang reopened this Jun 9, 2026
@ayueyang ayueyang force-pushed the feature/winzip-aes-only branch from adcf510 to 6d4aa22 Compare June 9, 2026 11:30
@ayueyang ayueyang closed this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant