@@ -168,7 +168,7 @@ namespace ServerMaint | |||
byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key); | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV); | |||
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); | |||
AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"); | |||
AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes); | |||
// We have the data, let's scan it | |||
ClamScanResult scanResult = clam.SendAndScanFile(aesStream); |
@@ -15,6 +15,7 @@ using Teknik.Filters; | |||
using Teknik.Utilities; | |||
using Teknik.Models; | |||
using Teknik.Attributes; | |||
using Teknik.Utilities.Cryptography; | |||
namespace Teknik.Areas.Paste.Controllers | |||
{ |
@@ -6,6 +6,7 @@ using System.Web; | |||
using Teknik.Configuration; | |||
using Teknik.Utilities; | |||
using Teknik.Models; | |||
using Teknik.Utilities.Cryptography; | |||
namespace Teknik.Areas.Paste | |||
{ |
@@ -294,7 +294,7 @@ namespace Teknik.Areas.Upload.Controllers | |||
return new FileGenerateResult(url, | |||
contentType, | |||
(response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"), (int)length, Config.UploadConfig.ChunkSize), | |||
(response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes), (int)length, Config.UploadConfig.ChunkSize), | |||
false); | |||
} | |||
else // Otherwise just send it |
@@ -8,6 +8,7 @@ using Teknik.Models; | |||
using Teknik.Utilities; | |||
using System.Text; | |||
using Org.BouncyCastle.Utilities.Encoders; | |||
using Teknik.Utilities.Cryptography; | |||
namespace Teknik.Areas.Upload | |||
{ | |||
@@ -54,7 +55,7 @@ namespace Teknik.Areas.Upload | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(iv); | |||
// Encrypt the file to disk | |||
AES.EncryptToFile(filePath, file, config.UploadConfig.ChunkSize, keyBytes, ivBytes, "CTR", "NoPadding"); | |||
AES.EncryptToFile(filePath, file, config.UploadConfig.ChunkSize, keyBytes, ivBytes); | |||
} | |||
else | |||
{ |
@@ -3,15 +3,8 @@ using SecurityDriven.Inferno.Hash; | |||
using SecurityDriven.Inferno.Mac; | |||
using System.IO; | |||
using System.Security.Cryptography; | |||
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; | |||
using Org.BouncyCastle.Bcpg.OpenPgp; | |||
using Org.BouncyCastle.Utilities.IO; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO.MemoryMappedFiles; | |||
@@ -154,155 +147,6 @@ namespace Teknik.Utilities | |||
} | |||
} | |||
public class AES | |||
{ | |||
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) | |||
{ | |||
return Decrypt(data, key, iv, "CTR", "NoPadding"); | |||
} | |||
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, "CTR", "NoPadding"); | |||
} | |||
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
return ProcessCipher(false, stream, 1024, key, iv, mode, padding); | |||
} | |||
} | |||
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) | |||
{ | |||
return Encrypt(data, key, iv, "CTR", "NoPadding"); | |||
} | |||
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, "CTR", "NoPadding"); | |||
} | |||
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
return ProcessCipher(true, stream, 1024, key, iv, mode, padding); | |||
} | |||
} | |||
public static byte[] ProcessCipher(bool encrypt, Stream input, int chunkSize, byte[] key, byte[] iv, string mode, string padding) | |||
{ | |||
// Create the cipher we are going to use | |||
IBufferedCipher cipher = CreateCipher(encrypt, key, iv, mode, padding); | |||
// 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 bytesRead = 0; | |||
// Process the stream and save the bytes to the output | |||
do | |||
{ | |||
int processedBytes = ProcessCipherBlock(cipher, input, 0, chunkSize, output, cipherOffset, out bytesRead); | |||
cipherOffset += processedBytes; | |||
} | |||
while (bytesRead > 0); | |||
// Finalize processing of the cipher | |||
FinalizeCipherBlock(cipher, output, cipherOffset); | |||
return output; | |||
} | |||
public static void EncryptToFile(string filePath, Stream input, int chunkSize, byte[] key, byte[] iv, string mode, string padding) | |||
{ | |||
IBufferedCipher cipher = CreateCipher(true, key, iv, mode, padding); | |||
// 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[chunkSize]; | |||
int bytesRead = 0; | |||
do | |||
{ | |||
processedBytes = ProcessCipherBlock(cipher, input, 0, chunkSize, buffer, 0, out bytesRead); | |||
if (processedBytes > 0) | |||
{ | |||
// We have bytes, lets write them to the file | |||
fileStream.Write(buffer, 0, processedBytes); | |||
// Clear the buffer | |||
Array.Clear(buffer, 0, chunkSize); | |||
} | |||
} | |||
while (processedBytes > 0); | |||
// Clear the buffer | |||
Array.Clear(buffer, 0, chunkSize); | |||
// Finalize processing of the cipher | |||
processedBytes = FinalizeCipherBlock(cipher, buffer, 0); | |||
if (processedBytes > 0) | |||
{ | |||
// We have bytes, lets write them to the file | |||
fileStream.Write(buffer, 0, processedBytes); | |||
} | |||
} | |||
} | |||
public static IBufferedCipher CreateCipher(bool encrypt, 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)); | |||
return cipher; | |||
} | |||
public static int ProcessCipherBlock(IBufferedCipher cipher, Stream input, int inputOffset, int chunkSize, byte[] output, int outputOffset, out int bytesRead) | |||
{ | |||
// Initialize buffer | |||
byte[] buffer = new byte[chunkSize + inputOffset]; | |||
// Read the next block of data | |||
bytesRead = input.Read(buffer, 0, chunkSize + inputOffset); | |||
if (bytesRead > 0) | |||
{ | |||
// process the cipher for the read block and add it to the output | |||
return cipher.ProcessBytes(buffer, inputOffset, bytesRead - inputOffset, output, outputOffset); | |||
} | |||
return 0; | |||
} | |||
public static int FinalizeCipherBlock(IBufferedCipher cipher, byte[] output, int outputOffset) | |||
{ | |||
// perform final action on cipher | |||
return cipher.DoFinal(output, outputOffset); | |||
} | |||
public static byte[] CreateKey(string password, string iv, int keySize = 256) | |||
{ | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(iv); | |||
return CreateKey(password, ivBytes, keySize); | |||
} | |||
public static byte[] CreateKey(string password, byte[] iv, int keySize = 256) | |||
{ | |||
const int Iterations = 300; | |||
var keyGenerator = new Rfc2898DeriveBytes(password, iv, Iterations); | |||
return keyGenerator.GetBytes(keySize / 8); | |||
} | |||
} | |||
public static class PGP | |||
{ | |||
public static bool IsPublicKey(string key) |
@@ -0,0 +1,129 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Security.Cryptography; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Teknik.Utilities.Cryptography | |||
{ | |||
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, byte[] key, byte[] iv) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
return ProcessCipher(false, stream, 1024, key, iv); | |||
} | |||
} | |||
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, byte[] key, byte[] iv) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
return ProcessCipher(true, stream, 1024, key, iv); | |||
} | |||
} | |||
public static byte[] ProcessCipher(bool encrypt, Stream input, int chunkSize, byte[] key, byte[] iv) | |||
{ | |||
// Make sure the input stream is at the beginning | |||
input.Seek(0, SeekOrigin.Begin); | |||
AESCryptoStream cryptoStream = new AESCryptoStream(input, encrypt, key, iv); | |||
// Initialize variables | |||
byte[] output = new byte[input.Length]; | |||
// Process the stream and save the bytes to the output | |||
int curByte = 0; | |||
int processedBytes = 0; | |||
byte[] buffer = new byte[chunkSize]; | |||
int bytesRemaining = (int)input.Length; | |||
int bytesToRead = chunkSize; | |||
do | |||
{ | |||
if (chunkSize > bytesRemaining) | |||
{ | |||
bytesToRead = bytesRemaining; | |||
} | |||
processedBytes = cryptoStream.Read(buffer, 0, bytesToRead); | |||
if (processedBytes > 0) | |||
{ | |||
buffer.Take(processedBytes).ToArray().CopyTo(output, curByte); | |||
// Clear the buffer | |||
Array.Clear(buffer, 0, chunkSize); | |||
} | |||
curByte += processedBytes; | |||
bytesRemaining -= processedBytes; | |||
} | |||
while (processedBytes > 0 && bytesRemaining > 0); | |||
return output; | |||
} | |||
public static void EncryptToFile(string filePath, Stream input, int chunkSize, byte[] key, byte[] iv) | |||
{ | |||
// Make sure the input stream is at the beginning | |||
input.Seek(0, SeekOrigin.Begin); | |||
AESCryptoStream cryptoStream = new AESCryptoStream(input, true, key, iv); | |||
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) | |||
{ | |||
int curByte = 0; | |||
int processedBytes = 0; | |||
byte[] buffer = new byte[chunkSize]; | |||
int bytesRemaining = (int)input.Length; | |||
int bytesToRead = chunkSize; | |||
do | |||
{ | |||
if (chunkSize > bytesRemaining) | |||
{ | |||
bytesToRead = bytesRemaining; | |||
} | |||
processedBytes = cryptoStream.Read(buffer, 0, bytesToRead); | |||
if (processedBytes > 0) | |||
{ | |||
fileStream.Write(buffer, 0, processedBytes); | |||
// Clear the buffer | |||
Array.Clear(buffer, 0, chunkSize); | |||
} | |||
curByte += processedBytes; | |||
bytesRemaining -= processedBytes; | |||
} | |||
while (processedBytes > 0 && bytesRemaining > 0); | |||
} | |||
} | |||
public static byte[] CreateKey(string password, string iv, int keySize = 256) | |||
{ | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(iv); | |||
return CreateKey(password, ivBytes, keySize); | |||
} | |||
public static byte[] CreateKey(string password, byte[] iv, int keySize = 256) | |||
{ | |||
const int Iterations = 300; | |||
var keyGenerator = new Rfc2898DeriveBytes(password, iv, Iterations); | |||
return keyGenerator.GetBytes(keySize / 8); | |||
} | |||
} | |||
} |
@@ -31,7 +31,8 @@ namespace Teknik.Utilities.Cryptography | |||
}; | |||
// Set the internal variables | |||
_Counter = counter; | |||
_Counter = new byte[counter.Length]; | |||
counter.CopyTo(_Counter, 0); | |||
} | |||
public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) | |||
@@ -103,8 +104,12 @@ namespace Teknik.Utilities.Cryptography | |||
counter.Length, symmetricAlgorithm.BlockSize / 8)); | |||
_SymmetricAlgorithm = symmetricAlgorithm; | |||
_IV = iv; | |||
_Counter = counter; | |||
_IV = new byte[iv.Length]; | |||
iv.CopyTo(_IV, 0); | |||
_Counter = new byte[counter.Length]; | |||
counter.CopyTo(_Counter, 0); | |||
_CounterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv); | |||
@@ -114,6 +119,9 @@ namespace Teknik.Utilities.Cryptography | |||
// Encrypt the counter | |||
EncryptCounter(); | |||
// Initial Increment | |||
IncrementCounter(); | |||
} | |||
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) | |||
@@ -130,13 +138,13 @@ namespace Teknik.Utilities.Cryptography | |||
// Encrypt the counter if we have reached the end, or | |||
if (_CounterPosition >= _EncryptedCounter.Length) | |||
{ | |||
// Encrypt the counter | |||
EncryptCounter(); | |||
//Reset current counter position | |||
_CounterPosition = 0; | |||
// Increment the counter for the next run | |||
// Encrypt the counter | |||
EncryptCounter(); | |||
// Increment the counter for the next batch | |||
IncrementCounter(); | |||
} | |||
@@ -162,7 +170,7 @@ namespace Teknik.Utilities.Cryptography | |||
public void ResetCounter() | |||
{ | |||
Array.Clear(_Counter, 0, _Counter.Length); | |||
Array.Copy(_IV, 0, _Counter, 0, _IV.Length); | |||
_IV.CopyTo(_Counter, 0); | |||
_Iterations = 0; | |||
} | |||
@@ -77,82 +77,5 @@ namespace Teknik.Utilities | |||
} | |||
} | |||
} | |||
public static void DecryptStreamToOutput(HttpResponseBase response, bool flush, Stream stream, int length, byte[] key, byte[] iv, string mode, string padding, int chunkSize) | |||
{ | |||
try | |||
{ | |||
if (flush) | |||
{ | |||
response.Flush(); | |||
} | |||
IBufferedCipher cipher = AES.CreateCipher(false, key, iv, mode, padding); | |||
int curByte = 0; | |||
int processedBytes = 0; | |||
byte[] buffer = new byte[chunkSize]; | |||
int bytesRemaining = length; | |||
int bytesToRead = chunkSize; | |||
int bytesRead = 0; | |||
do | |||
{ | |||
if (chunkSize > bytesRemaining) | |||
{ | |||
bytesToRead = bytesRemaining; | |||
} | |||
processedBytes = AES.ProcessCipherBlock(cipher, stream, 0, bytesToRead, buffer, 0, out bytesRead); | |||
if (processedBytes > 0) | |||
{ | |||
response.OutputStream.Write(buffer, 0, processedBytes); | |||
if (flush) | |||
{ | |||
response.Flush(); | |||
} | |||
// Clear the buffer | |||
Array.Clear(buffer, 0, chunkSize); | |||
} | |||
curByte += bytesRead; | |||
bytesRemaining -= bytesRead; | |||
} | |||
while (bytesRemaining > 0); | |||
// Clear the buffer | |||
Array.Clear(buffer, 0, chunkSize); | |||
// Finalize processing of the cipher | |||
processedBytes = AES.FinalizeCipherBlock(cipher, buffer, 0); | |||
if (processedBytes > 0) | |||
{ | |||
// We have bytes, lets write them to the output | |||
response.OutputStream.Write(buffer, 0, processedBytes); | |||
if (flush) | |||
{ | |||
response.Flush(); | |||
} | |||
} | |||
} | |||
catch (HttpException httpEx) | |||
{ | |||
// If we lost connection, that's fine | |||
if (httpEx.ErrorCode == -2147023667) | |||
{ | |||
// do nothing | |||
} | |||
else | |||
{ | |||
//throw httpEx; | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
throw ex; | |||
} | |||
finally | |||
{ | |||
// dispose of file stream | |||
stream.Dispose(); | |||
} | |||
} | |||
} | |||
} |
@@ -14,7 +14,16 @@ namespace Teknik.Utilities | |||
private Stream _Inner; | |||
private CounterModeCryptoTransform _Cipher; | |||
public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding) | |||
/// <summary> | |||
/// Performs Encryption or Decryption on a stream with the given Key and IV | |||
/// | |||
/// Cipher is AES-256 in CTR mode with no padding | |||
/// </summary> | |||
/// <param name="stream"></param> | |||
/// <param name="encrypt"></param> | |||
/// <param name="key"></param> | |||
/// <param name="iv"></param> | |||
public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv) | |||
{ | |||
_Inner = stream; | |||
@@ -44,7 +53,7 @@ namespace Teknik.Utilities | |||
int bytesRead = _Inner.Read(readBuf, 0, count); | |||
if (bytesRead > 0) | |||
{ | |||
// Process the | |||
// Process the read buffer | |||
processed = _Cipher.TransformBlock(readBuf, 0, bytesRead, buffer, 0); | |||
} | |||
@@ -68,11 +77,19 @@ namespace Teknik.Utilities | |||
{ | |||
// Process the cipher | |||
byte[] output = new byte[count]; | |||
//int processed = _Cipher.ProcessBytes(buffer, offset, count, output, 0); | |||
// Finalize the cipher | |||
//AES.FinalizeCipherBlock(_Cipher, output, processed); | |||
// Process the buffer | |||
int processed = _Cipher.TransformBlock(buffer, 0, count, output, 0); | |||
// Do we have more? | |||
if (processed < count) | |||
{ | |||
// Finalize the cipher | |||
byte[] finalBuf = _Cipher.TransformFinalBlock(buffer, processed, count); | |||
finalBuf.CopyTo(output, processed); | |||
processed += finalBuf.Length; | |||
} | |||
_Inner.Write(output, 0, count); | |||
} | |||
} | |||
@@ -187,7 +204,7 @@ namespace Teknik.Utilities | |||
int counterPos = (int)(_Inner.Position % _Cipher.InputBlockSize); | |||
// Are we out of sync with the cipher? | |||
if (_Cipher.Iterations != iterations || _Cipher.CounterPosition != counterPos) | |||
if (_Cipher.Iterations != iterations + 1 || _Cipher.CounterPosition != counterPos) | |||
{ | |||
// Reset the current counter | |||
_Cipher.ResetCounter(); |
@@ -106,6 +106,7 @@ | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="AccountType.cs" /> | |||
<Compile Include="Cryptography\AES.cs" /> | |||
<Compile Include="Cryptography\AesCounterMode.cs" /> | |||
<Compile Include="CurrencyHelper.cs" /> | |||
<Compile Include="CurrencyType.cs" /> |