I am downloading a binary file from a server and accessing it partially while the download progresses. I'd like to encrypt the file prior to uploading, and decrypt its content as my program receives it.
The file arrives in byte chunks of random size using the code below, so I think I need a method which acts on individual bytes, or at least a fixed number of bytes, and keeping the overall file size intact.
private void DownloadFile()
{
WebClient client = new WebClient();
Stream stream = client.OpenRead(address);
byte[] readBuffer = new byte[139043]; // File size known ahead of time
int totalBytesRead = 0;
int bytesRead;
int i = 0;
while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
File.WriteAllBytes("file_" + i + ".ext", readBuffer); // Save partially downloaded file
totalBytesRead += bytesRead;
i++;
}
}
Solution:
I opted for the simple XOR algorithm shown in my answer below. It works on individual bytes and considering I can generate a unique key for each file, I am comfortable with the level of protection.
You would want to look into AES CTR encryption. There is a related SO question: Can I use AES in CTR mode in .NET?
There is an answer there that points to an MSDN article about implementing that encryption in your applications: https://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aesmanaged%28v=vs.90%29.aspx#2
I have written the following XOR based algorithm which I believe sufficient for my purposes.
The password and salt can be derived from miscellaneous information about the file (eg. its size or unique file name) and as a consequence, no key will be ever used more than once.
All criticism and hate is welcome.
public void Crypt(byte[] data, string filepath)
{
// Define password and salt
byte[] pwd = GetBytes("PASSWORD");
byte[] salt = GetBytes("SALT");
// Generate PasswordDeriveBytes from the password and salt.
PasswordDeriveBytes pdb = new PasswordDeriveBytes(pwd, salt);
// Generate key from PasswordDeriveBytes
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, tdes.IV);
// Encrypt/Decrypt
for(int i = 0; i < data.Length; i++)
{
data[i] = (byte)(data[i] ^ tdes.Key[i % tdes.Key.Length]);
}
// Save File
File.WriteAllBytes(filepath, data);
}
Related
I was working a while ago with AHK (Autohotkey) and I found the crypt.ahk is handy in encrypting/decrypting files. I also decided to use an AES256 with SHA512 and a "Password".
Here you can see a short code from the crypto.ahk file:
if !dllCall("Advapi32\CryptAcquireContextW"
,"Ptr*",hCryptProv ; pointer to a handle of a CSP
,"Uint",0 ; key container name
,"Uint",0 ; 0 = default provider
,"Uint",c.PROV_RSA_AES ; AES type of provider to acquire => 24
,"UInt",c.CRYPT_VERIFYCONTEXT) ; 0xF0000000 <= This option is intended for applications that are using ephemeral keys, or applications that do not require access to persisted private keys, such as applications that perform only hashing, encryption, and digital signature verification.
if !dllCall("Advapi32\CryptCreateHash"
,"Ptr",hCryptProv ; handle to a CSP created by a call to CryptAcquireContext
,"Uint",CUR_PWD_HASH_ALG ; HASH algorithm to use ALG_ID: HashAlg==6 || HashAlg = "SHA512" ?c.CALG_SHA_512
; CALG_SHA_512 := (CryptConst.ALG_CLASS_HASH | CryptConst.ALG_TYPE_ANY | CryptConst.ALG_SID_SHA_512)
; 4<<13 = hex 8000 | 0 | 14 dec => 0x800E = 512 bit sha hashing algorithm
,"Uint",0 ; MAC / HMAC --> For nonkeyed algorithms, this parameter must be set to zero.
,"Uint",0 ; this flag is not used
,"Ptr*",hHash ) ; The address to which the function copies a handle to the new hash object.
;hashing password
passLen := StrPutVar(password, passBuf,0,this.PassEncoding)
if !dllCall("Advapi32\CryptHashData"
,"Ptr",hHash ; Handle of the hash object.
,"Ptr",&passBuf ; "thisIsMyPassword" in Unicode - pointer to a buffer that contains the data to be added to the hash object.
,"Uint",passLen ; 34 - Number of bytes of data to be added.
,"Uint",0 ) ; no flags are used
;getting encryption key from password
if !dllCall("Advapi32\CryptDeriveKey"
,"Ptr",hCryptProv ; A HCRYPTPROV handle of a CSP created by a call to CryptAcquireContext.
,"Uint",CUR_ENC_ALG ; An ALG_ID structure that identifies the symmetric encryption algorithm for which the key is to be generated
,"Ptr",hHash ; A handle to a hash object that has been fed the exact base data.
,"Uint",KEY_LENGHT ; 256 keylength - Specifies the type of key generated. lower 16 bit == 0 -> no flags set upper 16 bit => 256
,"Ptr*",hKey ) ; A pointer to a HCRYPTKEY variable to receive the address of the handle of the newly generated key.
if !dllCall("Advapi32\CryptGetKeyParam"
,"Ptr",hKey ; The handle of the key being queried.
,"Uint",c.KP_BLOCKLEN ; Specifies the type of query being made. For all key types, this parameter can contain one of the following values. If a session key is specified by the hKey parameter, retrieve the block length of the key cipher. The pbData parameter is a pointer to a DWORD value that receives the block length, in bits. For stream ciphers, this value is always zero.
,"Uint*",BlockLen ; A pointer to a buffer that receives the data. The form of this data depends on the value of dwParam.
,"Uint*",dwCount := 4 ; A pointer to a DWORD value that, on entry, contains the size, in bytes, of the buffer pointed to by the pbData parameter. When the function returns, the DWORD value contains the number of bytes stored in the buffer.
,"Uint",0) ; nothing
{foo := "CryptGetKeyParam", err := GetLastError(), err2 := ErrorLevel
GoTO FINITA_LA_COMEDIA
}
if !dllCall(CryptEnc
,"Ptr",hKey ;key ; A handle to the encryption key.
,"Ptr",0 ;hash ; If no hash is to be done, this parameter must be NULL.???
,"Uint",isFinal ;final ; A Boolean value that specifies whether this is the last section in a series being encrypted. Final is set to TRUE for the last or only block
,"Uint",0 ;dwFlags ; no flags
,"Ptr",&ReadBuf ;pbdata ; A pointer to a buffer that contains the plaintext to be encrypted. The plaintext in this buffer is overwritten with the ciphertext created by this function.
,"Uint*",BytesRead ;dwsize ; A pointer to a DWORD value that , on entry, contains the length, in bytes, of the plaintext in the pbData buffer. On exit, this DWORD contains the length, in bytes, of the ciphertext written to the pbData buffer.
,"Uint",ReadBufSize+BlockLen ) ;dwbuf ; Specifies the total size, in bytes, of the input pbData buffer. Note that, depending on the algorithm used, the encrypted text can be larger than the original plaintext. In this case, the pbData buffer needs to be large enough to contain the encrypted text and any padding.
I worked along with many Microsoft Docs and I could say, I have a little understanding. At least, the self-written comments (after the ;) seem to be okay.
create a handle for a CSP with AES
hash object gets the CALG_SHA_512
hash the Password "thisIsMyPassword"
put the hashing and aes together
crypt the file
Now I want to write a C# using System.Security.Cryptography; Programm.
But I can't manage to decrypt the file with AES or RijndaelManaged, because I can't get the SHA512 into my AES256. I always get an error stating that the Size is too big.
I tried it with .Net5.0 and .Net4.8 but no luck.
public static void FileDecrypt(string inputFile, string outputFile, string password)
{
// https://foxlearn.com/windows-forms/how-to-encrypt-and-decrypt-files-using-aes-encryption-algorithm-in-csharp-396.html
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
byte[] aesKey = SHA512.Create().ComputeHash(passwordBytes);
byte[] aesIV = { 0 }; // iV is zero
Aes trueAES = Aes.Create();
trueAES.BlockSize = 256;
trueAES.KeySize = 512;
trueAES.Mode = CipherMode.CBC;
trueAES.Padding = PaddingMode.None;
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, trueAES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
{
int read;
byte[] buffer = new byte[10240]; // [1048576];
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
}
I already have found a solution on how to translate the advapi32.dll to C#, but the crypto stuff seems to be deprecated. See MS Site:
This API is deprecated. New and existing software should start using
Cryptography Next Generation APIs. Microsoft may remove this API in
future releases.
Could someone help me out with my code?
Update and solution - 05/09/22
Thanks to Richard I was able to decrypt in c# the previous encrypted advapi32.dll file. My code is now:
public static void FileDecrypt(string inputFile, string outputFile, string password)
{
// https://foxlearn.com/windows-forms/how-to-encrypt-and-decrypt-files-using-aes-encryption-algorithm-in-csharp-396.html
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
byte[] aesKey = SHA512.Create().ComputeHash(passwordBytes);
// shorten the 512 bit (64 byte) to 256bit (32 byte)
// just take the first 32 byte of the sha512 key
byte[] shortingAesKey = aesKey.Take(32).ToArray();
trueAES.Key = shortingAesKey;
byte[] aesIV = { 0 }; // iV is zero
Aes trueAES = Aes.Create();
trueAES.BlockSize = 256;
trueAES.KeySize = 512;
trueAES.Mode = CipherMode.CBC;
trueAES.Padding = PaddingMode.None;
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, trueAES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
{
int read;
byte[] buffer = new byte[10240]; // [1048576];
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
}
I am working in a C# application. We have common methods to store data on a file. These methods encrypt the data and store them on the file system. when we need the data, ReadData method decrypts the data and returns me plain text.
This code works fine in normal cases if size of the text in small. but for a example text given below, the decryption code is throwing exception - length of the data to decrypt is invalid.
The exception occurs at line
// close the CryptoStream
x_cryptostream.Close();
I tried different ways but no luck. Can some pls help.
Why am I encrypting already encrypted data - I am just trying to store in a file using common method of the huge application. The common methods storedata(key,data) nad readdata(key) do the encryption/decryption I can't avoid.
public static byte[] Decrypt(byte[] ciphertext, string Key, string IV)
{
byte[] k = Encoding.Default.GetBytes(Key);
byte[] iv = Encoding.Default.GetBytes(IV);
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
x_alg.Padding = PaddingMode.PKCS7;
// create an ICryptoTransform that can be used to decrypt data
ICryptoTransform x_decryptor = x_alg.CreateDecryptor(k, iv);
// create the memory stream
MemoryStream x_memory_stream = new MemoryStream();
// create the CryptoStream that ties together the MemoryStream and the
// ICryptostream
CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
x_decryptor, CryptoStreamMode.Write);
// write the ciphertext out to the cryptostream
x_cryptostream.Write(ciphertext, 0, ciphertext.Length);
// close the CryptoStream
x_cryptostream.Close();
// get the plaintext from the MemoryStream
byte[] x_plaintext = x_memory_stream.ToArray();
Below is the code of encrypt method.
public static byte[] Encrypt(string strplain, string Key, string IV)
{
byte[] k = Encoding.Default.GetBytes(Key);
byte[] iv = Encoding.Default.GetBytes(IV);
byte[] plaintext = Encoding.Default.GetBytes(strplain);
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
x_alg.Padding = PaddingMode.PKCS7;
// create an ICryptoTransform that can be used to encrypt data
ICryptoTransform x_encryptor = x_alg.CreateEncryptor(k, iv);
// create the memory stream
MemoryStream x_memory_stream = new MemoryStream();
// create the CryptoStream that ties together the MemoryStream and
// the ICryptostream
CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
x_encryptor, CryptoStreamMode.Write);
// write the plaintext out to the cryptostream
x_cryptostream.Write(plaintext, 0, plaintext.Length);
// close the CryptoStream
x_cryptostream.Close();
// get the ciphertext from the MemoryStream
byte[] x_ciphertext = x_memory_stream.ToArray();
// close memory stream
x_memory_stream.Close();
// convert from array to string
string cipher_Tx = Encoding.Default.GetString(x_ciphertext,
0, x_ciphertext.Length);
x_encryptor.Dispose();
x_alg.Clear();
byte[] cipher = Encoding.Default.GetBytes(cipher_Tx);
return cipher;
}
Your problem is string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 0, x_ciphertext.Length);.
x_ciphertext is not a valid byte representation of text, it has many unpresentable characters and when you do your byte[] to string conversion you are losing information. The correct way to do it is use a string format that is designed to represent binary data using something like Convert.ToBase64String(byte[]) and Convert.FromBase64String(string).
string cipher_Tx = Convert.ToBase64String(x_ciphertext)
x_encryptor.Dispose();
x_alg.Clear();
byte[] cipher = Convert.FromBase64String(cipher_Tx)
That being said, there is a lot of other "odd" things about your code, for example you don't use using statements and you really should. Also that whole conversion to string and back is totally unnecessary, just return x_ciphertext. There may be other problems with the code too (like where did the strings for Key and IV come from) and many other best practices (like you should be generating a random IV and writing it out in to the output and the key should be generated using a key derivation function not straight from user text), but I stopped checking after I found the string conversion issue.
Your code above works as long as the key and iv used to decrypt match the key and iv used to encrypt. Try this:
byte[] test = new byte[1000000];
for (int i = 0; i < 256; i++)
{
test[i] = (byte)i;
}
var ciphertext = Encrypt(Encoding.Default.GetString(test), "0000000000000000", "0000000000000000");
byte[] check = Decrypt(ciphertext, "0000000000000000", "0000000000000000");
for (int i = 0; i < 256; i++)
{
Debug.Assert(check[i] == (byte)i, "round trip");
}
As you can see, one million bytes encrypt and decrypt just fine with your code, so I don't think it has anything to do with data size.
However, change the IV like this:
byte[] check = Decrypt(ciphertext, "0000000000000000", "000000000000000X"); // note X
and the Debug.Assert will fire -- the decryption will not match. However, x_cryptostream.Close() succeeds.
Next, try changing the key like this:
byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X
Now, x_cryptostream.Close() will fail with a CryptographicException, probably, "Padding is invalid and cannot be removed."
Corrupting the key will cause the decryption to fail, and x_cryptostream.Close() to fail.
I think the problem is in your saving and later restoring the key bytes.
BTW: Hopefully you are using the full binary range of the key, and not basing it only on ASCII characters, otherwise you don't really have a strong key.
I've been reading this article from MSDN on Rfc2898DeriveBytes. Here is the sample encryption code they provide.
string pwd1 = passwordargs[0];
// Create a byte array to hold the random value.
byte[] salt1 = new byte[8];
using (RNGCryptoServiceProvider rngCsp = ne RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
//data1 can be a string or contents of a file.
string data1 = "Some test data";
//The default iteration count is 1000 so the two methods use the same iteration count.
int myIterations = 1000;
try
{
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1,salt1,myIterations);
Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1);
// Encrypt the data.
TripleDES encAlg = TripleDES.Create();
encAlg.Key = k1.GetBytes(16);
MemoryStream encryptionStream = new MemoryStream();
CryptoStream encrypt = newCryptoStream(encryptionStream, encAlg.CreateEncryptor(), CryptoStreamMode.Write);
byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(data1);
encrypt.Write(utfD1, 0, utfD1.Length);
encrypt.FlushFinalBlock();
encrypt.Close();
byte[] edata1 = encryptionStream.ToArray();
k1.Reset();
My question is, how would I properly Read/Write the hashed data to/from a text file?
My main goal is to do what this developer is doing. I need to store a password locally. When my application prompts the user for the password, the user will enter the password, then my application will read from the text file and verify if the password that the user entered is indeed correct. How would I go about doing it?
You typically store the hash of the password, then when user enters password, you compute hash over the entered password and compare it with the hash which was stored - that said, just hashing is usually not enough (from security point of view) and you should use a function such as PKBDF2 (Password-Based Key Derivation Function 2) instead. Here is article covering all that information in more elaborate way as well as sample code (bottom of the page): http://www.codeproject.com/Articles/704865/Salted-Password-Hashing-Doing-it-Right
Here is a link to codereview, which I guess refers to the same implementation as above article.
How to properly store password locally
Just don't do it. No really don't do it.
...But if you really really have to, never just implement it yourself. I would recommend reviewing how ASP.NET Identity hashes passwords. Version 3 is pretty rock solid at the moment:
note that the following is taken from github.com and may be changed at any time. For the latest, please refer to the previous link.
private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
{
// Produce a version 3 (see comment above) text hash.
byte[] salt = new byte[saltSize];
rng.GetBytes(salt);
byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
var outputBytes = new byte[13 + salt.Length + subkey.Length];
outputBytes[0] = 0x01; // format marker
WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
return outputBytes;
}
You should store the password as a one-way hash and the salt used to create that password. This way you are absolutely sure that the password for the user can never be DECRYPTED. Never use any two-way encryption for this particular task, as you risk exposing user information to would-be attackers.
void Main()
{
string phrase, salt, result;
phrase = "test";
result = Sha256Hash(phrase, out salt);
Sha256Compare(phrase, result, salt);
}
public string Sha256Hash(string phrase, out string salt)
{
salt = Create256BitSalt();
string saltAndPwd = String.Concat(phrase, salt);
Encoding encoder = Encoding.Default;
SHA256Managed sha256hasher = new SHA256Managed();
byte[] hashedDataBytes = sha256hasher.ComputeHash(encoder.GetBytes(saltAndPwd));
string hashedPwd = Encoding.Default.GetString(hashedDataBytes);
return hashedPwd;
}
public bool Sha256Compare(string phrase, string hash, string salt)
{
string saltAndPwd = String.Concat(phrase, salt);
Encoding encoder = Encoding.Default;
SHA256Managed sha256hasher = new SHA256Managed();
byte[] hashedDataBytes = sha256hasher.ComputeHash(encoder.GetBytes(saltAndPwd));
string hashedPwd = Encoding.Default.GetString(hashedDataBytes);
return string.Compare(hash, hashedPwd, false) == 0;
}
public string Create256BitSalt()
{
int _saltSize = 32;
byte[] ba = new byte[_saltSize];
RNGCryptoServiceProvider.Create().GetBytes(ba);
return Encoding.Default.GetString(ba);
}
You could also figure out another method for obtaining the salt, but I have made mine to that it computes 2048 bits worth of random data. You could just use a random long you generate but that would be a lot less secure. You won't be able to use SecureString because SecureString isn't Serializable. Which the whole point of DPAPI. There are ways to get the data out but you end up having to jump a few hurdles to do it.
FWIW, PBKDF2 (Password-Based Key Derivation Function 2) is basically the same thing as SHA256 except slower (a good thing). On its own both are very secure. If you combined PBKDF2 with an SHA256 as your salt then you'd have a very secure system.
I have searched online but have not been able to find any solutions to my problem.
I am using previously written methods to encrypt and ecrypt text using the Rijndael class.
I use these functions to encrypt and decrypt usernames and emails for a web application I have been working on.
The encryption/decryption works perfectly, but every once in a while I get this error:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.
Currently, I am getting this error with a specific email address and I can't reproduce the error even if I replace some of the letters in the email.
Here are the encryption/decrytpion functions. The IV and Key are defined as read only strings.
static public string Encrypting(string Source)
{
byte[] bytIn = System.Text.ASCIIEncoding.ASCII.GetBytes(Source);
// create a MemoryStream so that the process can be done without I/O files
System.IO.MemoryStream ms = new System.IO.MemoryStream();
byte[] IVBytes = Encoding.ASCII.GetBytes(IV);
byte[] KEYBytes = Encoding.ASCII.GetBytes(KEY);
Rijndael rijndael = Rijndael.Create();
rijndael.IV = IVBytes;
rijndael.Key = KEYBytes;
// create Crypto Stream that transforms a stream using the encryption
CryptoStream cs = new CryptoStream(ms, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
// write out encrypted content into MemoryStream
cs.Write(bytIn, 0, bytIn.Length);
cs.FlushFinalBlock();
// get the output and trim the '\0' bytes
byte[] bytOut = ms.GetBuffer();
int i = 0;
for (i = 0; i < bytOut.Length; i++)
if (bytOut[i] == 0)
break;
// convert into Base64 so that the result can be used in xml
return System.Convert.ToBase64String(bytOut, 0, i);
}
static public string Decrypting(string Source)
{
// convert from Base64 to binary
byte[] bytIn = System.Convert.FromBase64String(Source);
// create a MemoryStream with the input
System.IO.MemoryStream ms = new System.IO.MemoryStream(bytIn, 0, bytIn.Length);
byte[] IVBytes = Encoding.ASCII.GetBytes(IV);
byte[] KEYBytes = Encoding.ASCII.GetBytes(KEY);
Rijndael rijndael = Rijndael.Create();
rijndael.IV = IVBytes;
rijndael.Key = KEYBytes;
// create Crypto Stream that transforms a stream using the decryption
CryptoStream cs = new CryptoStream(ms, rijndael.CreateDecryptor(), CryptoStreamMode.Read);
// read out the result from the Crypto Stream
System.IO.StreamReader sr = new System.IO.StreamReader(cs);
return sr.ReadToEnd();
}
FYI - I am very new to cryptography and security.
Can these functions be fixed to avoid special cases that cause the error, or should I scrap these and use the RijndaelManaged class?
Sites I found that use RijndaelManaged:
SeeSharp
TekEye
The issue is almost certainly nothing to do with Rijndael vs. RijndaelManaged (or any other such implementation), but instead because the encrypted data contains a 0x00, and you are incorrectly assuming that the the ciphertext ends at the first 0x00 byte. Since the ciphertext can legitimately contain any byte value you should instead use the stream's Length property to determine the length of the ciphertext.
Eliminate the section you've commented: "get the output and trim the '\0' bytes" and replace the return ... statement with:
return System.Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length);
It should be noted that there are many other issues with your use of cryptography here, e.g. the use of a key generated directly from the ASCII encoding of a string, and the fact you're using a fixed IV both negatively impact security.
The norm for the error is a padding issue. What version of .NET are you using? It is more common to use the AES classes (AES, or Advanced Encryption Standard, which is Rijndael). There are plenty of AES implementations you can find as samples.
If you need some proof AES is Rijndael: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
I use AES encryption. It's ok when I encrypt and then decrypt whole file. I want to add multiple files to one encrypted. That's where the problem is. Encryption is fine, but decryption causes CryptographicException - bad data length. Is it even possible to decrypt part of file or is it encrypted as whole ? I used one cryptostream and passed there all files I want to encrypt to single file. I am trying to do opposite:
AesManaged aes = AES.InitAes(key, salt);
ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
int defChunkSize = 1024 * 1024 * 50;
using (FileStream source = new FileStream(header.data.filename, FileMode.Open))
{
foreach (CryptHeader.fileStruct file in header.data.files)
{
preparePath(file.filename);
using (FileStream target = new FileStream(file.filename, FileMode.Create))
{
using (CryptoStream cryptoStream = new CryptoStream(target, transform, CryptoStreamMode.Write))
{
long padding = source.Length - header.data.files.Sum(x => x.length);//Just test
int chunkSize = (defChunkSize > (int)file.length) ? (int)file.length : defChunkSize;
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
int totalRead = 0;
while (totalRead < file.length)
{
bytesRead = source.Read(chunkData, 0, chunkSize);
if (bytesRead <= 0) break;
totalRead += bytesRead;
cryptoStream.Write(chunkData, 0, bytesRead);
}
chunkData = null;
}
}
}
}
I've done the same few years ago without any problem. The logic I used is the following:
Encryption
define number of files
define array for keeping encrypted sizes
open output stream
seek (forced) to (number of files * 4) + 4 (assuming lengths are integers)
loop for encryption (encrypt- write encrypted data -assigned encrypted size)
seek to 0 (begin)
write number of files
write encrypted size array
close output stream
Decryption
open input stream
read number of files
define-read-fill array with encrypted sizes
loop for decryption (read using known sizes)
close output stream
I hope that this helps.
Short form: "you can't get there from here", it's impossible.
If you look at the description of how AES works you'll see two things.
1 AES uses a block size of 128 bits, so if your files aren't multiples of 8 bytes in length the blocks in the appended part won't line up,
2: AES use uses different keys for each block according to the rijndael key schedule, this is a likely going to be a deal breaker.
If you need to be able to concatenate encrypted files either wrap them so that the joins are visible and the fragments can be individually decrypted (gzip does this when compressing) or use a fixed substitution cypher like rot13
It can be done and it's working. I make table during encryption containing lengths of encrypted files. Than I decrypt exact parts (with padding).