将函数从 python 重写为 php

问题描述 投票:0回答:1

我需要在php中实现文件解密。由于我的尝试,我得到了一个损坏的档案

我使用代码作为基础https://github.com/samloader/samloader/tree/master

我对这个部分感兴趣

https://github.com/samloader/samloader/blob/0e53d8032699a4039ea6f5310ebec05f8f417f07/samloader/main.py#L72

    elif args.command == "decrypt":
        getkey = crypt.getv4key if args.enc_ver == 4 else crypt.getv2key
        key = getkey(args.fw_ver, args.dev_model, args.dev_region)
        length = os.stat(args.in_file).st_size
        with open(args.in_file, "rb") as inf:
            with open(args.out_file, "wb") as outf:
                crypt.decrypt_progress(inf, outf, key, length)

https://github.com/samloader/samloader/blob/0e53d8032699a4039ea6f5310ebec05f8f417f07/samloader/crypt.py#L35

def decrypt_progress(inf, outf, key, length):
    """ Decrypt a stream of data while showing a progress bar. """
    cipher = AES.new(key, AES.MODE_ECB)
    if length % 16 != 0:
        raise Exception("invalid input block size")
    chunks = length//4096+1
    pbar = tqdm(total=length, unit="B", unit_scale=True)
    for i in range(chunks):
        block = inf.read(4096)
        if not block:
            break
        decblock = cipher.decrypt(block)
        if i == chunks - 1:
            outf.write(unpad(decblock))
        else:
            outf.write(decblock)
        pbar.update(4096)

部分https://github.com/samloader/samloader/blob/0e53d8032699a4039ea6f5310ebec05f8f417f07/samloader/crypt.py#L18

def getv4key(version, model, region):
    """ Retrieve the AES key for V4 encryption. """
    client = fusclient.FUSClient()
    version = versionfetch.normalizevercode(version)
    req = request.binaryinform(version, model, region, client.nonce)
    resp = client.makereq("NF_DownloadBinaryInform.do", req)
    root = ET.fromstring(resp)
    fwver = root.find("./FUSBody/Results/LATEST_FW_VERSION/Data").text
    logicval = root.find("./FUSBody/Put/LOGIC_VALUE_FACTORY/Data").text
    deckey = request.getlogiccheck(fwver, logicval)
    return hashlib.md5(deckey.encode()).digest()

我重写了,这行有疑问

hashlib.md5(deckey.encode()).digest()

我是这样的 `$deckey = 'AU77D7K3SAU/D3UU';

$key = hash('md5', $deckey, true);`

这个是对的吗?

以及负责我解密的代码部分

<?php
        $source = 'file.zip.enc4';
        $dest = 'file.zip';
        
        $source = fopen($source, 'r');
        $dest = fopen($dest, 'w');
        $chunkSize = 4096;
        while (!feof($source)) {
            $chunk = fread($source, $chunkSize + 1);
            if (!$chunk) {
                break;
            }
            $encryptedChunk = openssl_encrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
            fwrite($dest, $encryptedChunk);
        }
        fclose($source);
        fclose($dest);

帮我找出我做错了什么

测试文件

python php encryption openssl php-openssl
1个回答
0
投票

您建议的通过MD5导出密钥的移植是正确的。

但是,填充存在一个问题:从Python代码可以得出结论,在加密过程中,除最后一个块之外的所有块都禁用了PKCS#7填充。使用 PHP 代码解密时必须考虑到这一点。

一个可能的 PHP 实现(基于您的代码)是:

$source = 'file.zip.enc4';
$dest = 'file.zip';

$deckey = "AU77D7K3SAU/D3UU";
$key = hash('md5', $deckey, true);

$chunkSize = 4096;
$fileSize = filesize($source);

$source = fopen($source, 'rb');
$dest = fopen($dest, 'wb');

$totalBytesRead = 0;
while (!feof($source)) {
    $chunk = fread($source, $chunkSize);
    if (!$chunk) {
        break;
    }
    $totalBytesRead += strlen($chunk);
    $padding = ($totalBytesRead < $fileSize) ? OPENSSL_ZERO_PADDING : 0;
    $decryptedChunk = openssl_decrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA | $padding);
    fwrite($dest, $decryptedChunk);
}

fclose($source);
fclose($dest);

说明:

OPENSSL_ZERO_PADDING
禁用 PHP/OpenSSL 中的默认 PKCS#7 填充(请注意,该标志的名称具有误导性,可能会假设启用零填充;但实际上该标志禁用默认填充)。
上面的代码确定了读取的总字节数。如果小于文件大小,则刚刚读取的块不是最后一个块,因此必须禁用默认填充,否则它是最后一个块,并且默认填充保持启用状态。


解密后的文件file.zip(4337718623字节)比加密文件file.zip.enc4(4337718624字节)小一个字节,这是由于最后一个块的填充所致。
解密的文件可以成功提取并提供包含 5 个 .tar.md5 文件的存档。

© www.soinside.com 2019 - 2024. All rights reserved.