I have a very big csv file which is encrypted using AES. The code that does the encryption
using var aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = initializationVector;
using var memoryStream = new MemoryStream();
var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.Flush();
This is later saved into a file. On the decryption end, I'm trying to decrypt it in chunks, e.g.
using var sourceStream = File.OpenRead(path_to_encrypted_file);
using var aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = iv;
using (var fs = File.Create(path_to_decrypted_file))
using (var cryptoStream = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Write)
{
var dataBuffer = new byte[81290];
int read;
while ((read = await sourceStream.ReadAsync(dataBuffer)) != 0)
{
ReadOnlyMemory<byte> buffer = dataBuffer.AsMemory().Slice(0, read);
await cryptoStream.WriteAsync(buffer);
await cryptoStream.FlushAsync();
}
}
File is decrypted, however, I see some random bytes and empty lines at the end of the file
Is there anything wrong with how I decrypt ?
There's a couple potential issues I'd investigate first, at least in the existing provided code. There may be more depending on how you're generating the initial data byte array, how you're generating your key, how you're writing the encrypted stream to disk, etc.
You're using ECB and you almost certainly shouldn't. It isn't doing anything with your IV, either. Consider CBC or GCM depending on the application. https://stackoverflow.com/a/22958889/13374279
You're not using a padding mode. Unless your data is exactly contained within the block size, there's a chance you're losing some data, which might be contributing to the gibberish at the end.
You don't show the original encrypting stream disposal, you just show the Flush(). Depending on its disposal, it is likely not calling the CryptoStream's FlushFinalBlock() method, which is important. Given the lack of the padding mode, if you add this in, you'll likely suddenly see yourself with an exception here to alert you that The input data is not a complete block. due to #2 until you swap that out.
Thanks to the answer by #Adam G I reimplemented encrypt/decrypt following suggestions in the answer + comments.
A little background – I needed a solution where encryption happens on the client machine (disconnected from the internet) & decryption later on takes place in the cloud once the encrypted file uploaded to a blob storage.
I wanted to have a hybrid encryption, where key is RSA encrypted, data - AES.
So the file contents on the client:
RSA encrypted key
RSA encrypted IV (RSA encryption of the IV is not necessary AFAIK)
AES encrypted data
This is the final implementation:
// Local
var localRsa = RSA.Create();
localRsa.ImportRSAPublicKey(
Convert.FromBase64String(public_key),
out var _);
var localAes = Aes.Create();
localAes.GenerateKey();
localAes.GenerateIV();
localAes.Mode = CipherMode.CBC;
localAes.Padding = PaddingMode.PKCS7;
using (var dataStream = File.OpenRead(file_to_encrypt))
using (var secretFileStream = File.Create(encrypted_file))
{
await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.Key, RSAEncryptionPadding.OaepSHA256));
await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.IV, RSAEncryptionPadding.OaepSHA256));
using (var cryptoStream = new CryptoStream(secretFileStream, localAes.CreateEncryptor(localAes.Key, localAes.IV), CryptoStreamMode.Write))
{
await dataStream.CopyToAsync(cryptoStream);
}
}
And the decryption piece:
// Cloud
var cloudRsa = RSA.Create();
cloudRsa.ImportRSAPrivateKey(
Convert.FromBase64String(private_key),
out var _);
var cloudAes = Aes.Create();
cloudAes.Mode = CipherMode.CBC;
cloudAes.Padding = PaddingMode.PKCS7;
using (var secretFileStream = File.OpenRead(encrypted_file))
{
var keyBuffer = new byte[256];
await secretFileStream.ReadAsync(keyBuffer, 0, keyBuffer.Length);
cloudAes.Key = cloudRsa.Decrypt(keyBuffer, RSAEncryptionPadding.OaepSHA256);
var ivBuffer = new byte[256];
await secretFileStream.ReadAsync(ivBuffer, 0, keyBuffer.Length);
cloudAes.IV = cloudRsa.Decrypt(ivBuffer, RSAEncryptionPadding.OaepSHA256);
secretFileStream.Position = 512;
using (var plainTextStream = File.Create(decrypted_file))
{
using (var cryptoStream = new CryptoStream(secretFileStream, cloudAes.CreateDecryptor(cloudAes.Key, cloudAes.IV), CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(plainTextStream);
}
}
}
Related
I'm trying to encrypt & decrypt some data using AES. But i'm only getting garbled output. What am I doing wrong?
static void Test()
{
byte[] myFileBytes; // Will contain encrypted data. First the IV, then the ciphertext.
var myPassword = "helloworld";
var dataToEncrypt = "this is a test";
// STEP 1: Encrypt some data:
byte[] key;
using (var sha256 = SHA256.Create())
key = sha256.ComputeHash(Encoding.UTF8.GetBytes(myPassword));
using (var myFileStream = new MemoryStream())
using (var aes = System.Security.Cryptography.Aes.Create())
{
aes.Key = key;
myFileStream.Write(aes.IV); // Use the default created by AES, which is presumably non-pseudo random
using (var cryptoStream = new CryptoStream(myFileStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(Encoding.UTF8.GetBytes(dataToEncrypt));
cryptoStream.Flush();
myFileBytes = myFileStream.ToArray(); // We are done!
} // Disposing CryptoStream disposes the underlying MemoryStream
}
// STEP 2: Decrypt it to verify that it works
using (var aes = System.Security.Cryptography.Aes.Create())
{
using (var myFileStream = new MemoryStream(myFileBytes))
{
var iv = new byte[aes.IV.Length];
myFileStream.Read(iv, 0, iv.Length);
using (var cryptoStream = new CryptoStream(myFileStream, aes.CreateEncryptor(key, iv), CryptoStreamMode.Read))
using (var copyStream = new MemoryStream())
{
cryptoStream.CopyTo(copyStream);
var decrypted = Encoding.UTF8.GetString(copyStream.ToArray());
Debug.Assert(dataToEncrypt == decrypted); // Fails!
}
}
}
}
I would take a look at the example in the documentation and compare to your code.
Notably when decrypting you are using aes.CreateEncryptor(key, iv). It should probably be aes.CreateDecryptor(key, iv).
The example from the docs also inputs the key and IV when calling CreateEncryptor, but I'm not sure if that is required or not.
You should probably not use sha256 to generate the key from a password. The correct way would be a key derivation algorithm. For example Rfc2898DeriveBytes
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.
Note:
The following code sample is for demonstration purposes only and implements an insecure scheme. If you are looking for a secure scheme have a look at https://stackoverflow.com/a/10177020/40347
I am using the AESCryptoServiceProvider class for testing some encryption concepts. So far in all the examples and articles out there they generate a random key to use for encryption and then immediately for decryption. Sure, it works fine because you are using the key right there, but if you encrypt, save the text and at a later time you want to decrypt it you will need the SAME key. And for that purpose also the same IV.
Now, in this code I am using the same key and IV on multiple passes, every time I run the batch that batch gives the same result (as expected). But then I close the test application and rerun the same code without change and the resulting (Base64-encoded) cypher text is different for the same input parameters, why?
I "saved" one of the B64-encoded cyphers from a previous run and fed it to the TestDecrypt method and as expected, it threw a cryptographic exception mentioning something about padding though I am sure it has to do with the fact that somehow for the same Key,IV, plain text and parameters it gives a different result on every separate run of the application.
For encrypting I have this:
public string Test(string password, Guid guid, string text)
{
const int SaltSize = 16;
string b64Cryptogram;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
Rfc2898DeriveBytes pwbytes = new Rfc2898DeriveBytes(password, SaltSize);
// Block 128-bits Key 128/192/256 bits (16/24/32 bytes)
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
//aes.IV = pwbytes.GetBytes(aes.BlockSize / 8);
aes.IV = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
aes.Key = guid.ToByteArray();
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(text);
}
b64Cryptogram = Convert.ToBase64String(msEncrypt.ToArray());
}
}
Console.WriteLine("E: {0}", b64Cryptogram);
aes.Clear();
}
return b64Cryptogram;
}
Notice I am not using the RFC2898DeriveBytes because it will randomly derive something I will no longer remember :) The idea of encrypting it is precisely that I KNOW what I used to encrypt it.
The decryption method looks like this:
public void TestDecrypt(string password, Guid guid, string ciphertextB64)
{
const int SaltSize = 16;
byte[] cipher = Convert.FromBase64String(ciphertextB64);
string plaintext;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
Rfc2898DeriveBytes pwbytes = new Rfc2898DeriveBytes(password, SaltSize);
// Block 128-bits Key 128/192/256 bits (16/24/32 bytes)
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
//aes.IV = pwbytes.GetBytes(aes.BlockSize / 8);
aes.IV = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
aes.Key = guid.ToByteArray();
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream msEncrypt = new MemoryStream(cipher))
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader swEncrypt = new StreamReader(csEncrypt))
{
plaintext = swEncrypt.ReadToEnd();
}
}
}
Console.WriteLine("D: {0}", plaintext);
aes.Clear();
}
}
Now, just put that in a console application and run it. Then exit and run it again and you will see that for the same Mode, Padding, IV, Key and plain text data, the output cipher will not be the same on every application run. They will be the same provided you run the method repeatedly in the same run of the application.
In case it was not obvious, here is the console code I used to test:
Guid guid = Guid.NewGuid();
string plain = "Text to be encrypted 123458970";
string password = "This is a test of the emergency broadcast system";
TestDecrypt(password, guid, Test(password, guid, plain));
TestDecrypt(password, guid, Test(password, guid, plain));
Test(password, guid, plain);
Test(password, guid, plain);
Test(plain, guid, password);
TestDecrypt(password, guid, "W4Oi0DrKnRpxFwtE0xVbYJwWgcA05/Alk6LrJ5XIPl8=");
}
The solution here is to pull in from a stored or constant Guid. Calling
Guid.NewGuid();
will return a different result every time. From the docs:
This is a convenient static method that you can call to get a new Guid. The method wraps a call to the Windows CoCreateGuid function. The returned Guid is guaranteed to not equal Guid.Empty.
Alternatively when testing you can use Guid.Empty which will return all zeroes.
Or, you can store it as such using its string constructor overload:
var guid = new Guid("0f8fad5b-d9cb-469f-a165-70867728950e");
I have written a process where a file is encrypted and uploaded to Azure, then the download process has to be decrypted which is what fails with a "Padding is invalid and cannot be removed" error, or a "Length of the data to decrypt is invalid." error.
I've tried numerous solutions online, including C# Decrypting mp3 file using RijndaelManaged and CryptoStream, but none of them seem to work and I end up just bouncing back and forth between these two errors. The encryption process uses the same key/IV pair that decryption uses, and since it will decrypt a portion of the stream I feel like that's working fine - it just ends up dying with the above errors.
Here is my code, any ideas? Please note that the three variants (cryptoStream.CopyTo(decryptedStream), do {} and while) aren't run together - they are here to show the options I've already tried, all of which fail.
byte[] encryptedBytes = null;
using (var encryptedStream = new MemoryStream())
{
//download from Azure
cloudBlockBlob.DownloadToStream(encryptedStream);
//reset positioning for reading it back out
encryptedStream.Position = 0;
encryptedBytes = encryptedStream.ConvertToByteArray();
}
//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
//stream where decrypted contents will be stored
using (var decryptedStream = new MemoryStream())
{
using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
{
using (var decryptor = aes.CreateDecryptor())
{
//decrypt stream and write it to parent stream
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
//fails here with "Length of the data to decrypt is invalid." error
cryptoStream.CopyTo(decryptedStream);
int data;
//fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
//implying it is in fact decrypting part of it, just not everything
do
{
data = cryptoStream.ReadByte();
decryptedStream.WriteByte((byte)cryptoStream.ReadByte());
} while (!cryptoStream.HasFlushedFinalBlock);
//fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
//implying it is in fact decrypting part of it, just not everything
while ((data = cryptoStream.ReadByte()) != -1)
{
decryptedStream.WriteByte((byte)data);
}
}
}
}
//reset position in prep for reading
decryptedStream.Position = 0;
return decryptedStream.ConvertToByteArray();
}
}
One of the comments mentioned wanting to know what ConvertToByteArray is, and it's just a simple extension method:
/// <summary>
/// Converts a Stream into a byte array.
/// </summary>
/// <param name="stream">The stream to convert.</param>
/// <returns>A byte[] array representing the current stream.</returns>
public static byte[] ConvertToByteArray(this Stream stream)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
The code never reaches this though - it dies before I can ever get it to this point.
After a lot of back and forth from various blogs, I found I actually had a couple of errors in the above code that were nailing me. First, the encryption process was incorrectly writing the array - it was wrapped with a CryptoStream instance, but wasn't actually utilizing that so I was writing the unencrypted data to Azure. Here is the proper route to go with this (fileKey is part of a custom class I created to generate Key/IV pairs, so wherever that is referenced can be changed to the built-in process from RijndaelManaged or anything else you'd utilize for coming up with a key/IV pair):
using (var aes = new RijndaelManaged { KeySize = 256, Key = fileKey.Key, IV = fileKey.IV })
{
using (var encryptedStream = new MemoryStream())
{
using (ICryptoTransform encryptor = aes.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write))
{
using (var originalByteStream = new MemoryStream(file.File.Data))
{
int data;
while ((data = originalByteStream.ReadByte()) != -1)
cryptoStream.WriteByte((byte)data);
}
}
}
var encryptedBytes = encryptedStream.ToArray();
return encryptedBytes;
}
}
Second, since my encryption process involves multiple steps (three total keys per file - container, filename and file itself), when I tried to decrypt, I was using the wrong key (which is seen above when I referenced blobKey to decrypt, which was actually the key used for encrypting the filename and not the file itself. The proper decryption method was:
//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
//stream where decrypted contents will be stored
using (var decryptedStream = new MemoryStream())
{
using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
{
using (var decryptor = aes.CreateDecryptor())
{
//decrypt stream and write it to parent stream
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
int data;
while ((data = cryptoStream.ReadByte()) != -1)
decryptedStream.WriteByte((byte)data);
}
}
}
//reset position in prep for reading
decryptedStream.Position = 0;
return decryptedStream.ConvertToByteArray();
}
}
I had looked into the Azure Encryption Extensions (http://www.stefangordon.com/introducing-azure-encryption-extensions/), but it was a little more local file-centric than I was interested - everything on my end is streams/in-memory only, and retrofitting that utility was going to be more work than it was worth.
Hopefully this helps anyone looking to encrypt Azure blobs with zero reliance on the underlying file system!
Bit late to the party, but in case this is useful to someone who finds this thread:
The following works well for me.
internal static byte[] AesEncryptor(byte[] key, byte[] iv, byte[] payload)
{
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(key, iv);
var encrypted = encryptor.TransformFinalBlock(payload, 0, payload.Length);
return iv.Concat(encrypted).ToArray();
}
}
and to decrypt:
internal static byte[] AesDecryptor(byte[] key, byte[] iv, byte[] payload)
{
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
return decryptor.TransformFinalBlock(payload, 0, payload.Length);
}
}
this works for encrypting/decrypting both fixed length hex strings when decoded from hex to byte[] as well as utf8 variable length strings when decoded using Encoding.UTF8.GetBytes().
I've written Encryption/Decryption methods using the RC2CryptoServiceProvider in C# and for some reason, I cannot get my decryptor to decrypt the final few bytes. The file seems to just cut off. My encryption method looks like:
public static byte[] EncryptString(byte[] input, string password)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
byte[] IV = new byte[8];
ICryptoTransform encryptor = RC2.CreateEncryptor(pbeKey, IV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(input, 0, input.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
While my decryption looks like:
public static byte[] DecryptString(byte[] input, string password, int originalSize)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
byte[] IV = new byte[8];
ICryptoTransform decryptor = RC2.CreateDecryptor(pbeKey, IV);
MemoryStream msDecrypt = new MemoryStream();
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);
csDecrypt.Write(input, 0, originalSize);
// csDecrypt.FlushFinalBlock();
char[] decrypted = new char[input.Length];
decrypted = System.Text.Encoding.UTF8.GetChars(msDecrypt.ToArray());
return msDecrypt.ToArray();
}
The char[] decrypted is returning the whole file decrypted, except the file ends with </LudoData> and when decrypting, I only get up to the first < character.
I have been playing with the lengths of things and nothing is changing anything. In my specific case, input is of length 11296, and originalSize is of size 11290. However, decrypted ends up being of size 11280 when decrypting. What gives!
Is there a reason that you have the Flush() commented out? Have you tried fully closing your streams?
Sigh, I fought this battle about a month ago and had a very similar issue, except I was experiencing TOO much on the end. ToArray was my solution.
You're doing some weird stuff here I'm not exactly sure of. You're using cryptostreams when you don't have to, you're keeping track of the original length for some weird reason and you're using deprecated classes. Your issue is probably a combination of padding, incorrect assumptions (evidenced by originalLength) and incorrect handling of streams (which can be tricky). Try this instead:
Encrypt:
var rij = RijndaelManaged.Create();
rij.Mode = CipherMode.CBC;
rij.BlockSize = 256;
rij.KeySize = 256;
rij.Padding = PaddingMode.ISO10126;
var pdb = new Rfc2898DeriveBytes(password,
Encoding.Default.GetBytes("lolwtfbbqsalt" + password));
var enc = rij.CreateEncryptor(pdb.GetBytes(rij.KeySize / 8),
pdb.GetBytes(rij.BlockSize / 8));
return enc.TransformFinalBlock(unencryptedBytes, 0, unencryptedBytes.Length);
Decrypt:
// throws a cryptographic exception if password is wrong
var rij = RijndaelManaged.Create();
rij.Mode = CipherMode.CBC;
rij.BlockSize = 256;
rij.KeySize = 256;
rij.Padding = PaddingMode.ISO10126;
var pdb = new Rfc2898DeriveBytes(password,
Encoding.Default.GetBytes("lolwtfbbqsalt" + password));
var dec = rij.CreateDecryptor(pdb.GetBytes(rij.KeySize / 8),
pdb.GetBytes(rij.BlockSize / 8));
return dec.TransformFinalBlock(encryptedBytes, 0,
encryptedBytes.Length);
Notice the only thing different within these two methods are CreateEncryptor/CreateDecryptor, so you can refactor out lots of duplication. Also notice I get in a byte array and get out a byte array without having to use any streams. Its also a bit more secure than RC2, and would be even more so if the salt were more random.