如何解压使用 ANSI 编码密码加密的 zip 文件?

如何解压使用 ANSI 编码密码加密的 zip 文件?

我需要解压在 Windows 中生成的日语 zip 文件。我正在使用unzip

如果我使用,unzip files.zip我会得到错误的文件名。因此,根据这个问题,我使用unzip-O cp932解压缩它们。这样,我就能得到正确的文件名。

但是有些 zip 文件需要密码。我知道正确的密码,但unzip总是提示密码错误。

经过一番调查,我发现使用纯英文密码可以成功解压 zip 文件。也就是说,使用“Hello”这样的密码可以解压 zip 文件,但使用“こんにちは”这样的密码会导致“密码错误”。所以我猜这与字符编码有关。

事实上,我尝试过以下两种方法:

  1. unzip -O cp932 compressed.zip并在要求输入密码时粘贴“こんにちは”。
  2. unzip -O cp932 -P 'こんにちは' compressed.zip

它们都不起作用。

我发现了类似的问题这里没有答案。这个问题似乎是在寻求一种提供任何字节序列作为unzip密码的方法。如果这个问题有答案,那么解决方案也适用于我的问题,因为我可以手动将密码转换为正确的字符编码,并将转换后的字符串提供给unzip

答案1

如果使用非 Unicode 编解码器创建了 zip 文件,并且还使用密码加密,则您传递给unzip命令的密码也需要在此特定编解码器中编码为字节。在 Linux 上,您传递给的参数unzip将被读取为utf-8,这就是unzip -O cp932 -P 'こんにちは' compressed.zip不起作用的原因。

总而言之,您需要一种方法来提供以字节为cp932单位编码的密码以进行解压缩。没有简单的方法可以使用unzip命令来执行此操作,但可以使用 Python 脚本执行此操作:

from zipfile import ZipFile


def extract_zip(archive_name, out_path, pwd, codec):
    # password also needs to be encoded with codec
    password = pwd.encode(codec) if pwd else None
    # metadata_encoding argument is available in Python3.11
    with ZipFile(archive_name, "r", metadata_encoding=codec) as myzip:
        myzip.extractall(out_path, pwd=password)


extract_zip("compressed.zip", "output_dir", "こんにちは", "cp932")

答案2

zipfile根本不支持 AES 加密的 zip 文件,所以它不在考虑范围内。第三方模块pyzipper适用于 AES zip 文件,无论压缩算法如何。但它是基于zipfilePython 3.7 创建的,因此没有metadata_encoding。可以修改其源代码以添加该功能(相当简单),但这里有一个简短的脚本,只需在后期处理中修复文件名:

from pathlib import Path
import shutil


import pyzipper


def extract_encrypted_ANSI_zip(zipfile, password, encoding='gbk', create_new_folder=True):
    zipfile = Path(zipfile)

    output_dir = Path.cwd() / zipfile.stem if create_new_folder else Path.cwd()

    # create a temp folder to extract into, so we can fix the filenames before moving them to the output folder
    temp = Path('temp')
    while temp.exists():
        temp = temp.with_name(temp.stem + '_')

    temp.mkdir()

    with pyzipper.AESZipFile(str(zipfile), 'r', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as extracted_zip:
        extracted_zip.extractall(str(temp), pwd=password.encode(encoding, errors='replace'))

    all_files = [f for f in temp.rglob('*') if f.is_file()]
    for f in all_files:
        relative_path = f.relative_to(temp)
        old_path = str(relative_path)
        new_path = old_path.encode('cp437', errors='replace').decode(encoding, errors='replace')
        if new_path != old_path:
            print(old_path, '-->', new_path)

        f2 = (output_dir / new_path)
        f2.parent.mkdir(parents=True, exist_ok=True)
        try:
            f.rename(f2)
        except FileExistsError:
            print(f2, 'already exists')

    shutil.rmtree(temp)

extract_encrypted_ANSI_zip('comparessed.zip', password="こんにちは", encoding='cp932')

相关内容