使用 Rijndael 256(CakePHP 安全库)加密/解密某些文件类型会导致内容乱码

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

我正在使用 CakePHP 的

Security::rijndael()
函数来加密和解密文本和文件。 我之前直接使用
mcrypt
编写了一些代码,其工作方式相同,但后来我发现
Security::rijndael
并意识到我重新发明了轮子。 所以我遇到的问题无论如何都会发生。

如果我加密一个字符串、一个文本文件或一个 PDF 文档,下面的代码可以完美运行,并且我会得到正确的解密字符串/文件。 但是,如果我尝试加密 .doc、.docx 或图像文件,解密的文件会出现乱码。

这是执行加密/解密的代码

public static function encrypt($plainText, $key) {
    $plainText = base64_encode($plainText);

    //Hash key to ensure it is long enough
    $hashedKey = Security::hash($key);
    $cipherText = Security::rijndael($plainText, $hashedKey, 'encrypt');
    return base64_encode($cipherText);
}

public static function decrypt($cipherText, $key) {
    $cipherText = base64_decode($cipherText);

    $hashedKey = Security::hash($key);
    $plainText = Security::rijndael($cipherText, $hashedKey, 'decrypt');
    return base64_decode($plainText);
}

...这段代码实际上将文件呈现给用户(我编辑了代码以使其简单):

public function download($id){
    App::uses('File', 'Utility');
    $key = $this->User->getDocumentKey($id);
    $file = new File('my_encrypted_file.docx');
    $encrypted = $file->read();
    $decrypted = Encrypt::decrypt($encrypted, $key);

    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Content-Disposition: attachment; filename="my_decrypted_file.docx"');
    echo $decrypted;
    die();
}

更新 - 看起来加密是一个转移注意力的事情,因为即使没有加密和解密,文件也是乱码! 以下生成完全相同的损坏文件:

        header('Content-Disposition: attachment; filename="test.docx"');
        $file = new File($this->data['Model']['file']['tmp_name']);
        echo $file->read();
        die();
security cakephp encryption encoding rijndael
2个回答
0
投票

好吧,我找错了树。

无论出于何种原因(可能是某些 PHP 文件开头的空格?),在发送标头后立即添加

ob_clean();
已经解决了问题。


0
投票

我想我现在知道这个问题的原因了,它是208

中的
Security.php

$out .= rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");

由于 PHP 的

mycrypt()
使用
ZeroBytePadding
此行随后删除了填充。

问题是

.docx
文件(据我所知)以几个
Null
字符终止。如果仅删除其中一个,Word 无法打开该文件。
因此,
rtrim()
也会删除这些字节,即使它们不是填充的一部分。

要解决此问题,您可以在加密之前在文件末尾添加终止字符(例如

X
),并在解密后将其删除。这将防止从
.docx
文件中切断尾部零字节:

public static function encrypt($plainText, $key) {
    $plainText = base64_encode($plainText . "X"); // `X` terminates the file
    /* do encryption */
}

public static function decrypt($cipherText, $key) {
    /* do decrytion */
    return rtrim(base64_decode($plainText), "X"); // cut off the termination `X`
}
© www.soinside.com 2019 - 2024. All rights reserved.