c# Encryption in a multi-layered Streams read/write - c#

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.

Related

C# Send byte[] to server, decrypt content and send it as byte[] back to client

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");

Encrypting and Decrypting a file with AES generates a broken file

I'm trying to encrypt and decrypt a file using AES. The problem that I have is that when the file gets decrypted, it is broken and you can't open it. The original file has a length of 81.970 bytes and the decrypted file has a length of 81.984 bytes...so there are 14 bytes added for some reason. The problem could be in the way the file gets encrypted but I don't know what I'm doing wrong.
What am I missing here? Could it be the way I'm processing the password, the iv and the padding?
Thanks for your time!
This is the code I use to encrypt:
private AesManaged aesManaged;
private string filePathToEncrypt;
public Encrypt(AesManaged aesManaged, string filePathToEncrypt)
{
this.aesManaged = aesManaged;
this.filePathToEncrypt = filePathToEncrypt;
}
public void DoEncryption()
{
byte[] cipherTextBytes;
byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);
using(ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(textBytes, 0, textBytes.Length);
cs.FlushFinalBlock();
cipherTextBytes = ms.ToArray();
}
File.WriteAllBytes("EncryptedFile.aes", cipherTextBytes);
}
This is the code I use to decrypt:
private AesManaged aesManaged;
private string filePathToDecrypt;
public Decrypt(AesManaged aesManaged, string filePathToDecrypt)
{
this.aesManaged = aesManaged;
this.filePathToDecrypt = filePathToDecrypt;
}
public void DoDecrypt()
{
byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
byte[] clearBytes = new byte[cypherBytes.Length];
ICryptoTransform encryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV);
using (MemoryStream ms = new MemoryStream(cypherBytes))
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Read))
{
cs.Read(clearBytes, 0, clearBytes.Length);
clearBytes = ms.ToArray();
}
File.WriteAllBytes("DecryptedFile.gif", clearBytes);
}
And here is how I call the functions:
string filePathToEncrypt = "dilbert.gif";
string filePathToDecrypt = "EncryptedFile.aes";
string password = "Password";
string passwordSalt = "PasswordSalt";
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(passwordSalt));
var aesManaged = new AesManaged
{
Key = deriveBytes.GetBytes(128 / 8),
IV = deriveBytes.GetBytes(16),
Padding = PaddingMode.PKCS7
};
Console.WriteLine("Encrypting File...");
var encryptor = new Encrypt(aesManaged, filePathToEncrypt);
encryptor.DoEncryption();
Thread.Sleep(300);
Console.WriteLine("Decrypting File...");
var decryptor = new Decrypt(aesManaged, filePathToDecrypt);
decryptor.DoDecrypt();
Thread.Sleep(300);
Try with:
public void DoEncryption()
{
byte[] cipherBytes;
byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);
using (ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream input = new MemoryStream(textBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
input.CopyTo(cs);
cs.FlushFinalBlock();
cipherBytes = output.ToArray();
}
File.WriteAllBytes("EncryptedFile.aes", cipherBytes);
}
and
public void DoDecrypt()
{
byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
byte[] textBytes;
using (ICryptoTransform decryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream input = new MemoryStream(cypherBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(input, decryptor, CryptoStreamMode.Read))
{
cs.CopyTo(output);
textBytes = output.ToArray();
}
File.WriteAllBytes("DecryptedFile.gif", textBytes);
}
Note that the code could be modified to not use temporary byte[] and read/write directly to input/output streams.
In general you can't desume the length of the plaintext from the length of the cyphertext, so this line:
new byte[cypherBytes.Length]
was totally wrong.
And please, don't use Encoding.ASCII in 2016. It is so like previous century. Use Encoding.UTF8 to support non-english characters.
The answer may be very simple. I don't see where do u try to choose a cipher mode, so by default it probably takes CBC, as IV was inited. Then, 81.970 are padded by 14 bytes, to be divisible by 32. So when it happens, the memory you allocated was just 81.970, so the padding bytes doesn't write correctly, cause of some sort of memory leak, and when decrypt is started, unpadding doesn't work correctly.

Encrypt large file and add to ZIP

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);
}
}
}
}

C# aes encryption: Input size not output size

I'm trying to read a file, encrypt it, and send it to a server over socket, where it is written. And then the other way around, read it on server, send it to client, decrypt it, and write it again.
My problem using C# Aes class is, that the input size doesn't equal the output size.
For example, when I read 4096 bytes from the file, the output size is 4112 bytes, 16 bytes more. OK, so 4112 bytes are sent and written on the server, but when I get the file again, I can only send a maximum of 4096 bytes over the socket, and then, of course, the decrypt function on client throws an exception, that the padding is invalid and cannot be removed. Sure I could try to read less bytes on the client, but that doesn't work as well.
I'm a very experienced C++ programmer, and I've done this with OpenSsl, and it worked like a charm. The input size has been always the output size, I don't know what is wrong with my functions in C#.
this is the sending part:
byte[] SendData = new byte[4096];
iBytesRead = FileRead.Read (SendData, 0, 4096);
SendData = aes.encrypt (Encoding.Default.GetString (SendData, 0, iBytesRead), iBytesRead);
String a = aes.decrypt (SendData); // no problems here because the size is correct
Socket.sendB (SendData, SendData.Length);
and the part of receiving from server:
byte[] WriteData = new byte[4096],
Temp;
if ((iBytesReceived = Socket.receiveB (ref WriteData)) == 0)
break;
if (Encoding.ASCII.GetString (WriteData, 0, iBytesReceived) == "end")
break;
for (uint i = 0; i < iBytesReceived; i++)
Temp[i] = WriteData[i];
byte[] a = Encoding.Default.GetBytes (aes.decrypt (Temp));
FileWrite.Write (a, 0, Temp.Length);
Aes functions:
public byte[] encrypt(String _InStr, int _InStrLength)
{
if (!bKeySet)
return ErrorReturn;
byte[] encrypted;
using (Aes aes = Aes.Create ())
{
aes.Key = Key;
aes.IV = IV;
//aes.Padding = PaddingMode.PKCS7;
//aes.BlockSize = 128;
//aes.KeySize = 128;
//aes.Mode = CipherMode.CFB;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// Create the streams used for encryption.
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(_InStr);
}
}
ms.Close ();
encrypted = ms.ToArray ();
}
}
return encrypted;
}
public String decrypt(byte[] _InStr)
{
if (!bKeySet)
return "";
String plaintext;
using (Aes aes = Aes.Create ())
{
aes.Key = Key;
aes.IV = IV;
//aes.Padding = PaddingMode.PKCS7;
//aes.BlockSize = 128;
//aes.KeySize = 128;
//aes.Mode = CipherMode.CBC;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(_InStr))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd ();
}
}
}
}
return plaintext;
}
As it was said, if any padding is used, the output will be aligned to a block size. However .Net doesn't want to work with incomplete blocks when PaddingMode.None is used. You should pad data yourself before encryption(decryption) and remove added bytes after.
One of the way to do this is to wrap ICryptoTransform passed to a CryptoStream

Encrypt RijndaelManaged 128 in unity c# and Decrypt in Node.JS

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);
}
}

Categories

Resources