PHP从Javascript加密流文件
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了PHP从Javascript加密流文件,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8666字,纯文字阅读大概需要13分钟。
内容图文
我正在为大文件开发文件上传器.从HTML脚本上载,然后使用ArrayBuffer和Unit8Array从Java字节发送到PHP. PHP脚本将流式传输文件并将其保存到文件夹中.
这是我的Javascript看起来像
function upload(fileInputId, fileIndex)
{
var file = document.getElementById(fileInputId).files[fileIndex];
var blob;
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onloadend = function(evt)
{
xhr = new XMLHttpRequest();
xhr.open("POST", 'upload.php?name=' + file.name, true);
XMLHttpRequest.prototype.mySendAsBinary = function(text){
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++){
ui8a[i] = (text.charCodeAt(i) & 0xff);
}
if(typeof window.Blob == "function")
{
blob = new Blob([data]);
}else{
var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
bb.append(data);
blob = bb.getBlob();
}
this.send(blob);
}
var eventSource = xhr.upload || xhr;
eventSource.addEventListener("progress", function(e) {
var position = e.position || e.loaded;
var total = e.totalSize || e.total;
var percentage = Math.round((position/total)*100);
});
xhr.onreadystatechange = function()
{
if(xhr.readyState == 4)
{
if(xhr.status == 200)
{
console.log("Done");
}else{
console.log("Fail");
}
}
};
xhr.mySendAsBinary(evt.target.result);
};
}
这是我的upload.php
$inputHandler = fopen('php://input', "r");
$loc = "uploads/" . $_GET["name"];
$fileHandler = fopen($loc, "w+");
while(true) {
$buffer = fgets($inputHandler, 4096);
if (strlen($buffer) == 0) {
fclose($inputHandler);
fclose($fileHandler);
return true;
}
fwrite($fileHandler, $buffer);
}
我的问题是,当文件处于流传输模式时,如何使用AES或mcrypt加密那些上传文件?
解决方法:
是这样的.这是从内存中进行的,未经测试,因为我的笔记本电脑上没有PHPSecLib库,而且我懒于设置所有内容…
require __DIR__ . '/vendor/autoload.php';
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Random;
AESStreamEncode($input, $output, $key)
{
$cipher = new AES(AES::MODE_CBC);
$cipher->setKey($key);
$iv = Random::string($cipher->getBlockLength() >> 3);
$cipher->setIV($iv);
$base64_iv = rtrim(base64_encode($iv), '='); //22 chars
fwrite($output, $base64_iv); //store the IV this is like a salt
while(!feof($input)) {
$contents = fread($input, 1000000); //number of bytes to encrypt
$encrypted = $cipher->encrypt($contents);
//trim the = or ==, and replace with :, write to output stream.
fwrite($output, rtrim(base64_encode($encrypted), '=').':');
}
}
AESStreamDecode($input, $output, $key)
{
$cipher = new AES(AES::MODE_CBC);
$cipher->setKey($key);
$buffer = '';
$iv = false;
while(!feof($input)) {
$char = fgetc($input); //get a single char
if($char ==':'){
if(!$iv){
$iv = base64_decode(substr($buffer, 0, 22).'='); //iv is the first 22 of the first chunk.
$cipher->setIV($iv);
$buffer = substr($buffer, 22); //remove the iv
}
$buffer = base64_decode($buffer.'='); //decode base64 to bin
$decrypted = $cipher->decrypt($buffer);
fwrite($output, $decrypted);
$buffer = ''; //clear buffer.
}else{
$buffer .= $char;
}
}
}
其中$input和$output是有效的资源流句柄,例如来自fopen等.
$input = fopen($filepath, 'r');
$output = fopen($ohter_filepath, 'w');
AESStreamEncode($input, $output, $key);
如果下载解密的文件,则可以使用php:// output之类的内容作为流.
您必须删除=,因为它有时会丢失或其中2个,因此我们不能依靠它们作为分隔符.我通常只放1,它总是正确解码.我认为这只是一些填充.
参考
加密文件应如下所示:
xUg8L3AatsbvsGUaHLg6uYUDIpqv0xnZsimumv7j:zBzWUn3xqBt+k1XP0KmWoU8lyfFh1ege:nJzxnYF51VeMRZEeQDRl8:
但是有更长的块. IV就像是盐,将它添加到加密字符串的前面或后面是一种很常见的做法.所以举个例子
[xUg8L3AatsbvsGU]aHLg6uYUDIpqv0xnZsimumv7j:
[]中的部分是IV(我在base64_encode之后长22个字符),我数了数次,并且总是这么长.我们只需要记录IV并将其设置一次即可.我想您可以对每个块执行不同的IV,但是无论如何.
如果您确实使用PHPSecLib,则其中还包含一些不错的sFTP内容.只要确保获得2.0版本即可.基本上,它针对不同的加密算法具有一些后备功能和本机PHP实现.因此,就像它尝试使用open_ssl一样,如果您丢失了它,它将使用其本机实现.我将其用于sFTP,因此已经可用. sFTP需要扩展名ssh2_sftp,如果我还记得它在我们进行安装时仅在Linux上可用.
UPDATE
对于下载,您可以发出标题,然后将输出流提供给解码功能,如下所示
$input = fopen('encrypted_file.txt', 'r');
$output = fopen('php://output', 'w');
header('Content-Type: "text/plain"');
header('Content-Disposition: attachment; filename="decoded.txt"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0, max-age=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
//header('Content-Length: '.$fileSize); //unknown
AESStreamDecode($input, $output, $key);
这些是非常标准的标头.唯一真正的麻烦是,因为加密时文件大小不同,所以您不能只是简单地获取文件大小并使用它,因为它会更大一些.不通过文件大小不会阻止下载,只是没有估计的时间等.
但是因为在加密之前我们知道大小,所以我们可以将其嵌入文件数据本身中,如下所示:
3555543|xUg8L3AatsbvsGUaHLg6uYUDIpqv0xnZsimumv7j:zBzWUn3xqBt+k1XP0KmWoU8lyfFh1ege:nJzxnYF51VeMRZEeQDRl8:
然后在下载时将其拔出,但是您必须将其用作单独的函数来获取它,并且不弄乱解码文件可能有些棘手.
老实说,我认为这比值钱更麻烦.
UPDATE2
无论如何,我对嵌入文件大小的这些更改进行了处理,这是一个选择,但是如果不仔细做的话,它也可能使文件解密变得混乱. (我还没有测试过)
AESStreamEncode($input, $output, $key, $filesize = false)
{
$cipher = new AES(AES::MODE_CBC);
$cipher->setKey($key);
$iv = Random::string($cipher->getBlockLength() >> 3);
$cipher->setIV($iv);
$base64_iv = rtrim(base64_encode($iv), '='); //22 chars
//Option1 - optional filesize
if(false !== $filesize){
//add filesize if given in the arguments
fwrite($output, $filesize.'|');
}
/*
//Option2: using fstat, remove '$filesize = false' from the arguments
$stat = fstat($input);
fwrite($output, $stat['size'].'|');
*/
fwrite($output, $base64_iv); //store the IV this is like a salt
while(!feof($input)) {
$contents = fread($input, 1000000); //number of bytes to encrypt
$encrypted = $cipher->encrypt($contents);
//trim the = or ==, and replace with :, write to output stream.
fwrite($output, rtrim(base64_encode($encrypted), '=').':');
}
}
因此,现在我们应该具有3045345 | asdaeASE:AEREA等文件大小.然后我们可以在解密时将其拉回.
AESStreamDecode($input, $output, $key)
{
$cipher = new AES(AES::MODE_CBC);
$cipher->setKey($key);
$buffer = '';
$iv = false;
$filesize = null;
while(!feof($input)) {
$char = fgetc($input); //get a single char
if($char =='|'){
/*
get the filesize from the file,
this is a fallback method, so it wont affect the file if
we don't pull it out with the other function (see below)
*/
$filesize = $buffer;
$buffer = '';
}elseif($char ==':'){
if(!$iv){
$iv = base64_decode(substr($buffer, 0, 22).'='); //iv is the first 22 of the first chunk.
$cipher->setIV($iv);
$buffer = substr($buffer, 22); //remove the iv
}
$buffer = base64_decode($buffer.'='); //decode base64 to bin
$decrypted = $cipher->decrypt($buffer);
fwrite($output, $decrypted);
$buffer = ''; //clear buffer.
}else{
$buffer .= $char;
}
}
//when we do a download we don't want to wait for this
return $filesize;
}
解码获取文件大小部分用作备用,或者,如果不需要它,则不必担心在解码文件时将文件弄乱.下载时,我们可以使用以下功能,这样我们就不必等待文件完全读取即可获得大小(这与我们上面所做的基本相同).
//We have to use a separate function because
//we can't wait tell reading is complete to
//return the filesize, it defeats the purpose
AESStreamGetSize($input){
$buffer = '';
//PHP_INT_MAX (maximum allowed integer) is 19 chars long
//so by putting a limit of 20 in we can short cut reading
//if we can't find the filesize
$limit = 20;
$i; //simple counter.
while(!feof($input)) {
$char = fgetc($input); //get a single char
if($char =='|'){
return $buffer;
}elseif($i >= $limit){
break;
}
$buffer .= $char;
++$i; //increment how many chars we have read
}
return false;
}
然后,在下载时,您只需要进行一些更改.
$input = fopen('encrypted_file.txt', 'r');
//output streams dumps it directly to output, lets us handle larger files
$output = fopen('php://output', 'w');
//other headers go here
if(false !== ($filesize = AESStreamGetSize($input))){
header('Content-Length: '.$fileSize); //unknown
//because it's a file pointer we can take advantage of that
//and the decode function will start where the getSize left off.
// or you could rewind it because of the fallback we have.
AESStreamDecode($input, $output, $key);
}else{
//if we can't find the filesize, then we can fallback to download without it
//in this case we need to rewind the file
rewind($input);
AESStreamDecode($input, $output, $key);
}
如果您想缩短此时间,您也可以用这种方法来完成,它最多只能包含19个字符,因此这对性能没有太大影响.
if(false !== ($filesize = AESStreamGetSize($input))) header('Content-Length: '.$fileSize);
rewind($input);
AESStreamDecode($input, $output, $key);
基本上在上面,我们只执行(或不执行)filesize标头,然后倒回并进行下载.它将重新读取文件大小,但这很简单.
对于参考fstat(),希望这是有道理的.
内容总结
以上是互联网集市为您收集整理的PHP从Javascript加密流文件全部内容,希望文章能够帮你解决PHP从Javascript加密流文件所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。