I am trying to encrypt a stream (coming from a file) using AesManaged. I can encrypt the file without error, but on decryption I get the following CryptographicException:
Padding is invalid and cannot be
removed.
The exception is raised when the CryptoStream is being disposed. I use the following to encrypt the input data:
public byte[] Encrypt(Stream plain)
{
// Create a decrytor to perform the stream transform.
using( var msEncrypt = new MemoryStream() )
{
using (ICryptoTransform encryptor = _myAes.CreateEncryptor(_myAes.Key, _myAes.IV))
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt))
{
int buf_size = 32768;
byte[] buffer = new byte[buf_size];
int read = 0;
while ((read = plain.Read(buffer, 0, buf_size)) > 0)
{
swEncrypt.Write(buffer, 0, read);
}
}
return msEncrypt.ToArray();
}
}
And this to decrypt the data:
public byte[] Decrypt(Stream cipherText)
{
using (MemoryStream ms = new MemoryStream())
{
// Create a decrytor to perform the stream transform.
using (ICryptoTransform decryptor = _myAes.CreateDecryptor(_myAes.Key, _myAes.IV))
using (CryptoStream csDecrypt = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
using (BinaryWriter swDecrypt = new BinaryWriter(csDecrypt))
{
int buf_size = 32768;
byte[] buffer = new byte[buf_size];
int read = 0;
while ((read = cipherText.Read(buffer, 0, buf_size)) > 0)
{
swDecrypt.Write(buffer, 0, read);
}
}
return ms.ToArray();
}
}
Any ideas about why this exception is coming up would be great. Thanks
UPDATE
Here is where the Aes object was created, note the Key and IV are just set to their current values temporarily, it is not the real key that will be used:
private Crypto()
{
_myAes = new AesManaged();
_myAes.Padding = PaddingMode.PKCS7;
_myAes.KeySize = 128;
_myAes.Key = Enumerable.Repeat((byte)'B', 128 / 8).ToArray();
_myAes.IV = Enumerable.Repeat((byte)'C', 128 / 8).ToArray();
}
In the past I got this exception when I tried to decrypt a buffer whose length was not a multiple of 16 bytes.
Did you try calling Flush on the CryptoStream before it is disposed? Possibly, if it isn't flushed then it ends up trying to decrypt a buffer with a non-aligned length.
And another note - I don't know if this will solve your problem, but when you create a CryptoStream in order to decrypt the buffer, shouldn't you be using CryptoStreamMode.Read instead of CryptoStreamMode.Write?
Ensure that you finish the CryptoStream cleanly on the write side. You may need to call FlushFinalBlock() to ensure that the end-of-stream padding is written through -- otherwise, you are likely to end up missing the final block of the stream, which will result in an invalid padding exception.
I don't know if it is still relevant to post something after 4 years but, you should try to set padding to none. I got the same problem with 3DES and the issue got solved with that (but make sure the length of data to be decrypted is correct...)
private Crypto()
{
_myAes = new AesManaged();
_myAes.Padding = PaddingMode.none; //rather than _myAes.Padding = PaddingMode.PKCS7;
_myAes.KeySize = 128;
_myAes.Key = Enumerable.Repeat((byte)'B', 128 / 8).ToArray();
_myAes.IV = Enumerable.Repeat((byte)'C', 128 / 8).ToArray();
}
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 get error "the input data is not a complete block", I dont know if my code is wrong or something is missing. i try to encrypt/decrypt bytes with same lenght.
=byte[] plain => MyEnc(plain) => byte[] encrypted => MyDec(encrypt) => byte[] plain
Plain and encrypted have the same length.
This is my encryption code:
public static byte[] MyEnc(byte[] Input)
{
byte[] inputencdec = Input;
byte[] encrypted;
using (MemoryStream mstream = new MemoryStream())
{
using (AesCryptoServiceProvider encdec = new AesCryptoServiceProvider())
{
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
ICryptoTransform icrypt = encdec.CreateEncryptor(encdec.Key, encdec.IV);
using (CryptoStream cryptoStream = new CryptoStream(mstream,
icrypt, CryptoStreamMode.Write))
{
cryptoStream.Write(inputencdec, 0, inputencdec.Length);
}
}
encrypted = mstream.ToArray();
}
return encrypted;
}
this is my decryption code:
public static byte[] MyDec(byte[] Input)
{
byte[] inputencdec = Input;
byte[] buffer = new byte[Input.Length];
int totalRead = 0;
byte[] plain;
MemoryStream plainStream = new MemoryStream();
using (MemoryStream mStream = new MemoryStream(inputencdec))
{
using (AesCryptoServiceProvider encdec = new AesCryptoServiceProvider())
{
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
ICryptoTransform icrypt = encdec.CreateDecryptor(encdec.Key, encdec.IV);
using (CryptoStream cryptoStream = new CryptoStream(mStream, icrypt, CryptoStreamMode.Read))
{
while (true)
{
int read = cryptoStream.Read(buffer, 0, inputencdec.Length);
if (read == 0)
break;
else
plainStream.Write(buffer, totalRead, read);
totalRead += read;
}
}
}
plain = plainStream.ToArray();
}
return plain;
}
Plain and encrypted are not the same length for CBC mode encryption, which you are using. The plaintext needs to be padded before it can be encrypted, so the ciphertext size is always larger than the plaintext message for CBC (which is the default mode that the decryptor is using).
Streaming modes do not need to expand the ciphertext as they do not require padding. Unfortunately Microsoft opted not to include counter mode in .NET. You could use CFB mode.
You could also decide to implement counter mode using ECB if you require parallel encryption (in the comments below the question). Generally AES is so fast nowadays that parallelism is not required. Implementing a statically sized counter and CTR buffer should pale in comparison with creating the multithreaded code.
This is incorrect and may cause an issue:
int read = cryptoStream.Read(buffer, 0, inputencdec.Length);
you should of course put in buffer.length, not inputencdec.length. The use of a buffer is to store data in a buffer, by reading up to buffer.length bytes per loop iteration.
This is incorrect as well:
plainStream.Write(buffer, totalRead, read);
the problem is that totalRead should be the offset in the buffer, not the offset within the stream. You're reading into offset 0 of the buffer, so you should start writing from offset 0 as well.
You could also create a MemoryStream for the plaintext, wrap it with a CryptoStream using a decryptor, and then write the ciphertext all in one go. There is no need for the encryption / decryption to use a different scheme, as far as I can see. You seem to keep all the plaintext / ciphertext in memory anyway.
Notes:
for CBC mode the IV should be random; it is often prefixed to the ciphertext and removed and used by the decryptor;
keys and IV's should consist of random bytes; they should not be strings.
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'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.
I have this code which is meant to decrypt a file, but if I run it, it throws a CryptographicException (length of the data to decrypt is invalid) at the end of the using statement using (CryptoStream ...) { ... }
public static void DecryptFile(string path, string key, string saltkey, string ivkey)
{
try
{
byte[] cipherTextBytes;
using (StreamReader reader = new StreamReader(path)) cipherTextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
byte[] keyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] plainTextBytes;
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
plainTextBytes = new byte[Encoding.UTF8.GetByteCount((new StreamReader(cryptoStream)).ReadToEnd())];
cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
//plainTextBytes = memoryStream.ToArray();
cryptoStream.FlushFinalBlock();
}
}
string result = Encoding.ASCII.GetString(plainTextBytes, 0, plainTextBytes.Length).TrimEnd("\0".ToCharArray());
using (FileStream writer = new FileStream(path, FileMode.Create)) writer.Write(Encoding.ASCII.GetBytes(result), 0, Encoding.ASCII.GetBytes(result).Length);
MessageBox.Show("Decrypt succesfull");
}
catch (Exception ex)
{
MessageBox.Show("An error while decrypting the file:\n\n" + ex, "Error");
}
}
}
Does anybody know why this is or how I can fix it? (I don't know if it comes from my encrypting method, but I have another program which uses the exact same thing to encrypt strings and that one does work.)
My encrypting method:
public static void EncryptFile(string path, string key, string saltkey, string ivkey)
{
try
{
byte[] TextBytes;
using (StreamReader reader = new StreamReader(path)) TextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
byte[] KeyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(KeyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] CipherTextBytes;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(TextBytes, 0, TextBytes.Length);
cs.FlushFinalBlock();
CipherTextBytes = ms.ToArray();
}
}
using (FileStream writer = new FileStream(path, FileMode.Create)) writer.Write(CipherTextBytes, 0, CipherTextBytes.Length);
MessageBox.Show("Encrypt succesfull");
}
catch (Exception ex)
{
MessageBox.Show("An error while encrypting the file:\n\n" + ex, "Error");
}
}
There are a few issues with your code:
You use a padding mode of Zeroes in Encrypt and None in Decrypt. These need to match
You load the bytes from your file using Encoding.UTF8, you need to read the raw bytes, you can do this by using the following instead:
byte[] cipherTextBytes = File.ReadAllBytes(path);
You call cryptoStream.FlushFinalBlock(); when only using a single iteration of a stream. You don't need this call in Decrypt if you are only doing a single block iteration.
You read the original text from your file in UTF8 and then write it back as ASCII. You should either change the result assignment in decrypt to use UTF8 or (preferably) change both to use raw bytes.
You use Create to interact with the files when you are overwriting in-place. If you know the file already exists (as you are replacing it) you should use truncate or better yet just call File.WriteAllBytes.
Your decrypt is all kinds of messed up. It looks like you're tying yourself into knots over byte retrieval. You should just use the raw bytes out of the CryptoStream and not try using UTF8
Here's a revised set of methods for you:
public static void DecryptFile(string path, string key, string saltkey, string ivkey)
{
byte[] cipherTextBytes = File.ReadAllBytes(path);
byte[] keyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CFB, Padding = PaddingMode.PKCS7 };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] plainTextBytes;
const int chunkSize = 64;
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
using (MemoryStream dataOut = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var decryptedData = new BinaryReader(cryptoStream))
{
byte[] buffer = new byte[chunkSize];
int count;
while ((count = decryptedData.Read(buffer, 0, buffer.Length)) != 0)
dataOut.Write(buffer, 0, count);
plainTextBytes = dataOut.ToArray();
}
File.WriteAllBytes(path, plainTextBytes);
}
and:
public static void EncryptFile(string path, string key, string saltkey, string ivkey)
{
byte[] TextBytes = File.ReadAllBytes(path);
byte[] KeyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CFB, Padding = PaddingMode.PKCS7 };
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(KeyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] CipherTextBytes;
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(TextBytes, 0, TextBytes.Length);
cs.FlushFinalBlock();
CipherTextBytes = ms.ToArray();
}
File.WriteAllBytes(path, CipherTextBytes);
}
Most likely your problem comes from cipherTextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
You can't use UTF8 to encode arbitrary binary data, you will likely need to fix both your encrypting end decrypting end. You either must use cipherTextBytes = File.ReadAllBytes(path) or if you are forced to use strings you must first encode the bytes to a valid string using Convert.ToBase64String()
In my case it happened because I was decrypting a value which was never encrypted.
I had my values saved in the database without encryption. But when I introduced encryption and decryption routine in my code and executed my program first time, it was actually trying to decrypt a value which was never encrypted, hence the problem.
Simply clearing the existing values from the database for the initial run solved the problem. If you don't want to lose data even during the first run then you should write a separate routine to encrypt the existing values.