views:

157

answers:

6

Hi. This is kind of an unusual problem. I am having difficulty encrypting a file using 3 passwords. I am attempting to wrap one CryptoStream around two other CryptoStreams, but when I write the file to the disk, it seems to become corrupted, and the padding cannot be completely removed. Why would this be happening? EDIT: This was SOLVED. It turned out to simply be a server filesystem problem. It was fixed by repairing the virtual disk file.

Edit: Here's some sample code

 public static Stream Encrypt(Stream source, int delcount, params keyPair[] cryptInfo)
    {

        Stream prevStream = source;
        foreach (keyPair et in cryptInfo)
        {
            Rijndael mydale = Rijndael.Create();
            mydale.BlockSize = 256;
            mydale.KeySize = 256;
            mydale.IV = et.IV;
            mydale.Key = et.key;

            CryptoStream mystream = new CryptoStream(prevStream, mydale.CreateEncryptor(), CryptoStreamMode.Write);
            prevStream = mystream;
        }
        return prevStream;

}

Here's the full program Program.cs

class Program
{
    static string opcode = "test";
    static string IDCID = "an ID";
    static string password = "A strong password";
    static void Main(string[] args)
    {
        if (Console.ReadLine() == "encrypt")
        {
            Stream thestream = File.Open(Environment.CurrentDirectory + "\\sample.txt", FileMode.Create, FileAccess.ReadWrite);

            PasswordDeriveBytes mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes(opcode), Encoding.ASCII.GetBytes(opcode));
            byte[] key = mybytes.GetBytes(32);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes((IDCID.Length + password.Length + opcode.Length * 15).ToString()), Encoding.ASCII.GetBytes((IDCID.Length + password.Length + 5 + opcode.Length * 24).ToString()));
            byte[] IV = mybytes.GetBytes(32);
            keyPair mypair = new GlobalGridCore.keyPair(IV, key);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes(password), Encoding.ASCII.GetBytes(password));
            key = mybytes.GetBytes(32);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes((IDCID.Length + password.Length + opcode.Length * 9).ToString()), Encoding.ASCII.GetBytes((IDCID.Length + password.Length + 7 + opcode.Length * 24).ToString()));
            IV = mybytes.GetBytes(32);
            keyPair secondpair = new keyPair(IV, key);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes(IDCID), Encoding.ASCII.GetBytes(IDCID));
            key = mybytes.GetBytes(32);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes((IDCID.Length + password.Length + opcode.Length * 2).ToString()), Encoding.ASCII.GetBytes((IDCID.Length + password.Length + 14 + opcode.Length * 7).ToString()));
            IV = mybytes.GetBytes(32);
            keyPair thirdpair = new keyPair(IV, key);
            keyPair[] list = new keyPair[] { mypair, secondpair, thirdpair };
            thestream = gridCrypto.Encrypt(thestream, 0, list);
            BinaryWriter mywriter = new BinaryWriter(thestream);
            mywriter.Write("ehlo");
            mywriter.Write(new byte[512]);
            mywriter.Flush();
        }
        else
        {
            Stream thestream = File.Open(Environment.CurrentDirectory + "\\sample.txt", FileMode.Open, FileAccess.ReadWrite);

            PasswordDeriveBytes mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes(opcode), Encoding.ASCII.GetBytes(opcode));
            byte[] key = mybytes.GetBytes(32);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes((IDCID.Length + password.Length + opcode.Length * 15).ToString()), Encoding.ASCII.GetBytes((IDCID.Length + password.Length + 5 + opcode.Length * 24).ToString()));
            byte[] IV = mybytes.GetBytes(32);
            keyPair mypair = new GlobalGridCore.keyPair(IV, key);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes(password), Encoding.ASCII.GetBytes(password));
            key = mybytes.GetBytes(32);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes((IDCID.Length + password.Length + opcode.Length * 9).ToString()), Encoding.ASCII.GetBytes((IDCID.Length + password.Length + 7 + opcode.Length * 24).ToString()));
            IV = mybytes.GetBytes(32);
            keyPair secondpair = new keyPair(IV, key);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes(IDCID), Encoding.ASCII.GetBytes(IDCID));
            key = mybytes.GetBytes(32);
            mybytes = new PasswordDeriveBytes(Encoding.Unicode.GetBytes((IDCID.Length + password.Length + opcode.Length * 2).ToString()), Encoding.ASCII.GetBytes((IDCID.Length + password.Length + 14 + opcode.Length * 7).ToString()));
            IV = mybytes.GetBytes(32);
            keyPair thirdpair = new keyPair(IV, key);
            keyPair[] list = new keyPair[] { mypair, secondpair, thirdpair };
            thestream = gridCrypto.Decrypt(thestream, list);
          BinaryReader myreader = new BinaryReader(thestream);
          Console.WriteLine(myreader.ReadString());
          Console.ReadLine();
        }

    }
}

cryptDriver.cs

abstract class gridCrypto
{
    /// <summary>
    /// Decrypts the input stream to the output stream
    /// </summary>
    /// <param name="source">I</param>
    /// <param name="dest">O</param>
    /// <param name="cryptInfo">U</param>
    public static Stream Decrypt(Stream source, params keyPair[] cryptInfo)
    {
        Stream prevStream = source;
        foreach (keyPair et in cryptInfo)
        {
            Rijndael mydale = Rijndael.Create();
            mydale.BlockSize = 256;
            mydale.KeySize = 256;
            mydale.IV = et.IV;
            mydale.Key = et.key;
            CryptoStream mystream = new CryptoStream(prevStream, mydale.CreateDecryptor(), CryptoStreamMode.Read);
            prevStream = mystream;
        }
        return prevStream;
    }
   /// <summary>
   /// Encrypts the input stream and securely deletes the input file with the specified number of passes. The source stream MUST have length
   /// </summary>
   /// <param name="source">The source stream (to be deleted)</param>
   /// <param name="dest">The destination stream</param>
   /// <param name="delcount">The number of passes to erase the file</param>
   /// <param name="cryptInfo">Crypto stuff</param>
    public static Stream Encrypt(Stream source, int delcount, params keyPair[] cryptInfo)
    {

        Stream prevStream = source;
        foreach (keyPair et in cryptInfo)
        {
            Rijndael mydale = Rijndael.Create();
            mydale.BlockSize = 256;
            mydale.KeySize = 256;
            mydale.IV = et.IV;
            mydale.Key = et.key;

            CryptoStream mystream = new CryptoStream(prevStream, mydale.CreateEncryptor(), CryptoStreamMode.Write);
            prevStream = mystream;
        }
        return prevStream;
        //int cpos = 0;
        //while (cpos < delcount)
        //{
        //    source.Position = 0;
        //    while (source.Position < source.Length)
        //    {
        //        if (source.Length - source.Position > 512)
        //        {
        //            Random mrand = new Random();

        //            byte[] thearray = new byte[512];
        //            mrand.NextBytes(thearray);
        //            source.Write(thearray, 0, thearray.Length);
        //        }
        //        else
        //        {
        //            Random mrand = new Random();

        //            byte[] thearray = new byte[source.Length-source.Position];
        //            mrand.NextBytes(thearray);
        //            source.Write(thearray, 0, thearray.Length);
        //            source.Flush();
        //        }
        //    }
        //    cpos += 1;
        //}
    }
}
class keyPair
{
    public byte[] IV;
    public byte[] key;
    public keyPair(byte[] InitializationVector, byte[] Key)
    {
        IV = InitializationVector;
        key = Key;
    }
}

The code to delete the file is commented out and is not used in the program.

A: 

Are you sure that the new CryptoStream constructor does not spawn any thread?

Are you sure that when you do prevStream = mystream the newly created mystream has actually finished writing?

Perhaps you need to "flush" the stream first and make sure that the call has completed before assigning it to the older object. I do not know the library, but this looks like a race condition to me.

lorenzog
New CryptoStream() does not appear to spawn any new threads, and I am not writing anything before I call prevstream = mystream; so that should not be the problem. Also; the system this is running on will have plenty of memory, so it is CPU time; not memory usage I am concerned about. The stream is immediately flushed as each block is written, so there should be no need to manually call flush.
IDWMaster
Also - It should be noted the file is encrypted with the Encrypting File System in addition to these cryptostreams.
IDWMaster
A: 

Why not just encrypt them to an array of bytes, then run those bytes through another algorithm.

If you design it well then you can have n-number of encryption algorithms, and you can embed one inside the other if you desire.

But, if you go want go your route then you may want to break it up into separate steps for testing, to see where the problem may be.

So, you would first encrypt with one algorithm, then immediately decrypt, then use two, then three.

You should have a unit test for this, so that you can test with a short, then a long message, to make certain that you aren't having a problem with message size.

James Black
That's what I would do.
Aliostad
A: 

I believe the problem is that you do not use the Write method of the CryptoStream where encryption happens. You just initialise them which sets the stream that you will be writing to and not reading from. Have a look at this sample:

http://msdn.microsoft.com/en-us/library/k1f992c1.aspx

which says:

    // Create or open the specified file.
        FileStream fStream = File.Open(FileName, FileMode.OpenOrCreate);

        // Create a new Rijndael object.
        Rijndael RijndaelAlg = Rijndael.Create();

        // Create a CryptoStream using the FileStream 
        // and the passed key and initialization vector (IV).
        CryptoStream cStream = new CryptoStream(fStream,
            RijndaelAlg.CreateEncryptor(Key, IV),
            CryptoStreamMode.Write);

        // Create a StreamWriter using the CryptoStream.
        StreamWriter sWriter = new StreamWriter(cStream);

        try
        {
            // Write the data to the stream 
            // to encrypt it.
            sWriter.WriteLine(Data);
        }
        .....

I personally always use TransfromBlock because I know what my in and out bytes are.

Aliostad
The file IS being encrypted; it's just being encrypted the WRONG way.
IDWMaster
I cannot see how. File gets encrypted when you write to the stream.
Aliostad
+1  A: 

If I understand correctly, your passwords have to be used in reversed order when decrypting.

Try this in the decrypt-part of your Program.cs:

  keyPair[] list = new keyPair[] { thirdpair, secondpair, mypair };
  thestream = gridCrypto.Decrypt(thestream, list);
weberph
I've tried that already; and then I receive an InvalidPadding exception; which does not happen when they are not reversed.
IDWMaster
+1  A: 

You're not disposing of your stream. Insert this into your encryption test code:

thestream.Dispose();

or (preferably) use a using statement to open the file:

using (Stream stream = File.Open(...))
{
}

at the end, and it works.

You should (almost) always use using statements when using streams, to make sure they're closed properly. In the case of crypto streams, closing the stream also writes the final block.

I'm somewhat surprised you don't need to reverse the order of the keys... but the key creation code is sufficiently obscure that I don't really want to explore it much further :(

Jon Skeet
The file stream is automatically disposed by the server when the decryption/encryption process is finished. It is being ran as an XSFApp (proprietary executable format); which automatically disposes resources and uses a slightly modified garbage collector. If the stream is disposed before the system automatically disposes it; an exception is thrown when exiting the module, causing the entire application to restart.
IDWMaster
In other words; the stream must be left open for the server to flush it, encrypt it using EFS and other algorithms, then move it into a database file. The file is encrypted using both .NET and native algorithms (when .NET code is done accessing the file, and Database.Commit() is called). It should also be noted that the code works when running it on a desktop, but doesn't work when I deploy it to the cloud.
IDWMaster
@IDWMaster: Well, all I can say is that the test program you've given us works when you dispose of the stream. I wouldn't be surprised to find out that it's something in your "custom" disposal which is going wrong.
Jon Skeet
I'm not in control of the custom disposal. It's managed at the corporate server level.
IDWMaster
IDWMaster: Well please provide a test program that demonstrates the problem then.
Jon Skeet
This program is nearly identical; except the methods in which the files are opened (Database.OpenRead instead of File.Open)
IDWMaster
@IDWMaster: But the test program works... it encrypts and then decrypts, printing out the result appropriately. Does it not do that when you tested it? There's not much point in giving us a test program which works entirely correctly... it just shows that the problem is probably in the code you haven't given us.
Jon Skeet
This code is the only part of the program I'm in charge of, and I can't figure out why it works on a desktop computer, but not when deployed to the server. I don't have access to the rest of the code.
IDWMaster
@IDWMaster: Well for a start you should find out whether it's the *writing* side or the *reading* side which is having problems. Compare the file written on the desktop to what ends up on the database side.
Jon Skeet
It turned out to be a problem with the filesystem on the reading side. This was fixed after repairing the virtual hard disk file on the server; which had to be done by the admin.
IDWMaster
A: 

It was a filesystem problem on the server. It was fixed after the server admin ran an FSRepair on the server.

IDWMaster
I will accept this answer in 2 days ( can't accept before than for some reason)
IDWMaster