Encrypt and decrypt a file with aes in c#? - c#

I wanted to know if there is a code that encrypts and decrypts a file using AES in C#?I have seen some code about encrypting and decrypting a text in c# using aes but
encrypting and decrypting a file in c# ..there was no full code to understand it well..If somebody can help me please?

In general, you don't want to encrypt a file. That is, you don't want to write a file, then encrypt it. The data is probably in a different sector of the storage device, and can likely be recovered. (Of course, if you're trying to write ransomware, by all means write it poorly). What you want to do instead is encrypt contents before they make it to disk.
What you asked for
public static void EncryptFile(string filePath, byte[] key)
{
string tempFileName = Path.GetTempFileName();
using (SymmetricAlgorithm cipher = Aes.Create())
using (FileStream fileStream = File.OpenRead(filePath))
using (FileStream tempFile = File.Create(tempFileName))
{
cipher.Key = key;
// aes.IV will be automatically populated with a secure random value
byte[] iv = cipher.IV;
// Write a marker header so we can identify how to read this file in the future
tempFile.WriteByte(69);
tempFile.WriteByte(74);
tempFile.WriteByte(66);
tempFile.WriteByte(65);
tempFile.WriteByte(69);
tempFile.WriteByte(83);
tempFile.Write(iv, 0, iv.Length);
using (var cryptoStream =
new CryptoStream(tempFile, cipher.CreateEncryptor(), CryptoStreamMode.Write))
{
fileStream.CopyTo(cryptoStream);
}
}
File.Delete(filePath);
File.Move(tempFileName, filePath);
}
public static void DecryptFile(string filePath, byte[] key)
{
string tempFileName = Path.GetTempFileName();
using (SymmetricAlgorithm cipher = Aes.Create())
using (FileStream fileStream = File.OpenRead(filePath))
using (FileStream tempFile = File.Create(tempFileName))
{
cipher.Key = key;
byte[] iv = new byte[cipher.BlockSize / 8];
byte[] headerBytes = new byte[6];
int remain = headerBytes.Length;
while (remain != 0)
{
int read = fileStream.Read(headerBytes, headerBytes.Length - remain, remain);
if (read == 0)
{
throw new EndOfStreamException();
}
remain -= read;
}
if (headerBytes[0] != 69 ||
headerBytes[1] != 74 ||
headerBytes[2] != 66 ||
headerBytes[3] != 65 ||
headerBytes[4] != 69 ||
headerBytes[5] != 83)
{
throw new InvalidOperationException();
}
remain = iv.Length;
while (remain != 0)
{
int read = fileStream.Read(iv, iv.Length - remain, remain);
if (read == 0)
{
throw new EndOfStreamException();
}
remain -= read;
}
cipher.IV = iv;
using (var cryptoStream =
new CryptoStream(tempFile, cipher.CreateDecryptor(), CryptoStreamMode.Write))
{
fileStream.CopyTo(cryptoStream);
}
}
File.Delete(filePath);
File.Move(tempFileName, filePath);
}
What you really want
Instead of writing the original file via a FileStream, open the file, write the header and IV, create the CryptoStream, and use the CryptoStream for everything. There's no reason to ever let the unencrypted form be on disk.

Related

Parallel + Stream + Encryption ... c# supposed to work

Something very weird occur in my code.
If i create a simple for without parallel my code working fine.
But when I add Parallel.For it's no more working.
The problem is when it's rebuild the binary are not the same with the Parallel For but it's the same with the normal For ...
I create a sample for the example.
Essentially, the code only read a file, save it in chunck.
After "Simulate upload", just rebuild the file with the same Key and IV.
Like i said in the normal For everything working fine.
// Decryption
public static byte[] DecryptBinaryFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream())
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
{
csDecrypt.Write(cipherText, 0, cipherText.Length);
csDecrypt.Close();
}
return msDecrypt.ToArray();
}
}
}
// Encryption
public static byte[] EncryptBinary(byte[] binary, byte[] Key, byte[] IV)
{
// Check arguments.
if (binary == null || binary.Length <= 0)
throw new ArgumentNullException("Binary");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(binary);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
// Rebuild the file
static void RebuildFile(string directory, string file, int totalParts)
{
string finalName = string.Concat(directory, file);
using (FileStream fs = new FileStream(finalName, FileMode.Append))
{
for (int i = 0; i < totalParts; i++)
{
string fileName = $"{i.ToString("00000")}-{file}";
string path = Path.Combine(directory, fileName);
byte[] binary = DecryptBinaryFromBytes(File.ReadAllBytes(path), key, iv);
fs.Write(binary, 0, binary.Length);
}
fs.Close();
}
}
// Simulate upload
static void SimulateUpload(string filePath, byte[] binary)
{
binary = EncryptBinary(binary,key, iv);
using (FileStream fs = new FileStream(filePath, FileMode.Append))
{
fs.Write(binary, 0, binary.Length);
}
}
// variable declaration
static byte[] key;
static byte[] iv;
// The main method
static void Main(string[] args)
{
key = Guid.NewGuid().ToByteArray().Concat(Guid.NewGuid().ToByteArray()).ToArray();
iv = Guid.NewGuid().ToByteArray();
string inputFile = #"C:\temp\Fonctionnement-FR.docx";
string directory = #"C:\temp\uploads\";
const long BUFFER_SIZE = 524288;
byte[] buffer = new byte[BUFFER_SIZE];
FileInfo fInfo = new FileInfo(inputFile);
long totalBytes = fInfo.Length;
double estimateTotalChuck = Math.Ceiling((totalBytes / BUFFER_SIZE) + 0.5);
int totalParts = (int)estimateTotalChuck;
string fileName = Path.GetFileName(inputFile);
string fileExtension = Path.GetExtension(inputFile);
// This one is Working
for (int idx=0;idx<totalParts; idx++)
{
using (Stream input = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
{
using (MemoryStream output = new MemoryStream())
{
long startPosition = (idx * BUFFER_SIZE);
long maxPosition = startPosition + BUFFER_SIZE;
int maxBufferRead = (int)BUFFER_SIZE;
input.Seek(startPosition, SeekOrigin.Begin);
if (maxPosition > totalBytes)
{
maxBufferRead = (int)(totalBytes - startPosition);
}
input.Read(buffer, 0, maxBufferRead);
output.Write(buffer, 0, maxBufferRead);
SimulateUpload(string.Concat(directory, $"{idx.ToString("00000")}-{fileName}"), output.ToArray());
// upFile = UploadSingleFile(endPoint, new FileArgs(output.ToArray(), fileName, fileExtension, idx, totalParts), handShake, chunkSize, idx, totalParts, fileIdentifier);
}
}
}
// This one is not working
Parallel.For(0, (int)estimateTotalChuck, new ParallelOptions { MaxDegreeOfParallelism = 5 }, idx =>
{
using (Stream input = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
{
using (MemoryStream output = new MemoryStream())
{
long startPosition = (idx * BUFFER_SIZE);
long maxPosition = startPosition + BUFFER_SIZE;
int maxBufferRead = (int)BUFFER_SIZE;
input.Seek(startPosition, SeekOrigin.Begin);
if (maxPosition > totalBytes)
{
maxBufferRead = (int)(totalBytes - startPosition);
}
input.Read(buffer, 0, maxBufferRead);
output.Write(buffer, 0, maxBufferRead);
SimulateUpload(string.Concat(directory, $"{idx.ToString("00000")}-{fileName}"), output.ToArray());
// upFile = UploadSingleFile(endPoint, new FileArgs(output.ToArray(), fileName, fileExtension, idx, totalParts), handShake, chunkSize, idx, totalParts, fileIdentifier);
}
}
});
RebuildFile(directory, fileName, totalParts);
}
When you switch to parallel code, you need to be acutely aware of where all locals are declared; anything declared outside the parallel region will be shared by all the parallel threads. In this case, it looks like buffer is a key contender, and since that is the primary data exchange mechanism, it would be entirely expected that if multiple threads are reading and writing to buffer concurrently, that they're all virtually guaranteed to trip over each-other.
But: check all the other locals too! If in doubt: move the code that runs in parallel to a separate method. It is hard to trip over shared locals in that scenario. Note that for ref-type parameters (such as byte[]), you'd also need to ensure that the objects are separate - separate locals to the same object has the same problem.

The Length of Data is invalid while decrypting a txt file

I made a console app to decrypt a file . Please refer to the screenshot for detail error message.
If you copy paste the code in a console app . You should be able to debug .
1) Need to make folder SourceFolder\Newfolder\ and a new txt file destination.txt
2) Need to make folder for C:\DestinationFolder\source.txt(encrypted file cannot attach file here i hope you can make one
using System;
using System.IO;
using System.Security.Cryptography;
namespace ConsoleApp5
{
class Program
{
//SALT is a random data that is used as addition to a password to encrypt data
// The primary function is to protect against lists of often used password
private static readonly byte[] SALT = new byte[] {0x26,0xdc,0xff,0x76,0x76,
0xad,0xed,0x7a,0x64,0xc5,0xfe
,0x20,0xaf,0x4d,0x08,0x3c};
static void Main(string[] args)
{
//Need to make a SourceFolder and but the file in there abc.txt
string path = #"C:\SourceFolder\New folder\";
string[] txtFile = Directory.GetFiles(path, "*.txt");
string fileinnn = txtFile[0];
//Get the filepath of the decrypted file. Need to make this folder and add a new file that is what i am donig for now for this cosole app
string filepathDecrypted = #"C:\DestinationFolder\destination.txt"; //this is a new file need to create
//Get the password
string encrytionKey = "password.";
Decrypt(fileinnn, filepathDecrypted, encrytionKey);
}
public static void Decrypt(string fileIn, string fileOut, string Password)
{
//open filestreat for encrypted source file
using (System.IO.FileStream fsIn = new FileStream(fileIn, FileMode.Open, FileAccess.Read))
{
using (System.IO.FileStream fsOut = new FileStream(fileOut, FileMode.OpenOrCreate, FileAccess.Write))
{
try
{
//Create Key and IV from the password with the SALT
Rfc2898DeriveBytes pdf = new Rfc2898DeriveBytes(Password, SALT);
//Create a symmetric algorithm with Rijndael
Rijndael alg = Rijndael.Create();
alg.Padding = PaddingMode.PKCS7;
//alg.BlockSize = 128;
//SET key and IV
alg.Key = pdf.GetBytes(32);
alg.IV = pdf.GetBytes(16);
//Create a cryptoStream
using (CryptoStream cs = new CryptoStream(fsOut, alg.CreateDecryptor(), CryptoStreamMode.Write))
{
//Intialize the buffer and process the input in chunks
// this is done to avoid reading the whole file which is huge and memory consumption.
int bufferLen = 4096;
byte[] buffer = new byte[bufferLen];
int bytesRead;
do
{ //read a chunck of data from the input file
bytesRead = fsIn.Read(buffer, 0, bufferLen);
//Decrypt it
cs.Write(buffer, 0, bytesRead);
}
while (bytesRead != 0);
//close everything
cs.Close(); //this is where it throws exception
fsOut.Close();
fsIn.Close();
}
}
catch (Exception ex)
{
var error = ex.Message;
throw;
}
}
}
}
}
}
Error message that i am getting

Appending data to an encrypted file

I'd like to append data to an already encrypted file (AES, CBC-Mode, padding PKCS#7) using CryptoStream without reading and writing the whole file.
Example:
Old Content: "Hello world..."
New Content: "Hello world, with appended text"
Of course I would have to read individual blocks of data and append then to a already present block. In the above mentioned example I would have to read the number of bytes present in the first block (14 bytes) and append two bytes to the first block, then writing the rest of the appended text
"Hello world, wi"
"th appended text"
One problem I am facing is that I am unable to read the number of bytes in a data block. Is there a way to find out the number of bytes present (in the example, 14)?
Additionally I am stuck since the CryptoStreamMode only has members for Read and Write, but no Update.
Is there a way to accomplish my desired functionality using CryptoStream?
It is a little complex, but not too much. Note that this is for CBC mode + PKCS#7!
Three methods: WriteStringToFile will create a new file, AppendStringToFile will append to an already encrypted file (works as WriteStringToFile if the file is missing/empty), ReadStringFromFile will read from the file.
public static void WriteStringToFile(string fileName, string plainText, byte[] key, byte[] iv)
{
using (Rijndael algo = Rijndael.Create())
{
algo.Key = key;
algo.IV = iv;
algo.Mode = CipherMode.CBC;
algo.Padding = PaddingMode.PKCS7;
// Create the streams used for encryption.
using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write))
// Create an encryptor to perform the stream transform.
using (ICryptoTransform encryptor = algo.CreateEncryptor())
using (CryptoStream cs = new CryptoStream(file, encryptor, CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs))
{
// Write all data to the stream.
sw.Write(plainText);
}
}
}
public static void AppendStringToFile(string fileName, string plainText, byte[] key, byte[] iv)
{
using (Rijndael algo = Rijndael.Create())
{
algo.Key = key;
// The IV is set below
algo.Mode = CipherMode.CBC;
algo.Padding = PaddingMode.PKCS7;
// Create the streams used for encryption.
using (FileStream file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
byte[] previous = null;
int previousLength = 0;
long length = file.Length;
// No check is done that the file is correct!
if (length != 0)
{
// The IV length is equal to the block length
byte[] block = new byte[iv.Length];
if (length >= iv.Length * 2)
{
// At least 2 blocks, take the penultimate block
// as the IV
file.Position = length - iv.Length * 2;
file.Read(block, 0, block.Length);
algo.IV = block;
}
else
{
// A single block present, use the IV given
file.Position = length - iv.Length;
algo.IV = iv;
}
// Read the last block
file.Read(block, 0, block.Length);
// And reposition at the beginning of the last block
file.Position = length - iv.Length;
// We use a MemoryStream because the CryptoStream
// will close the Stream at the end
using (var ms = new MemoryStream(block))
// Create a decrytor to perform the stream transform.
using (ICryptoTransform decryptor = algo.CreateDecryptor())
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
// Read all data from the stream. The decrypted last
// block can be long up to block length characters
// (so up to iv.Length) (this with AES + CBC)
previous = new byte[iv.Length];
previousLength = cs.Read(previous, 0, previous.Length);
}
}
else
{
// Use the IV given
algo.IV = iv;
}
// Create an encryptor to perform the stream transform.
using (ICryptoTransform encryptor = algo.CreateEncryptor())
using (CryptoStream cs = new CryptoStream(file, encryptor, CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs))
{
// Rewrite the last block, if present. We even skip
// the case of block present but empty
if (previousLength != 0)
{
cs.Write(previous, 0, previousLength);
}
// Write all data to the stream.
sw.Write(plainText);
}
}
}
}
public static string ReadStringFromFile(string fileName, byte[] key, byte[] iv)
{
string plainText;
using (Rijndael algo = Rijndael.Create())
{
algo.Key = key;
algo.IV = iv;
algo.Mode = CipherMode.CBC;
algo.Padding = PaddingMode.PKCS7;
// Create the streams used for decryption.
using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
// Create a decrytor to perform the stream transform.
using (ICryptoTransform decryptor = algo.CreateDecryptor())
using (CryptoStream cs = new CryptoStream(file, decryptor, CryptoStreamMode.Read))
using (StreamReader sr = new StreamReader(cs))
{
// Read all data from the stream.
plainText = sr.ReadToEnd();
}
}
return plainText;
}
Example of use:
var key = Encoding.UTF8.GetBytes("Simple key");
var iv = Encoding.UTF8.GetBytes("Simple IV");
Array.Resize(ref key, 128 / 8);
Array.Resize(ref iv, 128 / 8);
if (File.Exists("test.bin"))
{
File.Delete("test.bin");
}
for (int i = 0; i < 100; i++)
{
AppendStringToFile("test.bin", string.Format("{0},", i), key, iv);
}
string plainText = ReadStringFromFile("test.bin", key, iv);
How does the AppendStringToFile works? Three cases:
Empty file: as WriteStringToFile
File with a single block: the IV of that block is the IV passed as the parameter. The block is decrypted and then reencrypted together with the passed plainText
File with multiple blocks: the IV of the last block is the penultimate block. So the penultimate block is read, and is used as the IV (the IV passed as the parameter is ignored). The last block is decrypted and then reencrypted together with the passed plainText. To reencyrpt the last block, the IV used is the penultimate block.

How to create a encrypted zip file can be decompressed by winzip/7zip

I've used AESManaged Class to encrypt a zip file, but it couldn't be decompressed by winzip/7zip. I can only decompress it after decrypting in my code.
Below is the code I used to encrypt and decrypt. Anyone can help?
private static void EncryptFile(string input, string output, string pwd)
{
using (AesManaged aes = new AesManaged())
{
FileStream fsCrypt=null;
try
{
byte[] key = Encoding.UTF8.GetBytes(pwd);
fsCrypt = new FileStream(output, FileMode.Create);
using (CryptoStream cs = new CryptoStream(fsCrypt, aes.CreateEncryptor(key, key), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(input, FileMode.Open))
{
int data;
while ((data = fsIn.ReadByte()) != -1)
{
cs.WriteByte((byte)data);
}
aes.Clear();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
aes.Clear();
}
finally
{
if(fsCrypt!=null)
{
fsCrypt.Dispose();
}
}
}
}
private static void DecryptFile(string input, string output, string pwd)
{
using (AesManaged aes = new AesManaged())
{
FileStream fsCrypt = null;
try
{
byte[] key = Encoding.UTF8.GetBytes(pwd);
fsCrypt = new FileStream(input, FileMode.Open);
{
using (FileStream fsOut = new FileStream(output, FileMode.Create))
{
using (CryptoStream cs = new CryptoStream(fsCrypt, aes.CreateDecryptor(key, key), CryptoStreamMode.Read))
{
int data;
while ((data = cs.ReadByte()) != -1)
{
fsOut.WriteByte((byte)data);
}
aes.Clear();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
aes.Clear();
}
finally
{
if (fsCrypt != null)
{
fsCrypt.Dispose();
}
}
}
}
You are overwriting the internal structure of a zip file with your encryption algorithm.
How could you expect that an external zip manager recognizes your encrypted file as a valid zip file?
If you really want to use a password protected zip file then use a library that can do this for you without destroying the zip file structure.
I recommend this library DotNetZip
It is not possible to unzip a file if the format is not that of zip. Once you encrypt a file you the format is no longer zip.
But it is possible to perform both encryption and compression in C#. For better compression ratio you would need to compress the file first and then encrypt.
You can use GZipstream to compress and the code you have depicted to encrypt.

C# Byte[] Encryption

I have a Byte[] field that is a file contents that I need to encrypt. Nothing special or fancy, just enough to make sure the next person who gets it won't be able to easily decode it without some effort. I would use the encryption that comes with .Net Framework 4.0 but I definitely do not need to make the file any bigger than it is.
I thought about just simply reversing the array or adding a few bytes to the end...?
If I can avoid making the array to much bigger that would be great.
Any suggestions?
Thanks!
Does the addition of 1-16 bytes hurt? AES will pad by default using the below method:
private static void EncryptThenDecrypt(byte[] msg)
{
byte[] message = msg; // fill with your bytes
if (message is null)
{
return;
}
byte[] encMessage; // the encrypted bytes
byte[] decMessage; // the decrypted bytes - s/b same as message
byte[] key;
byte[] iv;
using (SymmetricAlgorithm aes = Aes.Create())
{
if (aes is null)
{
iv = key = null;
encMessage = Array.Empty<byte>();
}
else
{
aes.GenerateKey();
aes.GenerateIV();
key = aes.Key;
iv = aes.IV;
encMessage = EncryptBytes(aes, message);
}
}
using (SymmetricAlgorithm aes = Aes.Create())
{
if (aes is null || key is null)
{
decMessage = Array.Empty<byte>();
}
else
{
aes.Key = key;
aes.IV = iv;
decMessage = DecryptBytes(aes, encMessage);
}
}
Debug.Assert(message.SequenceEqual(decMessage), "Decrypted bytes do not match original bytes.");
}
private static byte[] EncryptBytes(SymmetricAlgorithm alg, byte[] message)
{
if (message is null)
{
#pragma warning disable S1168 // Empty arrays and collections should be returned instead of null
return null;
#pragma warning restore S1168 // Empty arrays and collections should be returned instead of null
}
if (message.Length == 0)
{
return message;
}
if (alg is null)
{
throw new ArgumentNullException(nameof(alg));
}
using (MemoryStream stream = new MemoryStream())
using (ICryptoTransform encryptor = alg.CreateEncryptor())
using (CryptoStream encrypt = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
{
encrypt.Write(message, 0, message.Length);
encrypt.FlushFinalBlock();
return stream.ToArray();
}
}
private static byte[] DecryptBytes(SymmetricAlgorithm alg, byte[] message)
{
if (message is null)
{
#pragma warning disable S1168 // Empty arrays and collections should be returned instead of null
return null;
#pragma warning restore S1168 // Empty arrays and collections should be returned instead of null
}
if (message.Length == 0)
{
return message;
}
if (alg is null)
{
throw new ArgumentNullException(nameof(alg));
}
using (MemoryStream stream = new MemoryStream())
using (ICryptoTransform decryptor = alg.CreateDecryptor())
using (CryptoStream encrypt = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
{
encrypt.Write(message, 0, message.Length);
encrypt.FlushFinalBlock();
return stream.ToArray();
}
}
Don't invent your own Encryption mechanism (i.e. Security by Obfuscation), use one of the classes provided by the framework.

Categories