我正在用php开发一个项目,需要对用户上传的文件进行加密。该文件的大小可能在 1mb 到 200mb 之间。在网上搜索后,我得出的结论是,最好的方法是将文件分成多个块,例如 4096 字节。因此,我加密每个块并将其附加到完整的加密文件中。我实际上在 CBC 模式下使用 mcrypt 和 AES-256 加密。
所以,我的问题是: 1) 我必须为每个块创建一个新的初始向量,或者我可以获取前一个块的最后一个块的最后 16 个字节作为当前块的第一个块的初始向量吗?这将导致在加密文件的开头仅附加一个 iv,而不是在加密块之前为每个块附加一个 iv。
2)为了添加HMAC认证。这个问题与上一个问题相关。我应该为整个文件添加它还是为每个块单独添加它。在这种情况下,对整个文件执行此操作是一个问题,因为它通常添加在文件的开头,并且在加密文件完成之前我无法计算 hmac。
3)与此相关。对于文件下载,同时解密(分块)并将文件发送给用户是一个好主意还是先解密然后发送更好?
谢谢
您应该加密文件流并让 PHP 处理一切。特别是加密过滤器与stream_filter_append相结合来完成您想要的操作。然后,您只需读取纯文本文件的块并将它们写入输出文件流。过滤器会导致加密发生。
这样您就不会重新发明轮子,并且使用可能已经过安全问题审核的代码。
对于 hmac,大多数库都允许您不断向 hmac 添加数据,直到调用 Finalize 或类似的操作。然后您将读取大块的密文,并将它们添加到 hmac 中。重复此操作,直到整个 ciphertext 已添加到 hmac 中并最终确定。
或者,在服务器上安装 openssl 并从 PHP 中调用 openssl 函数。您可以使用经过身份验证的密码模式等。
我遇到了几乎相同的问题。这是我找到的解决方案。
<?php
$filecrypt = new filecrypt();
class filecrypt{
var $_CHUNK_SIZE;
function __construct(){
$this->_CHUNK_SIZE = 100*1024; // 100Kb
}
public function encrypt($string, $key){
$key = pack('H*', $key);
if (extension_loaded('mcrypt') === true) return mcrypt_encrypt(MCRYPT_BLOWFISH, substr($key, 0, mcrypt_get_key_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB)), $string, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB), MCRYPT_RAND));
return false;
}
public function decrypt($string, $key){
$key = pack('H*', $key);
if (extension_loaded('mcrypt') === true) return mcrypt_decrypt(MCRYPT_BLOWFISH, substr($key, 0, mcrypt_get_key_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB)), $string, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB), MCRYPT_RAND));
return false;
}
public function encryptFileChunks($source, $destination, $key){
return $this->cryptFileChunks($source, $destination, $key, 'encrypt');
}
public function decryptFileChunks($source, $destination, $key){
return $this->cryptFileChunks($source, $destination, $key, 'decrypt');
}
private function cryptFileChunks($source, $destination, $key, $op){
if($op != "encrypt" and $op != "decrypt") return false;
$buffer = '';
$inHandle = fopen($source, 'rb');
$outHandle = fopen($destination, 'wb+');
if ($inHandle === false) return false;
if ($outHandle === false) return false;
while(!feof($inHandle)){
$buffer = fread($inHandle, $this->_CHUNK_SIZE);
if($op == "encrypt") $buffer = $this->encrypt($buffer, $key);
elseif($op == "decrypt") $buffer = $this->decrypt($buffer, $key);
fwrite($outHandle, $buffer);
}
fclose($inHandle);
fclose($outHandle);
return true;
}
public function printFileChunks($source, $key){
$buffer = '';
$inHandle = fopen($source, 'rb');
if ($inHandle === false) return false;
while(!feof($inHandle)){
$buffer = fread($inHandle, $this->_CHUNK_SIZE);
$buffer = $this->decrypt($buffer, $key);
echo $buffer;
}
return fclose($inHandle);
}
}
?>
用途:
<?php
$key = '3da541559918a808c2402bba5012f6c60b27661c'; // Your encryption key
$filecrypt->encryptFileChunks('I-still-loooove-hula-hoop.gif', 'encrypted.gif', $key);
$filecrypt->decryptFileChunks('encrypted.gif', 'decrypted.gif', $key);
?>
我创建了一个可以通过composer使用的简单的Github项目https://github.com/bigb06/PHPCypherFile
PHPCypherFile 为加密大文件提供了强大的解决方案 安全地无需显着的内存开销。它结合了以下力量 用于公钥/私钥加密的 RSA 和用于对称的 AES-256-CBC 加密,保证性能和安全性。