如何使用 PHP 加密和解密长度超过 65535 个字符的字符串
How to encrypt and decrypt strings longer than 65535 characters with PHP
这是我的问题,
我想加密 JSON 个在某些情况下可能很长的文件。 (有时包含 Base64 格式的图像)。
在以下测试服务器上,一切正常:
- Raspberry Pi 3
- Dell Poweredge T110
- IIS 在 Windows 10
- Synology DS1815 +
另一方面,在以下服务器上,(用于使用..)超过 65535 个字符的加密不起作用,服务器似乎崩溃了。
- Synology RS212
- Synology DS112 +
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
干杯。
这是我的问题,
我想加密 JSON 个在某些情况下可能很长的文件。 (有时包含 Base64 格式的图像)。
在以下测试服务器上,一切正常:
- Raspberry Pi 3
- Dell Poweredge T110
- IIS 在 Windows 10
- Synology DS1815 +
另一方面,在以下服务器上,(用于使用..)超过 65535 个字符的加密不起作用,服务器似乎崩溃了。
- Synology RS212
- Synology DS112 +
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
干杯。