如何使用 PHP 加密和解密长度超过 65535 个字符的字符串

How to encrypt and decrypt strings longer than 65535 characters with PHP

这是我的问题,

我想加密 JSON 个在某些情况下可能很长的文件。 (有时包含 Base64 格式的图像)。

在以下测试服务器上,一切正常:

另一方面,在以下服务器上,(用于使用..)超过 65535 个字符的加密不起作用,服务器似乎崩溃了。

CPU有限制吗?

php.ini的一个参数会影响吗?

我在多个服务器上测试了完全相同的代码,在提到的两个 Synology 上,它都不起作用...

这是我的class加密/解密:

class PHP_AES_Cipher {

    private static $OPENSSL_CIPHER_NAME = "AES-256-CBC"; //Name of OpenSSL Cipher 
    private static $CIPHER_KEY_LEN = 32; 

    static function encrypt($key, $iv, $data) {
        if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0");
        } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); 
        }

        $encodedEncryptedData = base64_encode(openssl_encrypt($data, PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, $iv));
        $encodedIV = base64_encode($iv);
        $encryptedPayload = $encodedEncryptedData.":".$encodedIV;

        return $encryptedPayload;

    }


    static function decrypt($key, $data) {
        if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0");
        } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN);
        }

        $parts = explode(':', $data); //Separate Encrypted data from iv.
        $decryptedData = openssl_decrypt(base64_decode($parts[0]), PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, base64_decode($parts[1]));

        return $decryptedData;
    }
}

我是这样使用的:

$data = PHP_AES_Cipher::encrypt($key, $iv, $data);

$data = PHP_AES_Cipher::decrypt($key, $iv, $data);

假设在某些服务器上一切正常,我认为代码没有问题。我已经检查了 Apache 和 PHP 日志,没有什么可报告的。

找了好几天都没弄明白问题的原因。

希望有人能帮助我:-)

分块,

这就是我所做的(使用 PHPSecLib2)

/**
 * AES encrypt large files using streams and chunking
 * 
 * @param resource $stream
 * @param resource $outputStream
 * @param string $key
 * @throws SecExecption
 */
function streamSymEncode($stream, &$outputStream, $key, $chunkSize = 10240){
    if(!is_resource($stream)) throw new Execption('Resource expected[input]');  
    rewind($stream); //make sure the stream is rewound

    if(!is_resource($outputStream)) throw new Execption('Resource expected[output]');

    $Cipher = new AES(AES::MODE_CBC);
    $Cipher->setKey($key);
    //create the IV
    $iv = Random::string($Cipher->getBlockLength() >> 3);
    $Cipher->setIV($iv);

    if(strlen($iv_base64 = rtrim(base64_encode($iv), '=')) != 22) throw new Execption('IV lenght check fail');

    fwrite($outputStream, $iv_base64.'$'); //add the IV for later use when we decrypt

    while(!feof($stream)){
        $chunk = fread($stream, $chunkSize); 
        fwrite($outputStream, rtrim(base64_encode($Cipher->encrypt($chunk)),'=').':');
    }

    $stat = fstat($outputStream);

    ftruncate($outputStream, $stat['size'] - 1);    //trim off the last character, hanging ':'    
}

/**
 * AES decrypt large files that were previously encrypted using streams and chunking 
 * 
 * @param resource $stream
 * @param resource $outputStream
 * @param string $key
 * @throws SecExecption
 */
function streamSymDecode($stream, &$outputStream, $key){
    if(!is_resource($stream)) throw new Execption('Resource expected[input]');
    rewind($stream); //make sure the stream is rewound

    if(!is_resource($outputStream)) throw new Execption('Resource expected[output]');

    $Cipher = new AES(AES::MODE_CBC);
    $Cipher->setKey($key);

    $iv = base64_decode(fread($stream, 22) . '==');
    $Cipher->setIV($iv);

    fread($stream, 1); //advance 1 for the $

    $readLine = function(&$stream){
        $line = '';
        while(false !== ($char = fgetc($stream))){
            if($char == ':') break;
            $line .= $char;
        }
        return $line;
    };

    while(!feof($stream)){
        $chunk = $readLine($stream);

        $decrypted = $Cipher->decrypt(base64_decode($chunk.'=='));
        if(!$decrypted) throw new Execption('Failed to decode!');

        fwrite($outputStream, $decrypted);
    }       
}

它需要两个文件流资源,例如您从 fopen 获得的文件流资源和一个密钥。然后它使用相同的加密,但将文件分块到 $chunkSize 中,用 : 将它们分开,当它解码时,它将它拆分回块并重新组装所有内容。

结果是这样的(举例)

  IV$firstChunk:secondChunk:thirdChunk

这样您就不会 运行 在尝试加密大文件时内存不足。

请注意,这是我使用的啤酒 class 的一部分,所以我不得不 trim 一些东西并进行一些更改,我还没有测试过。

https://github.com/phpseclib/phpseclib

干杯。