Browse Source

Started making changes to how the file upload encrypt/decrypt works to better optimize memory usage.

tags/2.0.6
Teknikode 3 years ago
parent
commit
8d561dd5b8

+ 11
- 38
Teknik/Areas/API/Controllers/APIv1Controller.cs View File

@@ -44,31 +44,25 @@ namespace Teknik.Areas.API.Controllers
if (model.file.ContentLength <= Config.UploadConfig.MaxUploadSize)
{
// convert file to bytes
byte[] fileData = null;
string fileExt = Path.GetExtension(model.file.FileName);
int contentLength = model.file.ContentLength;
using (var binaryReader = new BinaryReader(model.file.InputStream))
{
fileData = binaryReader.ReadBytes(model.file.ContentLength);
}

// Scan the file to detect a virus
if (Config.UploadConfig.VirusScanEnable)
{
byte[] scanData = fileData;
// If it was encrypted client side, decrypt it
if (!model.encrypt && model.key != null)
{
// If the IV is set, and Key is set, then decrypt it
if (!string.IsNullOrEmpty(model.key) && !string.IsNullOrEmpty(model.iv))
{
// Decrypt the data
scanData = AES.Decrypt(scanData, model.key, model.iv);
}
}
//if (!model.encrypt && model.key != null)
//{
// // If the IV is set, and Key is set, then decrypt it
// if (!string.IsNullOrEmpty(model.key) && !string.IsNullOrEmpty(model.iv))
// {
// // Decrypt the data
// scanData = AES.Decrypt(scanData, model.key, model.iv);
// }
//}
ClamClient clam = new ClamClient(Config.UploadConfig.ClamServer, Config.UploadConfig.ClamPort);
clam.MaxStreamSize = Config.UploadConfig.MaxUploadSize;
ClamScanResult scanResult = clam.SendAndScanFile(scanData);
ClamScanResult scanResult = clam.SendAndScanFile(model.file.InputStream);

switch (scanResult.Result)
{
@@ -95,29 +89,8 @@ namespace Teknik.Areas.API.Controllers
if (model.blockSize <= 0)
model.blockSize = Config.UploadConfig.BlockSize;

byte[] data = null;
// If they want us to encrypt the file first, do that here
if (model.encrypt)
{
// Generate key and iv if empty
if (string.IsNullOrEmpty(model.key))
{
model.key = StringHelper.RandomString(model.keySize / 8);
}
if (string.IsNullOrEmpty(model.iv))
{
model.iv = StringHelper.RandomString(model.blockSize / 8);
}

data = AES.Encrypt(fileData, model.key, model.iv);
if (data == null || data.Length <= 0)
{
return Json(new { error = new { message = "Unable to encrypt file" } });
}
}

// Save the file data
Upload.Models.Upload upload = Uploader.SaveFile(db, Config, (model.encrypt) ? data : fileData, model.contentType, contentLength, fileExt, model.iv, (model.saveKey) ? model.key : null, model.keySize, model.blockSize);
Upload.Models.Upload upload = Uploader.SaveFile(db, Config, model.file.InputStream, model.contentType, contentLength, model.encrypt, fileExt, model.iv, model.key, model.saveKey, model.keySize, model.blockSize);

if (upload != null)
{

+ 12
- 33
Teknik/Areas/Upload/Controllers/UploadController.cs View File

@@ -60,30 +60,24 @@ namespace Teknik.Areas.Upload.Controllers
if (data.ContentLength <= Config.UploadConfig.MaxUploadSize)
{
// convert file to bytes
byte[] fileData = null;
int contentLength = data.ContentLength;
using (var binaryReader = new BinaryReader(data.InputStream))
{
fileData = binaryReader.ReadBytes(data.ContentLength);
}

// Scan the file to detect a virus
if (Config.UploadConfig.VirusScanEnable)
{
byte[] scanData = fileData;
// If it was encrypted client side, decrypt it
if (!encrypt && key != null)
{
// If the IV is set, and Key is set, then decrypt it
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv))
{
// Decrypt the data
scanData = AES.Decrypt(scanData, key, iv);
}
}
//if (!encrypt && key != null)
//{
// // If the IV is set, and Key is set, then decrypt it
// if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv))
// {
// // Decrypt the data
// scanData = AES.Decrypt(scanData, key, iv);
// }
//}
ClamClient clam = new ClamClient(Config.UploadConfig.ClamServer, Config.UploadConfig.ClamPort);
clam.MaxStreamSize = Config.UploadConfig.MaxUploadSize;
ClamScanResult scanResult = clam.SendAndScanFile(scanData);
ClamScanResult scanResult = clam.SendAndScanFile(data.InputStream);

switch (scanResult.Result)
{
@@ -97,23 +91,8 @@ namespace Teknik.Areas.Upload.Controllers
return Json(new { error = new { message = string.Format("Unknown result while scanning the file upload for viruses. {0}", scanResult.RawResult) } });
}
}

// if they want us to encrypt it, we do so here
if (encrypt)
{
// Generate key and iv if empty
if (string.IsNullOrEmpty(key))
{
key = StringHelper.RandomString(keySize / 8);
}

fileData = AES.Encrypt(fileData, key, iv);
if (fileData == null || fileData.Length <= 0)
{
return Json(new { error = new { message = "Unable to encrypt file" } });
}
}
Models.Upload upload = Uploader.SaveFile(db, Config, fileData, fileType, contentLength, fileExt, iv, (saveKey) ? key : null, keySize, blockSize);
Models.Upload upload = Uploader.SaveFile(db, Config, data.InputStream, fileType, contentLength, encrypt, fileExt, iv, key, saveKey, keySize, blockSize);
if (upload != null)
{
if (User.Identity.IsAuthenticated)

+ 38
- 11
Teknik/Areas/Upload/Uploader.cs View File

@@ -6,31 +6,32 @@ using System.IO;
using Teknik.Configuration;
using Teknik.Models;
using Teknik.Utilities;
using System.Text;

namespace Teknik.Areas.Upload
{
public static class Uploader
{
public static Models.Upload SaveFile(TeknikEntities db, Config config, byte[] file, string contentType, int contentLength)
public static Models.Upload SaveFile(TeknikEntities db, Config config, System.IO.Stream file, string contentType, int contentLength, bool encrypt)
{
return SaveFile(db, config, file, contentType, contentLength, string.Empty, null, null, 256, 128);
return SaveFile(db, config, file, contentType, contentLength, encrypt, string.Empty, null, null, false, 256, 128);
}
public static Models.Upload SaveFile(TeknikEntities db, Config config, byte[] file, string contentType, int contentLength, string defaultExtension)
public static Models.Upload SaveFile(TeknikEntities db, Config config, System.IO.Stream file, string contentType, int contentLength, bool encrypt, string defaultExtension)
{
return SaveFile(db, config, file, contentType, contentLength, defaultExtension, null, null, 256, 128);
return SaveFile(db, config, file, contentType, contentLength, encrypt, defaultExtension, null, null, false, 256, 128);
}

public static Models.Upload SaveFile(TeknikEntities db, Config config, byte[] file, string contentType, int contentLength, string defaultExtension, string iv)
public static Models.Upload SaveFile(TeknikEntities db, Config config, System.IO.Stream file, string contentType, int contentLength, bool encrypt, string defaultExtension, string iv)
{
return SaveFile(db, config, file, contentType, contentLength, defaultExtension, iv, null, 256, 128);
return SaveFile(db, config, file, contentType, contentLength, encrypt, defaultExtension, iv, null, false, 256, 128);
}

public static Models.Upload SaveFile(TeknikEntities db, Config config, byte[] file, string contentType, int contentLength, string defaultExtension, string iv, string key)
public static Models.Upload SaveFile(TeknikEntities db, Config config, System.IO.Stream file, string contentType, int contentLength, bool encrypt, string defaultExtension, string iv, string key, bool saveKey)
{
return SaveFile(db, config, file, contentType, contentLength, defaultExtension, iv, key, 256, 128);
return SaveFile(db, config, file, contentType, contentLength, encrypt, defaultExtension, iv, key, saveKey, 256, 128);
}

public static Models.Upload SaveFile(TeknikEntities db, Config config, byte[] file, string contentType, int contentLength, string defaultExtension, string iv, string key, int keySize, int blockSize)
public static Models.Upload SaveFile(TeknikEntities db, Config config, System.IO.Stream file, string contentType, int contentLength, bool encrypt, string defaultExtension, string iv, string key, bool saveKey, int keySize, int blockSize)
{
if (!Directory.Exists(config.UploadConfig.UploadDirectory))
{
@@ -42,7 +43,33 @@ namespace Teknik.Areas.Upload
string fileName = Path.GetFileName(filePath);

// once we have the filename, lets save the file
File.WriteAllBytes(filePath, file);
if (encrypt)
{
// Generate key and iv if empty
if (string.IsNullOrEmpty(key))
{
key = StringHelper.RandomString(keySize / 8);
}
if (string.IsNullOrEmpty(iv))
{
iv = StringHelper.RandomString(blockSize / 8);
}

byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);

// Encrypt the file to disk
AES.EncryptToFile(filePath, file, config.UploadConfig.ChunkSize, keyBytes, ivBytes, "CTR", "NoPadding");
}
else
{
// Just write the stream to the file
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
file.Seek(0, SeekOrigin.Begin);
file.CopyTo(fileStream);
}
}

// Generate a unique url
string extension = (config.UploadConfig.IncludeExtension) ? FileHelper.GetDefaultExtension(contentType, defaultExtension) : string.Empty;
@@ -59,7 +86,7 @@ namespace Teknik.Areas.Upload
upload.FileName = fileName;
upload.ContentType = (!string.IsNullOrEmpty(contentType)) ? contentType : "application/octet-stream";
upload.ContentLength = contentLength;
upload.Key = key;
upload.Key = (saveKey) ? key : null;
upload.IV = iv;
upload.KeySize = keySize;
upload.BlockSize = blockSize;

+ 87
- 17
Utilities/Utilities/Crypto.cs View File

@@ -155,19 +155,12 @@ namespace Teknik.Utilities
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Decrypt(data, keyBytes, ivBytes, "CTR", "NoPadding");
}
public static byte[] DecryptCBC(byte[] data, string key, string iv)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Decrypt(data, keyBytes, ivBytes, "CBC", "PKCS5PADDING");
}
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding)
{
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/" + mode + "/" + padding);

cipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), iv));

return cipher.DoFinal(data);
using (MemoryStream stream = new MemoryStream(data))
{
return ProcessCipher(false, stream, 1024, key, iv, mode, padding);
}
}


@@ -181,19 +174,96 @@ namespace Teknik.Utilities
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Encrypt(data, keyBytes, ivBytes, "CTR", "NoPadding");
}
public static byte[] EncryptCBC(byte[] data, string key, string iv)
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Encrypt(data, keyBytes, ivBytes, "CBC", "PKCS5PADDING");
using (MemoryStream stream = new MemoryStream(data))
{
return ProcessCipher(true, stream, 1024, key, iv, mode, padding);
}
}
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding)

public static byte[] ProcessCipher(bool encrypt, Stream input, int blockSize, byte[] key, byte[] iv, string mode, string padding)
{
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/" + mode + "/" + padding);

cipher.Init(encrypt, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), iv));

// Make sure the input stream is at the beginning
input.Seek(0, SeekOrigin.Begin);

// Initialize variables
byte[] output = new byte[input.Length];
int cipherOffset = 0;
int processedBytes = 0;

// Process the stream and save the bytes to the output
do
{
processedBytes = ProcessCipherBlock(cipher, input, blockSize, output, cipherOffset);
cipherOffset += processedBytes;
}
while (processedBytes > 0);

// Finalize processing of the cipher
cipher.DoFinal(output, cipherOffset);

return output;
}

public static void EncryptToFile(string filePath, Stream input, int blockSize, byte[] key, byte[] iv, string mode, string padding)
{
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/" + mode + "/" + padding);

cipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), iv));

return cipher.DoFinal(data);
// Make sure the input stream is at the beginning
input.Seek(0, SeekOrigin.Begin);

using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
int processedBytes = 0;
byte[] buffer = new byte[blockSize];
do
{
processedBytes = ProcessCipherBlock(cipher, input, blockSize, buffer, 0);
if (processedBytes > 0)
{
// We have bytes, lets write them to the file
fileStream.Write(buffer, 0, processedBytes);

// Clear the buffer
Array.Clear(buffer, 0, blockSize);
}
}
while (processedBytes > 0);

// Clear the buffer
Array.Clear(buffer, 0, blockSize);

// Do the final output
processedBytes = cipher.DoFinal(buffer, 0);
if (processedBytes > 0)
{
// We have bytes, lets write them to the file
fileStream.Write(buffer, 0, processedBytes);
}
}
}

public static int ProcessCipherBlock(IBufferedCipher cipher, Stream input, int blockSize, byte[] output, int outputOffset)
{
// Initialize buffer
byte[] buffer = new byte[blockSize];

// Read the next block of data
int bytesRead = input.Read(buffer, 0, blockSize);
if (bytesRead > 0)
{
// process the cipher for the read block and add it to the output
return cipher.ProcessBytes(buffer, 0, bytesRead, output, outputOffset);
}

return 0;
}

public static byte[] CreateKey(string password, string iv, int keySize = 256)

Loading…
Cancel
Save