Henrik Nilsson

Hello fellow developers!

I have a problem in two routines for encrypting and decrypting text files, it works fine except the decrypted file contains 3 extra characters "i ". Is there anyone out there that has experienced the same problem or has an idea for a solution I have tried using different PaddingMode settings but without getting closer to a solution.

PaddingMode.None - Exception during encryption, "Length of the data to encrypt is invalid."
PaddingMode.ANSIX923 - Ok but "i " at end of file.
PaddingMode.ISO10126 - Ok but "i " at end of file.
PaddingMode.PKCS7 - Ok but "i " at end of file.
PaddingMode.Zeroz - Exception during decryption, "Invalid character in a Base-64 string."

Below is the sourcecode attached...

public void Encrypt(string sourceFilePath, string targetFilePath)

{

// Create CryptoServiceProvider for AES(Rijndael).

RijndaelManaged cryptoProvider = new RijndaelManaged();

cryptoProvider.Padding = PaddingMode.PKCS7;

int test = cryptoProvider.BlockSize;

// Create StreamReader for reading unencrypted data from source file.

using (StreamReader sourceReader = new StreamReader(sourceFilePath, true))

{

// Get encoding for source file used for target file.

Encoding sourceEncoding = sourceReader.CurrentEncoding;

// Crate fileStream for writing encrypted target file data using the same encoding as source file.

using (FileStream targetStream = new FileStream(targetFilePath, FileMode.Create, FileAccess.Write))

{

// Create CryptoStream object using TripleDESCryptoServiceProvider object for encrypting data.

using (CryptoStream cryptoStream = new CryptoStream(targetStream, cryptoProvider.CreateEncryptor(_encryptionKey, _iv), CryptoStreamMode.Write))

{

// Create StreamWriter for writing encrypted data to cryptoStream object.

using (StreamWriter cryptoWriter = new StreamWriter(cryptoStream, sourceEncoding))

{

// Read from the input file.

byte[] buffer = new byte[sourceReader.BaseStream.Length];

sourceReader.BaseStream.Read(buffer, 0, buffer.Length);

// Encrypt and write to target file.

cryptoWriter.Write(Convert.ToBase64String(buffer));

}

}

}

}

}

Next method____________________________________________

public void Decrypt(string sourceFilePath, string targetFilePath)

{

// Create CryptoServiceProvider for AES(Rijndael).

RijndaelManaged cryptoProvider = new RijndaelManaged();

cryptoProvider.Padding = PaddingMode.PKCS7;

// Create StreamReader for reading encrypted data from source file.

using (StreamReader sourceReader = new StreamReader(sourceFilePath, true))

{

// Get encoding for source file used for target file.

Encoding sourceEncoding = sourceReader.CurrentEncoding;

// Create CryptoStream object using TripleDESCryptoServiceProvider object for decrypting data.

using (CryptoStream cryptoStream = new CryptoStream(sourceReader.BaseStream, cryptoProvider.CreateDecryptor(_encryptionKey, _iv), CryptoStreamMode.Read))

{

// Create StreamReader for reading encrypted data from cryptoStream object.

using (StreamReader cryptoReader = new StreamReader(cryptoStream, sourceEncoding))

{

// Create StreamWriter to write decrypted data to target file.

using (StreamWriter targetFileWriter = new StreamWriter(targetFilePath, false, sourceEncoding))

{

// Byte array for holding data until time to write.

byte[] rawData;

// Convert source data from Base64.

string Base64 = cryptoReader.ReadToEnd();

rawData = Convert.FromBase64String(Base64);

// Decrypt and write to target file.

targetFileWriter.BaseStream.Write(rawData,0, rawData.Length);

}

}

}

}

}

Kind regards
Henrik Nilsson



Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

element109

Try calling cryptostream.FlushFinalBlock() after writing to your cryptostream.



Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

Henrik Nilsson

Unfortunately it didn't work, the result was an exception during decryption, "Padding is invalid and cannot be removed.".

I made this change to the encryption routine...

// Create StreamWriter for writing encrypted data to cryptoStream object.

using (StreamWriter cryptoWriter = new StreamWriter(cryptoStream, sourceEncoding))

{

// Read from the input file.

byte[] buffer = new byte[sourceReader.BaseStream.Length];

sourceReader.BaseStream.Read(buffer, 0, buffer.Length);

// Encrypt and write to target file.

cryptoWriter.Write(Convert.ToBase64String(buffer));

cryptoStream.FlushFinalBlock();

}





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

element109

Since there are so many considerations, if you can read VB there is a really good example of encryption located here: http://www.codeproject.com/useritems/Crypto.asp

If you can't read VB use this website to convert the code: http://www.carlosag.net/Tools/CodeTranslator/

PS After using the code translator you may have to make a couple of fixes. It works fairly well though.





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

Henrik Nilsson

This actually didn't help me, I wan't to know the reason those three characters are added or should I say not removed. My code is almost perfect (of course there is always optimizations to do), either I have missed something but in that case it isn't anything I have seen in any example or there is a bug in the runtime classes.

One thing that is important to me not available in the example you gave me is that the target file needs to have the same text encoding as the sourcefile and it's therefore I heavily use StreamReader and StreamWriter instead of simply the base streams.





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

element109

Code Snippet

//You can use this sample to encrypt/decrypt any type of file. Create a new Console Applicaton Project and paste this code.

using System.Security.Cryptography;

using System.IO;

using System.Text;

namespace ConsoleApplication1

{

class Program

{

static void Main(string[] args)

{

Rijndael.AESWriter en = new Rijndael.AESWriter();

en.EncryptFile("c:\\test.txt", "yourkeystring", Rijndael.IV_16);

//Rijndael.AESReader de = new Rijndael.AESReader();

//de.DecryptFile("c:\\test.txt.enc", "yourkeystring", Rijndael.IV_16);

}

}

public class Rijndael

{

internal static byte[] SALT_BYTES = new byte[] {

182,

53,

90,

15,

186,

239,

63,

37,

137,

182,

145};

internal static byte[] IV_16 = new byte[] {

16,

183,

53,

67,

234,

156,

109,

222,

9,

12,

85,

72,

44,

206,

23,

157};

private static byte[] DeriveKey(string OriginalKey, int NewKeyLength)

{

Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(OriginalKey, SALT_BYTES, 5);

return derivedBytes.GetBytes(NewKeyLength);

}

public class AESWriter

{

private CryptoStream cs;

private MemoryStream ms;

private RijndaelManaged aes;

private ICryptoTransform aesencrypt;

public void EncryptFile(string fileName, string key, byte[] IV)

{

if ((System.IO.File.Exists(fileName) == true))

{

byte[] filebytes = File.ReadAllBytes(fileName);

ms = new MemoryStream();

aes = new RijndaelManaged();

aes.Padding = System.Security.Cryptography.PaddingMode.PKCS7;

aesencrypt = aes.CreateEncryptor(DeriveKey(key, 16), IV);

cs = new CryptoStream(ms, aesencrypt, CryptoStreamMode.Write);

cs.Write(filebytes, 0, filebytes.Length);

cs.FlushFinalBlock();

File.WriteAllBytes((fileName + ".enc"), ms.ToArray());

this.Close();

System.IO.File.Delete(fileName);

}

}

private void Close()

{

if (!(cs == null))

{

cs.Close();

}

if (!(ms == null))

{

ms.Close();

}

if (!(aes == null))

{

aes.Clear();

}

if (!(aesencrypt == null))

{

aesencrypt.Dispose();

}

}

}

public class AESReader

{

private CryptoStream cs;

private MemoryStream ms;

private RijndaelManaged aes;

private ICryptoTransform aesdecrypt;

public void DecryptFile(string fileName, string key, byte[] IV)

{

if ((System.IO.File.Exists(fileName) == true))

{

if ((Path.GetExtension(fileName) == ".enc"))

{

byte[] filebytes = File.ReadAllBytes(fileName);

ms = new MemoryStream();

aes = new RijndaelManaged();

aes.Padding = System.Security.Cryptography.PaddingMode.PKCS7;

aesdecrypt = aes.CreateDecryptor(DeriveKey(key, 16), IV);

cs = new CryptoStream(ms, aesdecrypt, CryptoStreamMode.Write);

cs.Write(filebytes, 0, filebytes.Length);

cs.FlushFinalBlock();

ms.Position = 0;

byte[] Output = new byte[ms.Length - 1];

ms.Read(Output, 0, Output.Length);

this.Close();

File.WriteAllBytes(TrimExtension(fileName), Output);

System.IO.File.Delete(fileName);

}

}

}

private string TrimExtension(string filename)

{

int trmval = filename.LastIndexOf( ".");

string name = filename.Substring(0, trmval);

return name;

}

private void Close()

{

if (!(cs == null))

{

cs.Close();

}

if (!(ms == null))

{

ms.Close();

}

if (!(aes == null))

{

aes.Clear();

}

if (!(aesdecrypt == null))

{

aesdecrypt.Dispose();

}

}

}

}

}

//Hope this helps.





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

timvw

Am i right when i say the following:

- When you write the data you encrypt, and then convert to base64...
- When you read the data you first try to encrypt, and then convert from base64...

---
cryptoWriter.Write(Convert.ToBase64String(buffer));
---
byte[] rawData;
string Base64 = cryptoReader.ReadToEnd();
rawData = Convert.FromBase64String(Base64);
---


In the second phase you could do something like:

memoryStream = new MemoryStream(Convert.FromBase64String(File.ReadAllText(sourceFilePath)));
CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateDecryptor(_encryptionKey, _iv), CryptoStreamMode.Read);

StreamReader streamReader = new StreamReader(cryptoStream);
string decrypted = streamReader.ReadToEnd();

Console.WriteLine("decrypted: " + decrypted);








Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

element109

- When you write the data you encrypt, and then convert to base64...
Yes

 

- When you read the data you first try to decrypt, and then convert from base64...

No

Convert from base64 and then decrypt.

 





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

timvw

element109 wrote:

- When you write the data you encrypt, and then convert to base64...
Yes

- When you read the data you first try to decrypt, and then convert from base64...

No

Convert from base64 and then decrypt.



Well, that's the point i was trying to make towards the OP... If i understood his code well, he's firsts decrypting, and then converting from base64 in his Decrypt function...





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

Henrik Nilsson

That is not the problem, on encryption I first get data from the source stream...
// Read from the input file.
byte[] buffer = new byte[sourceReader.BaseStream.Length];
sourceReader.BaseStream.Read(buffer, 0, buffer.Length);

Before I convert it and write it to the encrytion stream (target)...
// Encrypt and write to target file.
cryptoWriter.Write(Convert.ToBase64String(buffer));

During decryption I first read from the decrypted source stream (cryptoReader)...
// Byte array for holding data until time to write.
byte[] rawData;
// Convert source data from Base64.
string Base64 = cryptoReader.ReadToEnd();

Before I finally convert it back from Base64 and write it to target....
rawData = Convert.FromBase64String(Base64);
// Decrypt and write to target file.
targetFileWriter.BaseStream.Write(rawData,0, rawData.Length);

It seems you have been misslead by my comment on the second last line above that unfortunately hasn't been updated during development, the decryption is actually made on the "string Base64 = cryptoReader.ReadToEnd();" line. If I would have tried to switch the order of Base64 conversion and decryption I would definately have got an exception trying to Base64 convert the encrypted data, propably "Invalid character in a Base-64 string.".
The data coming out of my routines are perfect with international characters and all except the three extra characters at the end and in between the encrypted file is truly encrypted.





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

timvw

Ok, i was mistaken.. The odd part at the end you get to see is the Byte-Order-Mark....

I'm not quite sure how you can disable it on the original sourceEncoding.. But i do know that you can disable it via the constructor of an Encoding, eg:

sourceEncoding = new UTF8Encoding(false);
using (StreamWriter targetFileWriter = new StreamWriter(targetFilePath, false, sourceEncoding))
{
byte[] rawData;
string Base64 = cryptoReader.ReadToEnd();
rawData = Convert.FromBase64String(Base64);
targetFileWriter.BaseStream.Write(rawData, 0, rawData.Length);
}





Re: Common Language Runtime RijndaelManaged and Base64 - overflow characters after decryption.

Henrik Nilsson

This at least tells me what it is and one way to get rid of it could be to create a new instance of the same type as the source encoding and specify that no byte order mark should be supplied. I had to take a bit different approach but it works now and I'm thankfull for all your help!

Henrik Nilsson