Browse Source

Implemented key storage, deletion key generation, file deletion, and server side decryption.

tags/2.0.3
Teknikode 4 years ago
parent
commit
72db3a891a

+ 79
- 11
Teknik/Areas/Upload/Controllers/UploadController.cs View File

@@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Teknik.Areas.Error.ViewModels;
using Teknik.Areas.Upload.Models;
using Teknik.Areas.Upload.ViewModels;
using Teknik.Controllers;
using Teknik.Helpers;
using Teknik.Models;

namespace Teknik.Areas.Upload.Controllers
@@ -20,6 +23,7 @@ namespace Teknik.Areas.Upload.Controllers
[AllowAnonymous]
public ActionResult Index()
{
ViewBag.Title = "Teknik Upload - End to End Encryption";
return View(new UploadViewModel());
}

@@ -48,6 +52,7 @@ namespace Teknik.Areas.Upload.Controllers
[AllowAnonymous]
public ActionResult Download(string file)
{
ViewBag.Title = "Teknik Download - " + file;
Models.Upload upload = db.Uploads.Where(up => up.Url == file).FirstOrDefault();
if (upload != null)
{
@@ -63,16 +68,29 @@ namespace Teknik.Areas.Upload.Controllers

return View(model);
}
else
else // We have the key, so that means server side decryption
{
// decrypt it server side! Weee
return View();
if (System.IO.File.Exists(upload.FileName))
{
// Read in the file
byte[] encData = System.IO.File.ReadAllBytes(upload.FileName);
// Decrypt the data
byte[] data = AES.Decrypt(encData, upload.Key, upload.IV, Config.UploadConfig.KeySize, Config.UploadConfig.IVSize);

// Create File
var cd = new System.Net.Mime.ContentDisposition
{
FileName = upload.Url,
Inline = true
};

Response.AppendHeader("Content-Disposition", cd.ToString());

return File(data, upload.ContentType);
}
}
}
else
{
return RedirectToRoute("Error.Http404");
}
return RedirectToRoute("*.Error.Http404");
}

[HttpPost]
@@ -110,20 +128,70 @@ namespace Teknik.Areas.Upload.Controllers
return null;
}

[HttpGet]
[AllowAnonymous]
public ActionResult Delete(string file, string key)
{
ViewBag.Title = "File Delete - " + file + " - " + Config.Title;
Models.Upload upload = db.Uploads.Where(up => up.Url == file).FirstOrDefault();
if (upload != null)
{
DeleteViewModel model = new DeleteViewModel();

if (!string.IsNullOrEmpty(upload.DeleteKey) && upload.DeleteKey == key)
{
string filePath = upload.FileName;
// Delete from the DB
db.Uploads.Remove(upload);
db.SaveChanges();

// Delete the File
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
model.Deleted = true;
}
else
{
model.Deleted = false;
}
return View(model);
}
return RedirectToRoute("*.Error.Http404");
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Delete(string url, string deleteKey)
public ActionResult GenerateDeleteKey(string file)
{
return Json(new { result = true });
Models.Upload upload = db.Uploads.Where(up => up.Url == file).FirstOrDefault();
if (upload != null)
{
string delKey = Utility.RandomString(Config.UploadConfig.DeleteKeyLength);
upload.DeleteKey = delKey;
db.Entry(upload).State = EntityState.Modified;
db.SaveChanges();
return Json(new { result = Url.SubRouteUrl("upload", "Upload.Delete", new { file = file, key = delKey }) });
}
return Json(new { error = "Invalid URL" });
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult GenerateDeleteKey(string uploadID)
public ActionResult SaveFileKey(string file, string key)
{
return Json(new { result = "temp-delete-key" });
Models.Upload upload = db.Uploads.Where(up => up.Url == file).FirstOrDefault();
if (upload != null)
{
upload.Key = key;
db.Entry(upload).State = EntityState.Modified;
db.SaveChanges();
return Json(new { result = Url.SubRouteUrl("upload", "Upload.Download", new { file = file }) });
}
return Json(new { error = "Invalid URL" });
}
}
}

+ 5
- 4
Teknik/Areas/Upload/Scripts/Upload.js View File

@@ -8,9 +8,10 @@ function linkSaveKey(selector, uploadID, key, fileID) {
$.ajax({
type: "POST",
url: saveKeyToServerURL,
data: AddAntiForgeryToken({ uploadID: uploadID, key: key }),
data: AddAntiForgeryToken({ file: uploadID, key: key }),
success: function (html) {
if (html.result) {
$('#upload-link-' + fileID).html('<p><a href="' + html.result + '" target="_blank" class="alert-link">' + html.result + '</a></p>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
@@ -27,7 +28,7 @@ function linkUploadDelete(selector, uploadID) {
$.ajax({
type: "POST",
url: generateDeleteKeyURL,
data: AddAntiForgeryToken({ uploadID: uploadID }),
data: AddAntiForgeryToken({ file: uploadID }),
success: function (html) {
if (html.result) {
bootbox.dialog({
@@ -132,8 +133,8 @@ function encryptFile(file, callback) {
reader.onload = (function (callback) {
return function (e) {
// Create random key and iv
var keyStr = randomString(24, '#aA');
var ivStr = randomString(24, '#aA');
var keyStr = randomString(keySize, '#aA');
var ivStr = randomString(ivSize, '#aA');

var worker = new Worker(encScriptSrc);


+ 10
- 10
Teknik/Areas/Upload/UploadAreaRegistration.cs View File

@@ -26,14 +26,14 @@ namespace Teknik.Areas.Upload
"Upload.Download",
"dev",
"Upload/{file}",
new { controller = "Upload", action = "Download", url = string.Empty },
new { controller = "Upload", action = "Download", file = string.Empty },
new[] { typeof(Controllers.UploadController).Namespace }
);
context.MapSubdomainRoute(
"Upload.Delete",
"dev",
"Upload/{file}/{key}",
new { controller = "Upload", action = "Delete", url = string.Empty, deleteKey = string.Empty },
new { controller = "Upload", action = "Delete", file = string.Empty, key = string.Empty },
new[] { typeof(Controllers.UploadController).Namespace }
);
context.MapSubdomainRoute(
@@ -53,15 +53,15 @@ namespace Teknik.Areas.Upload
context.MapSubdomainRoute(
"Upload.Download",
"u",
"{url}",
new { controller = "Upload", action = "Download", url = string.Empty },
"{file}",
new { controller = "Upload", action = "Download", file = string.Empty },
new[] { typeof(Controllers.UploadController).Namespace }
);
context.MapSubdomainRoute(
"Upload.Delete",
"u",
"{url}/{deleteKey}",
new { controller = "Upload", action = "Delete", url = string.Empty, deleteKey = string.Empty },
"{file}/{key}",
new { controller = "Upload", action = "Delete", file = string.Empty, key = string.Empty },
new[] { typeof(Controllers.UploadController).Namespace }
);
context.MapSubdomainRoute(
@@ -81,15 +81,15 @@ namespace Teknik.Areas.Upload
context.MapSubdomainRoute(
"Upload.Download",
"upload",
"{url}",
new { controller = "Upload", action = "Download", url = string.Empty },
"{file}",
new { controller = "Upload", action = "Download", file = string.Empty },
new[] { typeof(Controllers.UploadController).Namespace }
);
context.MapSubdomainRoute(
"Upload.Delete",
"upload",
"{url}/{deleteKey}",
new { controller = "Upload", action = "Delete", url = string.Empty, deleteKey = string.Empty },
"{file}/{key}",
new { controller = "Upload", action = "Delete", file = string.Empty, key = string.Empty },
new[] { typeof(Controllers.UploadController).Namespace }
);
context.MapSubdomainRoute(

+ 14
- 0
Teknik/Areas/Upload/ViewModels/DeleteViewModel.cs View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Teknik.ViewModels;

namespace Teknik.Areas.Upload.ViewModels
{
public class DeleteViewModel : ViewModelBase
{
public string File { get; set; }
public bool Deleted { get; set; }
}
}

+ 16
- 0
Teknik/Areas/Upload/Views/Upload/Delete.cshtml View File

@@ -0,0 +1,16 @@
@model Teknik.Areas.Upload.ViewModels.DeleteViewModel

<div class="container">
<div class="row">
<div class="col-sm-12 text-center">
@if (Model.Deleted)
{
<h1>@Model.File has been Deleted</h1>
}
else
{
<h1>@Model.File was unable to be Deleted</h1>
}
</div>
</div>
</div>

+ 2
- 0
Teknik/Areas/Upload/Views/Upload/Index.cshtml View File

@@ -8,6 +8,8 @@
var uploadFileURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action = "Upload" })';
var maxUploadSize = @Model.Config.UploadConfig.MaxUploadSize;
var chunkSize = @Model.Config.UploadConfig.ChunkSize;
var keySize = @Model.Config.UploadConfig.KeySize;
var ivSize = @Model.Config.UploadConfig.IVSize;
</script>

@Styles.Render("~/Content/upload")

+ 6
- 0
Teknik/Configuration/UploadConfig.cs View File

@@ -14,6 +14,9 @@ namespace Teknik.Configuration
// File Extension for saved files
public string FileExtension { get; set; }
public int UrlLength { get; set; }
public int DeleteKeyLength { get; set; }
public int KeySize { get; set; }
public int IVSize { get; set; }
public bool IncludeExtension { get; set; }
// The size of the chunk that the file will be encrypted/decrypted in (bytes)
public int ChunkSize { get; set; }
@@ -29,6 +32,9 @@ namespace Teknik.Configuration
UploadDirectory = string.Empty;
FileExtension = "enc";
UrlLength = 6;
DeleteKeyLength = 24;
KeySize = 32;
IVSize = 16;
IncludeExtension = true;
ChunkSize = 1024;
}

+ 64
- 0
Teknik/Helpers/Crypto.cs View File

@@ -1,6 +1,17 @@
using System.Text;
using SecurityDriven.Inferno.Hash;
using SecurityDriven.Inferno.Mac;
using System.IO;
using System.Security.Cryptography;
using Teknik.Configuration;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;


namespace Teknik.Helpers
{
@@ -16,4 +27,57 @@ namespace Teknik.Helpers
return Encoding.ASCII.GetString(result);
}
}

public class AES
{
public static byte[] Decrypt(byte[] data, string key, string iv)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Decrypt(data, keyBytes, ivBytes);
}
public static byte[] Decrypt(byte[] data, string key, string iv, int keySize, int blockSize)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Decrypt(data, keyBytes, ivBytes, keySize, blockSize);
}
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
{
return Decrypt(data, key, iv, 256, 128);
}
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, int keySize, int blockSize)
{
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");

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

return cipher.DoFinal(data);
}

public static byte[] Encrypt(byte[] data, string key, string iv)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Encrypt(data, keyBytes, ivBytes);
}
public static byte[] Encrypt(byte[] data, string key, string iv, int keySize, int blockSize)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
return Encrypt(data, keyBytes, ivBytes, keySize, blockSize);
}
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
return Encrypt(data, key, iv, 256, 128);
}
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, int keySize, int blockSize)
{
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");

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

return cipher.DoFinal(data);
}
}
}

+ 1
- 2
Teknik/Modules/PerformanceMonitorModule.cs View File

@@ -28,8 +28,7 @@ namespace Teknik.Modules
timer.Stop();
// Don't interfere with non-HTML responses

if (requestContext.Response.ContentType == "text/html" ||
requestContext.Response.ContentType == "application/json")
if (requestContext.Response.ContentType == "text/html")
{

Uri requestUrl = requestContext.Request.Url;

+ 6
- 0
Teknik/Teknik.csproj View File

@@ -47,6 +47,10 @@
<HintPath>..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll</HintPath>
<Private>True</Private>
@@ -170,6 +174,7 @@
<Compile Include="Areas\Upload\Models\Upload.cs" />
<Compile Include="Areas\Upload\UploadAreaRegistration.cs" />
<Compile Include="Areas\Upload\Uploader.cs" />
<Compile Include="Areas\Upload\ViewModels\DeleteViewModel.cs" />
<Compile Include="Areas\Upload\ViewModels\DownloadViewModel.cs" />
<Compile Include="Areas\Upload\ViewModels\UploadViewModel.cs" />
<Compile Include="Configuration\BlogConfig.cs" />
@@ -285,6 +290,7 @@
<Content Include="Areas\Error\Views\Error\General.cshtml" />
<Content Include="Areas\Error\Views\Error\Http500.cshtml" />
<Content Include="Areas\Upload\Views\Upload\Download.cshtml" />
<Content Include="Areas\Upload\Views\Upload\Delete.cshtml" />
<None Include="Properties\PublishProfiles\Teknik Dev.pubxml" />
<None Include="Scripts\jquery-2.1.4.intellisense.js" />
<Content Include="Scripts\bootbox\bootbox.min.js" />

+ 1
- 0
Teknik/packages.config View File

@@ -2,6 +2,7 @@
<packages>
<package id="Antlr" version="3.5.0.2" targetFramework="net452" userInstalled="true" />
<package id="bootstrap" version="3.3.6" targetFramework="net452" userInstalled="true" />
<package id="BouncyCastle" version="1.7.0" targetFramework="net452" />
<package id="EntityFramework" version="6.1.3" targetFramework="net452" userInstalled="true" />
<package id="FontAwesome" version="4.4.0" targetFramework="net452" userInstalled="true" />
<package id="GitVersionTask" version="3.3.0" targetFramework="net452" developmentDependency="true" />

Loading…
Cancel
Save