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

时间:2018-08-23 17:06:11

标签: php encryption openssl php-openssl synology

这是我的问题,

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

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

  • Raspberry Pi 3
  • 戴尔Poweredge T110
  • Windows 10上的IIS
  • Synology DS1815 +

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

  • Synology RS212
  • Synology DS112 +

CPU有限制吗?

php.ini的参数会影响吗?

我在多台服务器上测试了完全相同的代码,并且在提到的两个Synology上都无法正常工作...

这是我的加密/解密课程:

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日志,没有任何报告。

我已经搜寻了几天,却不了解问题的原因。

希望有人可以帮助我:-)

1 个答案:

答案 0 :(得分:1)

将其大块

这就是我的工作(使用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

这样,您就不会用尽内存来加密大型文件。

请注意,这是我使用的一个更大的类的一部分,所以我不得不修剪一些东西并进行一些未测试的更改。

https://github.com/phpseclib/phpseclib

干杯。