I'm using some code like this to encrypt a file.
FileStream fsInput = new FileStream(ifile_path,
FileMode.Open,
FileAccess.Read);
FileStream fsEncrypted = new FileStream(ofile_path,
FileMode.Create,
FileAccess.Write);
AesCryptoServiceProvider AES = new AesCryptoServiceProvider();
AES.Mode = CipherMode.CBC;
AES.KeySize = 256;
iv = AES.IV;
AES.Key = key;
ICryptoTransform aesencrypt = AES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsEncrypted,
aesencrypt,
CryptoStreamMode.Write);
byte[] bytearrayinput = new byte[fsInput.Length];
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Close();
fsInput.Close();
fsEncrypted.Close();
However, while this code successfully encrypts .txt and .xml files, it doesn't work on other file types such as .docx, or image file formats. What changes can I make to the code to extend the functionality to all such file types?
You want to use a BinaryReader and BinaryWriter for doing the file I/O. The normal StreamReader will attempt to read the bytes using a particular encoding as it implements TextReader and will mangle primitive data types. This is why plain text .txt and .xml work while .docx files do not.
Related
I've been fighting with chained using statements, and am unable to resolve the latest in a long line of implementation issues. I need to compress, then encrypt and append the generated IV to the selected file. This all appears to work correctly, however i'm unable to unwind the process. After looking at several similar stack postings and articles i'm still unable to get it to work and am now after more direct assistance.
The latest thrown error is System.IO.InvalidDataException: 'Found invalid data while decoding.' It appears that the decryption stream isn't functioning as intended and that's throwing the decompression stream out of wack.
byte[] key;
byte[] salt;
const int keySize = 256;
const int blockSize = keySize;
byte[] iv = new byte[blockSize / 8];//size to bits
RijndaelManaged rjndl;
RNGCryptoServiceProvider cRng;
void InitializeCryptor() {
//Temporarily define the salt & key
salt = Encoding.UTF8.GetBytes("SaltShouldBeAtLeast8Bytes");
key = new Rfc2898DeriveBytes("MyL0ngPa$$phra$e", salt, 4).GetBytes(keySize / 8);
//Initialize the crypto RNG generator
cRng = new RNGCryptoServiceProvider();
// Create instance of Rijndael (AES) for symetric encryption of the data.
rjndl = new RijndaelManaged();
rjndl.KeySize = keySize;
rjndl.BlockSize = blockSize;
rjndl.Mode = CipherMode.CBC;
}
void CompressAndEncryptFile(string relativeFilePath, string fileName) {
//Create a unique IV each time
cRng.GetBytes(iv);
//Create encryptor
rjndl.Key = key;
rjndl.IV = iv;
ICryptoTransform encryptor = rjndl.CreateEncryptor(rjndl.Key, rjndl.IV);
//Create file specific output sub-directory
Directory.CreateDirectory(Path.Combine(outputPath, relativeFilePath));
//Read and compress file into memory stream
using (FileStream readStream = File.OpenRead(Path.Combine(initialpath, relativeFilePath, fileName)))
using (FileStream writeStream = new FileStream(Path.Combine(outputPath, relativeFilePath, fileName + ".dat"), FileMode.Create))
using (CryptoStream encryptStream = new CryptoStream(writeStream, encryptor, CryptoStreamMode.Write))
using (DeflateStream compStream = new DeflateStream(encryptStream, CompressionLevel.Optimal)) {
//Write the following to the FileStream for the encrypted file:
// - length of the IV
// - the IV
byte[] ivSize = BitConverter.GetBytes(rjndl.IV.Length);
writeStream.Write(ivSize, 0, 4);
writeStream.Write(rjndl.IV, 0, rjndl.BlockSize / 8);
readStream.CopyTo(compStream);
}
}
void DecryptAndDecompressFile(string relativeFilePath) {
string outputPath = Path.Combine(initialpath, "Unpack");
Directory.CreateDirectory(outputPath);
using (FileStream readStream = new FileStream(Path.Combine(initialpath, manifestData.version, relativeFilePath + ".dat"), FileMode.Open)) {
byte[] tmpLength = new byte[4];
//Read length of IV
readStream.Seek(0, SeekOrigin.Begin);
readStream.Read(tmpLength, 0, 3);
int ivLength = BitConverter.ToInt32(tmpLength, 0);
byte[] readIv = new byte[ivLength];
//Read IV
readStream.Seek(4, SeekOrigin.Begin);
readStream.Read(readIv, 0, ivLength);
rjndl.IV = readIv;
//Start at beginning of encrypted data
readStream.Seek(4 + ivLength, SeekOrigin.Begin);
//Create decryptor
ICryptoTransform decryptor = rjndl.CreateEncryptor(key, readIv);
using (CryptoStream decryptStream = new CryptoStream(readStream, decryptor, CryptoStreamMode.Read))
using (DeflateStream decompStream = new DeflateStream(decryptStream, CompressionMode.Decompress))
using (FileStream writeStream = new FileStream(Path.Combine(outputPath, relativeFilePath), FileMode.Create)) {
decompStream.CopyTo(writeStream);
}
}
}
For those who like to point to other similar stack questions and vote to close/duplicate without offering support, the following are the threads, and posts I've worked through first, each without success.
https://learn.microsoft.com/en-us/dotnet/standard/security/walkthrough-creating-a-cryptographic-application
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rijndaelmanaged?redirectedfrom=MSDN&view=netcore-3.1
Chained GZipStream/DeflateStream and CryptoStream (AES) breaks when reading
DeflateStream / GZipStream to CryptoStream and vice versa
https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.gzipstream?redirectedfrom=MSDN&view=netcore-3.1#code-snippet-2
How to fix 'Found invalid data while decoding.'
Compression/Decompression string with C#
After ~2 days of investigating, i located my error.
I was calling rjndl.CreateEncryptor instead of rjndl.CreateDecryptor during the decryption portion... (Please tell me this type of $#!t happens to others too)
Once i finish testing i'll update my question code to serve as a nice example for anyone who lands here via google in the future.
I am attempting to copy a memory stream to a file stream. I noticed that the output exe is corrupt when decrypted. I am certain there is no issue with the decrypt function. Here is the code
private MemoryStream My_Encrypt(Stream inputFile)
{
//FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
MemoryStream fsCrypt = new MemoryStream();
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write);
int data;
while ((data = inputFile.ReadByte()) != -1)
cs.WriteByte((byte)data);
inputFile.Flush();
return fsCrypt;
}
MemoryStream ms = My_Encrypt(bundleStream);
ms.Seek(0, SeekOrigin.Begin);
FileStream atest = new FileStream("c:\\Somefile.exe",FileMode.Create);
ms.Seek(0, SeekOrigin.Begin);
ms.CopyTo(atest);
atest.Close();
More details:
The reason I am saying that the memory stream method is not working is because in My_Encrypt method if I replace fsCrypt with FileStream instead of Memory Stream and close fsCrypt at the end of the method and then reopen the saved file and write it to another file it works. My question is why is the memory stream method not working.
I believe you have to call FlushFinalBlock on the CryptoStream.
private MemoryStream My_Encrypt(Stream inputFile)
{
//FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
MemoryStream fsCrypt = new MemoryStream();
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write);
int data;
while ((data = inputFile.ReadByte()) != -1)
cs.WriteByte((byte)data);
cs.FlushFinalBlock();
return fsCrypt;
}
I need to perform data hiding in encrypted image. To perform data hiding i need to have bitmap Image. But i don't know how to save the image as bitmap.
Below is my encryption code.
public void EncryptFile(string source, string destination)
{
string sKey = "super545";
FileStream fsInput = new FileStream(source, FileMode.Open, FileAccess.Read);
FileStream fsEncrypted = new FileStream(destination, FileMode.Create, FileAccess.Write);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
ICryptoTransform desencrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write);
byte[] bytearrayinput = new byte[fsInput.Length - 1];
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Close();
fsInput.Close();
fsEncrypted.Close();
}
This is called like:
EncryptFile(originalimage, output);
output is a string variable with the path to store the encrypted image.
How can I call the function to run the encryption?
I receive the error the parameter is invalid when i hit this line:
Bitmap bitmap3 = new Bitmap(output);
I guess what you are trying to do is pretty close to this:
public void EncryptFile(string source, string destination)
{
string sKey = "super545";
FileStream fsInput = new FileStream(source, FileMode.Open, FileAccess.Read);
FileStream fsEncrypted = new FileStream(destination, FileMode.Create, FileAccess.Write);
//Consider to use something else, DES is dead
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
//use some key derivation function like pbkdf2 instead
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
//should be random, may be fixed ONLY for testing purposes
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
ICryptoTransform desencrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write);
//byte[] bytearrayinput = new byte[fsInput.Length - 1]; // what do you need that big buffer for anyways?
//fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
//cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
byte[] headerBuffer = new byte[54]; // buffer for our bmp header ... without any color tables or masks
//No need for lots of checks in a proof of concept
fsInput.Read(headerBuffer, 0, headerBuffer.Length);
var biCompression = BitConverter.ToInt32(headerBuffer, 30); //get biComp from header
if (biCompression != 0 && biCompression != 3)
{
throw new Exception("Compression is not in the correct format");
}
//The buffer is copied without any encryption
fsEncrypted.Write(headerBuffer, 0, headerBuffer.Length);
//copy the rest and encrypt it ... don't care about color tables and masks for now
//and let's just hope plaintext and ciphertext have the right size
fsInput.CopyTo(cryptostream);
cryptostream.Close();
fsInput.Close();
fsEncrypted.Close();
}
I have used this code in the past for writing and reading xml files. This time I want to write some encrypted generated XML and then read it and process it internally. I will post the code and perhaps someone can spot the problem.
When I am testing the decrypt I have been able to output a file that has a continuous line of null character codes. The encrypted file seems to contain data and varys in size with different amounts of data.
Please help, thanks!
Encrypt
MemoryStream ms = new MemoryStream();
XmlTextWriter xmlwriter = new XmlTextWriter(ms,Encoding.UTF8);
FileStream EncryptedFileStream = new FileStream(file, FileMode.Create, FileAccess.Write);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes("AAAAAAAA");
DES.IV = ASCIIEncoding.ASCII.GetBytes("AAAAAAAA");
ICryptoTransform desEncrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(EncryptedFileStream, desEncrypt, CryptoStreamMode.Write);
/*create working and tested XML data here*/
byte[] bytearray = new byte[ms.Length];
ms.Read(bytearray, 0, bytearray.Length);
cryptostream.Write(bytearray, 0, bytearray.Length);
cryptostream.Close();
EncryptedFileStream.Close();
xmlwriter.Close();
ms.Flush();
ms.Close();
DECRYPT
MemoryStream ms = new MemoryStream();
StreamWriter swDecrypt = new StreamWriter(ms);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes("AAAAAAAA");
DES.IV = ASCIIEncoding.ASCII.GetBytes("AAAAAAAA");
ICryptoTransform desDecrypt = DES.CreateDecryptor();
FileStream fsDecrypt = new FileStream(mstrIndexFile, FileMode.Open, FileAccess.Read);
CryptoStream cryptostreamDecr = new CryptoStream(fsDecrypt, desDecrypt, CryptoStreamMode.Read);
swDecrypt.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
swDecrypt.Flush();
ms.Position = 0;
Using your current architecture, you need to use the MemoryStream that you just filled with data (not forgetting to reset its position to zero and to flush any pending writes)
//I am currently stuck on this point.
swDecrypt.Flush();
ms.Position=0;
XmlTextReader lxmlReader = new XmlTextReader(ms);
however, my feeling is that you don't need a MemoryStream here. Instead, just supply the CryptoStream to the XmlTextReader:
CryptoStream cryptostreamDecr = new CryptoStream(.....
XmlTextReader lxmlReader = new XmlTextReader(cryptostreamDecr);
After much trial and error I was pointed to XML element encryption using the first method on the page. This method was much easier and straight forward. If anyone decides to use this just make sure you use the same KEY and IV on your encryption and decryption if they are taking place in separate places.
Basically, a copy paste operation and you can encrypt your entire document by passing in the root element!
-Feelsgoodman
I'm writing an application to Encrtpt/Decrypt files and using DESCryptoServiceProvider for this purpose. This seems to work for text files but when I use the same application for .xlsx file, the resulting encrypted file and the decrypted file get corrupted and I'm not able to open that any more. is there any way I can encryt / decrypt different kinds of file like .doc..xls etc.
update : added code for encryption/decryption
public static void EncryptFile(string filepath,string fileOutput, string key)
{
FileStream fsInput = new FileStream(filepath, FileMode.Open, FileAccess.Read);
FileStream fsEncrypted = new FileStream(fileOutput, FileMode.Create, FileAccess.Write);
DESCryptoServiceProvider DESc = new DESCryptoServiceProvider();
DESc.Key = ASCIIEncoding.ASCII.GetBytes(key);
DESc.IV = ASCIIEncoding.ASCII.GetBytes(key);
ICryptoTransform desEncrypt = DESc.CreateEncryptor();
CryptoStream cryptoStream = new CryptoStream(fsEncrypted, desEncrypt, CryptoStreamMode.Write);
byte[] byteArrayInput = new byte[fsInput.Length - 1];
fsInput.Read(byteArrayInput, 0, byteArrayInput.Length);
cryptoStream.Write(byteArrayInput, 0, byteArrayInput.Length);
cryptoStream.Close();
fsInput.Close();
fsEncrypted.Close();
}
public static void DecryptFile(string filepath, string fileOutput, string key)
{
DESCryptoServiceProvider DESc = new DESCryptoServiceProvider();
DESc.Key = ASCIIEncoding.ASCII.GetBytes(key);
DESc.IV = ASCIIEncoding.ASCII.GetBytes(key);
FileStream fsread = new FileStream(filepath, FileMode.Open, FileAccess.Read);
ICryptoTransform desDecrypt = DESc.CreateDecryptor();
CryptoStream cryptoStreamDcr = new CryptoStream(fsread, desDecrypt, CryptoStreamMode.Read);
StreamWriter fsDecrypted = new StreamWriter(fileOutput);
fsDecrypted.Write(new StreamReader(cryptoStreamDcr).ReadToEnd());
fsDecrypted.Flush();
fsDecrypted.Close();
}
static void Main(string[] args)
{
EncryptFile(#"C:\test1.xlsx", #"c:\test2.xlsx", "ABCDEFGH");
DecryptFile(#"C:\test2.xlsx", #"c:\test3.xlsx", "ABCDEFGH");
}
You are not encrypting or decrypting correctly. Encrypt -> Decrypt will always give a file identical to the input. If you post your code we may be able to assist in finding the bug in it.
You should use FileStreama as suggested in one of the comments by Kieren Johnstone. Also, you're not flushing the streams when encrypting - this may not be done automatically, so you should try and flush the streams, too.