my goal is to encrypt large (cca 10 GB) input file and append it to an existing System.IO.Packaging Package. I can use .NET Framework 3.5 only and no third-party libraries.
I tried maybee ten methods with no success. I tried to read the input to Stream, encrypt it and save to PackagePart. I tried to read the input file byte after byte, then encrypt byte read and append it to Stream from PackagePart too. Everytime I found a new issue (e.g. CryptoStream does not supports seeking and so on).
Could you show me the right way, please?
//method to create zip file (just a sample)
public static void AppendToZip(SomeType encryptedData)
{
using (Package zip = Package.Open(#"C:\myarchive.zip", FileMode.OpenOrCreate))
{
Uri uri = PackUriHelper.CreatePartUri(new Uri("/files/test.enc", UriKind.Relative));
try
{
part = zip.GetPart(uri);
}
catch
{
}
if (part == null)
{
part = zip.CreatePart(uri, "", CompressionOption.Maximum);
}
using (Stream dest = part.GetStream())
{
//how to write encryptedData to destination stream?
}
}
}
//sample method for encrypting a file
private static void Encrypt(string inputFile, string cryptFile, byte[] passwordBytes, byte[] saltBytes)
{
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.Zeros;
AES.Mode = CipherMode.CBC;
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
{
cs.WriteByte((byte)data);
}
fsIn.Close();
cs.Close();
fsCrypt.Close();
}
Try this out - play around with block size for performance. I did this with a 3.5 GB ISO successfully. However the zip file is much larger compressing encrypted content, so as the other guy said you're better compressing the file FIRST and then encrypting it. But I don't know your requirements, so here's this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Packaging;
namespace ZipTest
{
class Program
{
static void Main(string[] args)
{
// Block size we apply to all reads / writes
const int BLOCK_SIZE = 65536;
// The zip file we're using
var zipFileName = #"C:\temp\ZipSO\MyZip.zip";
// Password for encryption
var password = "ThisIsMyPassword";
// Name of temp file where we'll encrypt the file first
var intermediateFile = #"C:\temp\ZipSO\Intermediate_" + Guid.NewGuid().ToString();
// File we're encrypting / adding to archive
var inputFile = #"C:\temp\ZipSO\InputFile.txt";
// Salt for encryption
var salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
// For the new / existing package part
PackagePart part = null;
// Open the archive
using (var zip = Package.Open(zipFileName, System.IO.FileMode.OpenOrCreate))
{
// Uri for the part
var uri = PackUriHelper.CreatePartUri(new Uri("/files/test.enc", UriKind.Relative));
// Get existing part if found, or create new
if (zip.PartExists(uri))
part = zip.GetPart(uri);
else
part = zip.CreatePart(uri, "", CompressionOption.Maximum);
// Encrypt the file first
var passBytes = System.Text.Encoding.ASCII.GetBytes(password);
using (var fs = new System.IO.FileStream(intermediateFile, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
{
var key = new System.Security.Cryptography.Rfc2898DeriveBytes(passBytes, salt, 1000);
var keySize = 256;
var blockSize = 128;
var aes = new System.Security.Cryptography.RijndaelManaged()
{
KeySize = keySize,
BlockSize = blockSize,
Key = key.GetBytes(keySize / 8),
IV = key.GetBytes(blockSize / 8),
Padding = System.Security.Cryptography.PaddingMode.Zeros,
Mode = System.Security.Cryptography.CipherMode.CBC
};
using (var fsSource = new System.IO.FileStream(inputFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (var cs = new System.Security.Cryptography.CryptoStream(fs, aes.CreateEncryptor(), System.Security.Cryptography.CryptoStreamMode.Write))
{
var readBytes = new byte[BLOCK_SIZE];
int read;
while ((read = fsSource.Read(readBytes, 0, BLOCK_SIZE)) != 0)
{
cs.Write(readBytes, 0, read);
}
cs.Close();
}
fsSource.Close();
}
fs.Close();
}
// Now add it to the archive
using (var p = part.GetStream(System.IO.FileMode.OpenOrCreate))
{
using (var source = new System.IO.FileStream(intermediateFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
using (var bw = new System.IO.BinaryWriter(p))
{
var readBytes = new byte[BLOCK_SIZE];
int read;
while ((read = source.Read(readBytes, 0, BLOCK_SIZE)) != 0)
{
bw.Write(readBytes.Take(read).ToArray());
}
}
}
// Clean up the intermediate
System.IO.File.Delete(intermediateFile);
}
}
}
}
Related
Need help T_T
I'm running the code below to Decrypt an exe File I'm trying to Run it and Automatically execute the Decrypted file would it be possible to execute it without saving it's data to the disk?
I'm also trying to run it without the need of specifying the encrypted file name but have no idea what changes need to be done for this to happen or if it's even possible.
FileInfo encFile = new FileInfo("7GNTBBASDADASDASDASDASDASDASDASDSW7VBKGUX5TB5XBXDG3W4DWC6K6JBMTG7C2OYEHNPSN4PE6JYLJDUA"); // < File name in the current directory
const int ReadBufferSize = 64 * 1024;
static void Main(string[] args)
{
{
// DECRYPTION
FileInfo encFile = new FileInfo("7GNTBBASDADASDASDASDASDASDASDASDSW7VBKGUX5TB5XBXDG3W4DWC6K6JBMTG7C2OYEHNPSN4PE6JYLJDUA"); // < File name in the current directory
byte[] iv = Convert.FromBase64String("SWW/HAWEWQF/F2d/WrSSA==");
byte[] key = Convert.FromBase64String("ASDSADSAwwqIM221vASXG1221nqk=");
// DECRYPTION
// DECRYPTION
using (FileStream inp = encFile.OpenRead())
using (AesManaged aes = new AesManaged())
{
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.IV = iv;
aes.Key = key;
using (CryptoStream cs = new CryptoStream(inp, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
// crypted file structure: {name length x4}{full file name}{data length x8}{data}{sha512 hash of data x64}
byte[] nameLengthBits = new byte[2];
if (cs.Read(nameLengthBits, 0, 2) != 2)
{
Console.Error.WriteLine("ERROR: Failed reading file name size");
return;
}
ushort nameLength = BitConverter.ToUInt16(nameLengthBits, 0);
byte[] originalName = new byte[nameLength];
if (cs.Read(originalName, 0, nameLength) != nameLength)
{
Console.Error.WriteLine("ERROR: Failed reading file name");
return;
}
string fileName = Encoding.UTF8.GetString(originalName);
byte[] dataLengthBits = new byte[8];
if (cs.Read(dataLengthBits, 0, dataLengthBits.Length) != dataLengthBits.Length)
{
Console.Error.WriteLine("ERROR: Failed reading data length");
return;
}
long dataLength = BitConverter.ToInt64(dataLengthBits, 0);
string outputFileName = Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(fileName));
if (File.Exists(outputFileName))
{
}
byte[] decryptedHash;
long totalRead = 0;
using (FileStream outputStream = new FileStream(outputFileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
using (SHA512Managed hasher = new SHA512Managed())
{
byte[] buffer = new byte[ReadBufferSize];
long bytesRemaining = dataLength;
while (bytesRemaining > 0)
{
int readingThisRound = ReadBufferSize < bytesRemaining ? ReadBufferSize : (int)bytesRemaining;
int bytesRead = cs.Read(buffer, 0, readingThisRound);
totalRead += bytesRead;
// dump decrypted data to file
outputStream.Write(buffer, 0, bytesRead); }
//
//
hasher.TransformFinalBlock(buffer, 0, 0);
decryptedHash = hasher.Hash;}
byte[] originalHashBits = new byte[64];
if (cs.Read(originalHashBits, 0, originalHashBits.Length) != originalHashBits.Length) using (FileStream outputStream = new FileStream(outputFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
}
if (originalHashBits.SequenceEqual(decryptedHash))
My problem is the following:
I have a client/server application connected over sockets. My client´s task is to send a file byte-wise to the server. The server gets the bytes, decrypt them, send it back to the client and he writes them in a new file on disk.
I get everytime a serverside exception (System.Security.Cryptography.Exception: Padding is invalid and cannot be removed) at this line of code: plaintext = sr.ReadToEnd();
Could somebody help me to solve my problem?
Here is the decryption code:
public byte[] Dec(byte[] content, byte[] Key, byte[] IV, int fileLength, string filepath, int chunkSize, int bytesToRead)
{
byte[] contentDec;
string plaintext = null;
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream ms = new MemoryStream(content))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plaintext = sr.ReadToEnd();
cs.FlushFinalBlock();
}
contentDec = encoding.GetBytes(plaintext);
}
}
}
return contentDec;
}
Here is my encryption code:
public byte[] Enc(byte[] content,byte[] Key, byte[] IV, int fileLength,string filepath, int chunkSize, int bytesToRead)
{
byte[] contentEnc;
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(content);
}
contentEnc = ms.ToArray();
}
}
}
return contentEnc;
}
On client side I call encryption method like this
int chunkSize = 1024;
byte[] chunk = new byte[chunkSize];
using (FileStream fileReader = new FileStream(plainPath, FileMode.Open, FileAccess.Read))
using (FileStream filewriter = new FileStream(pathEncrypt, FileMode.Create, FileAccess.ReadWrite))
using (BinaryReader binaryReader = new BinaryReader(fileReader))
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.GenerateKey();
myRijndael.GenerateIV();
Key = myRijndael.Key;
IV = myRijndael.IV;
int bytesToRead = (int)fileReader.Length;
do
{
chunk = service.Enc(binaryReader.ReadBytes(chunkSize), Key, IV,(int)fileReader.Length,
fileReader.Name, chunkSize, bytesToRead);
filewriter.Write(chunk, 0, chunk.Length);
bytesToRead -= chunkSize;
} while (bytesToRead > 0);
}
Key and IV are declared as private byte[]
On client side I call decryption method like this
int chunkSize = 1024;
byte[] chunk = new byte[chunkSize];
using (FileStream fileReader = new FileStream(pathEncrypt, FileMode.Open, FileAccess.Read))
using (FileStream filewriter = new FileStream(pathDecrypt, FileMode.Create, FileAccess.ReadWrite))
using (BinaryReader binaryReader = new BinaryReader(fileReader))
{
int bytesToRead = (int)fileReader.Length;
do
{
chunk = service.Dec(binaryReader.ReadBytes(chunkSize), Key, IV, (int)fileReader.Length,
fileReader.Name, chunkSize, bytesToRead);
filewriter.Write(chunk, 0, chunk.Length);
bytesToRead -= chunkSize;
} while (bytesToRead > 0);
}
Edit: This is my connection establishment between client and server.
Server:
var host = new ServiceHost(typeof(Service),
new Uri("net.pipe://localhost"));
host.AddServiceEndpoint(typeof(TiService),
new NetNamedPipeBinding(), "TestService");
host.Open();
Console.WriteLine("Server connection established...");
Console.ReadKey();
Client:
var callback = new Callback();
var context = new InstanceContext(callback);
var pipeFactory =
new DuplexChannelFactory<TiService>(context,
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/TestService"));
service = pipeFactory.CreateChannel();
service.Connect();
Your problem start from using StreamWriter in the encryption. It's meant for writing Text file, not arbitrary file. When you call sw.Write(content), it simply call content.ToString(), which return "System.Byte[]", instead what you'd probably expect, each byte of the array. To fix it, simply write the CryptoStream, no need to use StreamWriter, like this :
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(content, 0, content.Length);
}
contentEnc = ms.ToArray();
}
}
You probably noticed I used AesCng instead of RijndaelManaged. Why? Because it's much faster in my test, and unless you really need non-standard block, there's no benefit of using RijndaelManaged. Also, I use the parameterless CreateEncryptor because you already set the Key & IV on the previous lines anyway.
Same deal in the decryption. You shouldn't treat them as text, thus :
var buffer = new byte[content.Length]; //at first its size is actual size+padding
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(content))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
var actualSize = cs.Read(buffer, 0, content.Length);
//we write the decrypted content to the buffer, and get the actual size
Array.Resize(ref buffer, actualSize);
//then we resize the buffer to the actual size
}
}
}
return buffer;
Also, your usage of the Enc and Dec is needlessly complex. It's already able to handle the whole file by itself. So to encrypt the file, simply use
var original = File.ReadAllBytes("originalPath");
var enc = Enc(original, rM.Key, rM.IV);
File.WriteAllBytes("encryptedPath", enc);
And to decrypt the file, just use
var enc = File.ReadAllBytes("encryptedPath");
var dec = Dec(enc, rM.Key, rM.IV);
File.WriteAllBytes("decryptedPath", dec);
As you can see, I throw away the fileLength,filepath, chunkSize, and bytesToRead on Enc & Dec, because your current code doesn't actually use them anyway. I've tried the code with short text file on ASCII, Unicode and UTF-8, and with large binary files, all encrypted & decrypted successfully with identical hash on the final decrypted files.
Edit :
Turning the code into direct filestream writing affair actually makes everything so much simpler.
public static void Transform(string source, string target, ICryptoTransform transf)
{
var bufferSize = 65536;
var buffer = new byte[bufferSize];
using (var sourceStream = new FileStream(source, FileMode.Open))
{
using (var targetStream = new FileStream(target, FileMode.OpenOrCreate))
{
using (CryptoStream cs = new CryptoStream(targetStream, transf, CryptoStreamMode.Write))
{
var bytesRead = 0;
do
{
bytesRead = sourceStream.Read(buffer, 0, bufferSize);
cs.Write(buffer, 0, bytesRead);
} while (bytesRead != 0);
}
}
}
}
public static void Enc(string source, byte[] Key, byte[] IV, string target)
{
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor();
Transform(source, target, encryptor);
}
}
public static void Dec(string source, byte[] Key, byte[] IV, string target)
{
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor();
Transform(source, target, decryptor);
}
}
Usage is :
Enc(#"originalPath", key, iv, #"encryptedPath");
Dec(#"encrypedPath", key, iv, #"decryptedPath");
There are files created from multiple layers of data:
//input is not always the same, but the structure is, so for example there might be h4 and h5, but I will know that, so that is not the problem
private void generalizationofwrittendata(string filename, int someint, int chunks, string outputfilename, ICryptoTransform crypt, byte[] somedata, int h1, int h2, int h3)
{
using (FileStream fs = new FileStream(outputfilename, FileMode.Create, FileAccess.Write))
{
using (BinaryWriter w = new BinaryWriter(fs, Encoding.ASCII))
{
//writing some data with binary writer
w.Write(h1);
w.Write(h2);
w.Write(h3);
using (FileStream fsIn = File.OpenRead(filename))
{
using (CryptoStream cs = new CryptoStream(fs, crypt, CryptoStreamMode.Write))
{
//writing the rest of data with crypto stream
cs.Write(somedata, 0, somedata.Length);
byte[] chunk = new byte[chunks];
int bytesRead = 0;
while ((bytesRead = fsIn.Read(chunk, 0, chunks)) > 0)
{
cs.Write(chunk, 0, bytesRead);
}
}
}
}
}
}
"generalizationofwrittendata" works perfectly fine in an intended way.
Now the problem is in separating all of that data from the file:
private void test(string filename, int someint, int chunks, string outputfilename, ICryptoTransform crypt)
{
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
using (BinaryReader br = new BinaryReader(fs, Encoding.ASCII))
{
//reading some not encrypted data
int h1 = br.ReadInt32();
int h2 = br.ReadInt32();
int h3 = br.ReadInt32();
using (CryptoStream cs = new CryptoStream(fs, crypt, CryptoStreamMode.Read))
{
using (BinaryReader br2 = new BinaryReader(cs, Encoding.ASCII))
{
//reading some encrypted data
byte[] somedata = br2.ReadBytes(someint);
//writing the rest of the data to file
using (FileStream fsOut = new FileStream(outputfilename, FileMode.Create))
{
byte[] chunk = new byte[chunks];
int bytesRead = 0;
while ((bytesRead = cs.Read(chunk, 0, chunks)) > 0)
{
fsOut.Write(chunk, 0, bytesRead);
}
}
}
}
}
}
}
This approach simply doesn't work. Only h1,h2,h3 can be received back in this way. somedata will be different and the written file will be longer than the original data, which lead me to thinking that the problem is with CryptoStream+BinaryReader reading from the beginning of the file.
Probably you will suggest me to use MemoryStream, but this will be valid only for small files, thus will lead to memory out of range exception.
The only other solution I found was SubStream implementation, but unfortunately when I used it between "fs" and "cs" it resulted in wrong "somedata" and the resulted file had wrong data as well.
Maybe there is a way to do this using Memory-Mapped Files? But I'm not quite sure how would I need to approach it in that way.
Or maybe I'm missing something else, since writing in "generalizationofwrittendata" using BinaryWriter and then CryptoStream does seem to work just fine.
Update#1:
So after receiving replies I've rechecked all of the code, especially related to ICryptoTransform. The ICryptoTransform is definitely not the problem, it is exactly the same for both methods.
The other thing that I noticed was that I used "BinaryReader br2" for no apparent reason, so I have removed that:
private void test(string filename, int someint, int chunks, string outputfilename, ICryptoTransform crypt)
{
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
using (BinaryReader br = new BinaryReader(fs, Encoding.ASCII))
{
//reading some not encrypted data
int h1 = br.ReadInt32();
int h2 = br.ReadInt32();
int h3 = br.ReadInt32();
using (CryptoStream cs = new CryptoStream(fs, crypt, CryptoStreamMode.Read))
{
//reading some encrypted data
byte[] somedata = new byte[someint];
cs.Read(somedata, 0, someint);
//writing the rest of the data to file
using (FileStream fsOut = new FileStream(outputfilename, FileMode.Create))
{
byte[] chunk = new byte[chunks];
int bytesRead = 0;
while ((bytesRead = cs.Read(chunk, 0, chunks)) > 0)
{
fsOut.Write(chunk, 0, bytesRead);
}
}
}
}
}
}
But unfortunately that hasn't solved the issue, both somedata and the data written to file aren't the same as original.
Update#2:
So that was a stupid problem - I've created CreateEncryptor() instead of CreateDecryptor() for decryption.
Possibly I am being thick, but couldn't you just seek to the appropriate position of fs and continue to use the same binary reader?
I will try this but setting up a crypto key will take some time.
Edit: Also, are you certain that you are building the CryptoTransform correctly when you decrypt?
Edit:
Your code works as you supplied it when I use the following code:
byte[] testdata = new byte[] { 0x12, 0x34, 0x56 };
byte[] key;
byte[] iv;
String encryptedFile = "encrypted.bin";
private void buttonCrypt_Click(object sender, EventArgs e)
{
ICryptoTransform transform;
RijndaelManaged rjndl = new RijndaelManaged();
rjndl.KeySize = 256;
rjndl.BlockSize = 256;
rjndl.Mode = CipherMode.CBC;
transform = rjndl.CreateEncryptor();
key = (byte[])rjndl.Key.Clone();
iv = (byte[])rjndl.IV.Clone();
generalizationofwrittendata("plain.bin",testdata.Length,1,encryptedFile,transform,testdata,0x0abbccdd,0x01223344,0x09887766);
}
private void buttonDecrypt_Click(object sender, EventArgs e)
{
RijndaelManaged rjndl = new RijndaelManaged();
rjndl.KeySize = 256;
rjndl.BlockSize = 256;
rjndl.Mode = CipherMode.CBC;
var transform = rjndl.CreateDecryptor(key, iv);
test(encryptedFile, testdata.Length, 1, "decrypted.bin", transform);
}
The other data is read as {0x12,0x34,0x56}, h1 to h3 are the values I supplied and decrypted.bin is the same as plain.bin.
This points to a problem with the key you are supplying to the decryptor.
Update 1:
I have modded my test harness to use Aes:
private void buttonCrypt_Click(object sender, EventArgs e)
{
ICryptoTransform transform;
AesManaged Aes = new AesManaged();
Aes.KeySize = 256;
Aes.BlockSize = 128;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
transform = Aes.CreateEncryptor();
key = (byte[])Aes.Key.Clone();
iv = (byte[])Aes.IV.Clone();
generalizationofwrittendata("plain.bin",testdata.Length,1,encryptedFile,transform,testdata,0x0abbccdd,0x01223344,0x09887766);
}
private void buttonDecrypt_Click(object sender, EventArgs e)
{
AesManaged Aes = new AesManaged();
Aes.KeySize = 256;
Aes.BlockSize = 128;
Aes.Mode = CipherMode.CBC;
Aes.Padding = PaddingMode.PKCS7;
var transform = Aes.CreateDecryptor(key, iv);
test(encryptedFile, testdata.Length, 1, "decrypted.bin", transform);
}
The results are identical, it still works as you originally posted it.
I am getting Aes to create a key and initialisation vector which I save, you need to check that the encryption and decryption both call your Aes generator with exactly the same values. Also, ensure that you are using Aes.CreateEncyptor() to encrypt and Aes.CreateDecryptor() to decrypt.
The problem really does have to be in the keys so my suggestion is to break after you create the encryptor and dump the key/iv, then do the same after you create the decryptor and check that they are byte for byte identical.
I'm trying to make a console application that can encrypt and decrypt file. I gave encrypted file a custom file extension ".aes" (e.g. samplefile.aes) now the problem is when I decrypt the file there is no way to identify what is the original extension of the file when it was encrypted. Is there any chance I can get file type from AES 256 encrypted file ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;
namespace FileEncryption
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 2)
{
Environment.ExitCode = 1;
Console.Error.WriteLine(Strings.CommandlineUsage);
return;
}
bool encrypt = args[0].StartsWith("-e", StringComparison.InvariantCultureIgnoreCase);
bool decrypt = args[0].StartsWith("-d", StringComparison.InvariantCultureIgnoreCase);
if (!(encrypt || decrypt))
{
Environment.ExitCode = 1;
Console.Error.WriteLine(Strings.CommandlineUnknownMode);
return;
}
string inputname = (args.Length >= 3) ? args[2] : null;
if (inputname != null && !File.Exists(inputname))
{
Environment.ExitCode = 2;
Console.Error.WriteLine(Strings.CommandlineInputFileNotFound);
return;
}
byte[] passwordBytes = Encoding.UTF8.GetBytes(args[1]);
// Hash the password with SHA256
passwordBytes = SHA256Managed.Create().ComputeHash(passwordBytes);
try
{
if (encrypt)
{
//Encrypt file
byte[] bytesToBeEncrypted = File.ReadAllBytes(inputname);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
//Save encrypted file
string fileEncrypted = inputname.Remove(inputname.IndexOf('.')) + ".aes";
File.WriteAllBytes(fileEncrypted, bytesEncrypted);
}
else
{
byte[] bytesToBeDecrypted = File.ReadAllBytes(inputname);
byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
string file = inputname;
File.WriteAllBytes(file, bytesDecrypted);
}
Environment.ExitCode = 0;
}
catch (Exception ex)
{
Console.Error.WriteLine(string.Format(Strings.CommandlineError, ex.Message));
}
}
//***********************************************************************************************
// --- HELPER FUNCTIONS ---
//*
//Encrypt File
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
//Decrypt File
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
}
}
I know that the nature of encrypted file is not to identify original extension. What I was going to do is that when I encrypt the file I will give it .originalextension.aes (e.g. picture.png.aes) and then when I decrypt it I just need to remove .aes extension. Just wondering is this a good way or not?
If the file type is required to be hidden by the encryption or not is a user requirement. You may just want to protect the content and not the file type. Note that the file size can also give hints to an "attacker". It's also up to you if you need to hide that kind of meta-data.
Without the extension you may have a look at the file contents by using file fingerprinting. Many file formats give some kind of hint on what they are. For instance, it's certainly possible to guess the character encoding of .txt files. JPG files have JPG headers, same goes for zip archives etc. etc. Usually these tools output the MIME file type (as the extension is not always that well defined or standardized).
On GNU systems you may use the file command line for that. This kind of fingerprinting is also used for many content management systems (CMS). So you can have a look at the CMS in your language and try to filter out the file command functionality in there.
Or, now you know the right search keywords, you can simply find it here on StackOverflow: Using .NET, how can you find the mime type of a file based on the file signature not the extension was given as 3rd hit when typing in "fingerprint file type c# mime".
Note that, because extensions are not always that well defined, you may not get back the original extension this way. To allow this you need to store the extension in plaintext (in the filename) growing the file name size (!) or you can store it encrypted together with the data. You'd have to create an encryption protocol for that.
I'm looking for a way to encrypt a byte array in unity c# and decrypt on a node.js server.
I'm open to any implementation of either but I have currently gone with the below code which encrypts/decrypts fine in unity but I receive the error:
TypeError: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
When decrypting a file encrypted in unity using RijndaelManaged 128
Find the encrypting and decrypting code below:
Unity C# Encrypt
private void GenerateEncryptionKey(string userID)
{
//Generate the Salt, with any custom logic and using the user's ID
StringBuilder salt = new StringBuilder();
for (int i = 0; i < 8; i++)
{
salt.Append("," + userID.Length.ToString());
}
Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes (Encoding.UTF8.GetBytes(userID), Encoding.UTF8.GetBytes(salt.ToString()), 100);
m_cryptoKey = pwdGen.GetBytes(KEY_SIZE / 8);
m_cryptoIV = pwdGen.GetBytes(KEY_SIZE / 8);
}
public void Save(string path)
{
string json = MiniJSON.Json.Serialize(m_saveData);
using (RijndaelManaged crypto = new RijndaelManaged())
{
crypto.BlockSize = KEY_SIZE;
crypto.Padding = PaddingMode.PKCS7;
crypto.Key = m_cryptoKey;
crypto.IV = m_cryptoIV;
crypto.Mode = CipherMode.CBC;
ICryptoTransform encryptor = crypto.CreateEncryptor(crypto.Key, crypto.IV);
byte[] compressed = null;
using (MemoryStream compMemStream = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(compMemStream, Encoding.UTF8))
{
writer.Write(json);
writer.Close();
compressed = compMemStream.ToArray();
}
}
if (compressed != null)
{
using (MemoryStream encMemStream = new MemoryStream(compressed))
{
using (CryptoStream cryptoStream = new CryptoStream(encMemStream, encryptor, CryptoStreamMode.Write))
{
using (FileStream fs = File.Create(GetSavePath(path)))
{
byte[] encrypted = encMemStream.ToArray();
fs.Write(encrypted, 0, encrypted.Length);
fs.Close();
}
}
}
}
}
}
ignore the compressed bit, I'll eventually be compressing the data for encryption but I have removed it in this example.
Node.JS Decrypt
var sUserID = "hello-me";
var sSalt = "";
for (var i = 0; i < 8; i++)
{
sSalt += "," + sUserID.length;
}
var KEY_SIZE = 128;
crypto.pbkdf2(sUserID, sSalt, 100, KEY_SIZE / 4, function(cErr, cBuffer){
var cKey = cBuffer.slice(0, cBuffer.length / 2);
var cIV = cBuffer.slice(cBuffer.length / 2, cBuffer.length);
fs.readFile("save.sav", function (cErr, cData){
try
{
var cDecipher = crypto.createDecipheriv("AES-128-CBC", cKey, cIV);
var sDecoded = cDecipher.update(cData, null, "utf8");
sDecoded += cDecipher.final("utf8");
console.log(sDecoded);
}
catch(e)
{
console.log(e.message);
console.log(e.stack);
}
});
});
I believe the problem is something to do with padding! I am not using:
cryptoStream.FlushFinalBlock();
when saving the file in c# land because for some reason after doing that c# can't decrypt it anymore and it doesn't really have an effect on the ability of node to decrypt it either, but maybe I'm just missing something in the decryption of it with padding?
Any help is appreciated
One problem is that you're using PasswordDeriveBytes which according to this article is for PBKDF1, whereas Rfc2898DeriveBytes is for PBKDF2. You're using PBKDF2 in your node script.
Then you should check that your cKey and cIV values match between C# and node.
Okay well it seems that order of operation is very important when encrypting and decryption using RijndaelManaged.
Below is the code to encrypt and decrypt in Unity and works with the node.js code posted in the question.
public void Save(string path)
{
string json = MiniJSON.Json.Serialize(m_saveData);
using (RijndaelManaged crypto = new RijndaelManaged())
{
crypto.BlockSize = KEY_SIZE;
crypto.Padding = PaddingMode.PKCS7;
crypto.Key = m_cryptoKey;
crypto.IV = m_cryptoIV;
crypto.Mode = CipherMode.CBC;
ICryptoTransform encryptor = crypto.CreateEncryptor(crypto.Key, crypto.IV);
byte[] compressed = null;
using (MemoryStream compMemStream = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(compMemStream, Encoding.UTF8))
{
writer.Write(json);
writer.Close();
//compressed = CLZF2.Compress(compMemStream.ToArray());
compressed = compMemStream.ToArray();
}
}
if (compressed != null)
{
using (MemoryStream encMemStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(encMemStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(compressed, 0, compressed.Length);
cryptoStream.FlushFinalBlock();
using (FileStream fs = File.Create(GetSavePath(path)))
{
encMemStream.WriteTo(fs);
}
}
}
}
}
}
public void Load(string path)
{
path = GetSavePath(path);
try
{
byte[] decrypted = null;
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (RijndaelManaged crypto = new RijndaelManaged())
{
crypto.BlockSize = KEY_SIZE;
crypto.Padding = PaddingMode.PKCS7;
crypto.Key = m_cryptoKey;
crypto.IV = m_cryptoIV;
crypto.Mode = CipherMode.CBC;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = crypto.CreateDecryptor(crypto.Key, crypto.IV);
using (CryptoStream cryptoStream = new CryptoStream(fs, decryptor, CryptoStreamMode.Read))
{
using (MemoryStream decMemStream = new MemoryStream())
{
var buffer = new byte[512];
var bytesRead = 0;
while ((bytesRead = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
decMemStream.Write(buffer, 0, bytesRead);
}
//decrypted = CLZF2.Decompress(decMemStream.ToArray());
decrypted = decMemStream.ToArray();
}
}
}
}
if (decrypted != null)
{
using (MemoryStream jsonMemoryStream = new MemoryStream(decrypted))
{
using (StreamReader reader = new StreamReader(jsonMemoryStream))
{
string json = reader.ReadToEnd();
Dictionary<string, object> saveData = MiniJSON.Json.Deserialize(json) as Dictionary<string, object>;
if (saveData != null)
{
m_saveData = saveData;
}
else
{
Debug.LogWarning("Trying to load invalid JSON file at path: " + path);
}
}
}
}
}
catch (FileNotFoundException e)
{
Debug.LogWarning("No save file found at path: " + path);
}
}