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

时间:2014-08-20 12:31:20

标签: security cakephp encryption encoding rijndael

我正在使用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();

2 个答案:

答案 0 :(得分:0)

我想我现在知道这个问题的原因,它是line 208 in 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`
}

答案 1 :(得分:0)

好吧,我正在吠叫错误的树。

无论出于何种原因(某些PHP文件开头的空格可能?),在发送标题后立即添加ob_clean();已解决了问题。