Search code examples
c#encryptionunicodefilestreamrijndael

C# Length of data to decrypt is invalid


There are quite a lot of similar questions on SO about this but "none" match my problem.

I a creating a file which is filled with encrypted random bytes. Then within this file at a specific location I write a few encrypted bytes. When I attempt to read in that section of bytes I get the "Length of data to decrypt is invalid".

This is the Encrypt and Decrypt Methods (found at some point on SO)

 private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };
        //TODO: The SALT needs to be derived and unique for each user!

        internal  byte[] Encrypt(byte[] plain)
        {
            string password = Properties.Settings.Default.PasswordOne;
            MemoryStream memoryStream;
            CryptoStream cryptoStream;
            Rijndael rijndael = Rijndael.Create();
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
            rijndael.Key = pdb.GetBytes(32);
            rijndael.IV = pdb.GetBytes(16);
            memoryStream = new MemoryStream();
            cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
            cryptoStream.Write(plain, 0, plain.Length);
            cryptoStream.Close();
            return memoryStream.ToArray();
        }

        internal  byte[] Decrypt(byte[] cipher)
        {
            string password = Properties.Settings.Default.PasswordOne;
            MemoryStream memoryStream;
            CryptoStream cryptoStream;
            Rijndael rijndael = Rijndael.Create();
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
            rijndael.Key = pdb.GetBytes(32);
            rijndael.IV = pdb.GetBytes(16);
            memoryStream = new MemoryStream();
            cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write);
            cryptoStream.Write(cipher, 0, cipher.Length);
            cryptoStream.Close();
            return memoryStream.ToArray();
        }

I save the new data within the first file using this

 private void InsertDEFT2NameLength(FileSystemEncryption fse, string Deft2FileName, FileStream fs, StreamWriter sw)
        {

            string deft2NameLength = "0";                                       // init the length
            if (DrivesLog != null) DrivesLog("Converting DEFT2 Name to Bytes");
            byte[] bdeft2Name = GetBytes(Deft2FileName);                        // convert filename to bytes
            if (DrivesLog != null) DrivesLog("Encrypting DEFT2 Name");
            byte[] ebdeft2Name = fse.Encrypt(bdeft2Name);                       // Encrypt
            if (DrivesLog != null) DrivesLog("Getting Length of Encrypted DEFT2 Name");
            long ebdeft2NameLength = ebdeft2Name.LongLength;                    // Get Length of the Encrypted Bytes as a long
            if (DrivesLog != null) DrivesLog("Converting DEFT2 Name Length to String");
            string sebdeft2NameLength = ebdeft2NameLength.ToString() + "!";     // Convert Length to string Add Exclamation so we know when we have read the full length
            if (DrivesLog != null) DrivesLog("Converting DEFT2 Name Length to Bytes");
            byte[] bsebdeft2NameLength = GetBytes(sebdeft2NameLength);          // Convert length string to bytes
            if (DrivesLog != null) DrivesLog("Encrypting DEFT2 Name Length");
            byte[] ebsebdeft2NameLength = fse.Encrypt(bsebdeft2NameLength);     // Encrypt
            if (DrivesLog != null) DrivesLog("Converting Encrypted DEFT2 Name Length to String");
            deft2NameLength = GetString(ebsebdeft2NameLength);                  // Convert to string

            if (DrivesLog != null) DrivesLog("Seeking to Correct Location");
            long startPos = GenerateDEFT2LengthStartPosition();
            fs.Seek(startPos, SeekOrigin.Current);    // Seek to correct location
            if (DrivesLog != null) DrivesLog("New Position " + startPos.ToString("N0"));
            if (DrivesLog != null) DrivesLog("Writing Encrypted Name Length to New Position");
            sw.Write(deft2NameLength);                                          // Write the Encrypted length
            fs.Flush();                                                         // Flush the buffer immediately
        }

I attempt to re-read in the encrypted data at that location using this method:

private long ReadDEFT2Len(string DEFT, long lenPos, FileSystemEncryption fse)
        {
            if (DrivesLog != null) DrivesLog("Reading DEFT2 Name Length");
            StringBuilder sb = new StringBuilder();

            FileStream fs = null;
            StreamReader sr = null;

            try
            {
                fs = new FileStream(DEFT, FileMode.Open, FileAccess.Read, FileShare.Read);
                sr = new StreamReader(fs, Encoding.Unicode);

                char[] C = new char[101];
                fs.Seek(lenPos, SeekOrigin.Begin);
                sr.Read(C, 0, 100);

                string sC = new string(C);
                byte[] bsC = GetBytes(sC);

                byte[] dRes = fse.Decrypt(bsC);  // This is where the Exception is thrown.
                foreach(char ic in GetString(dRes))
                {
                    if (ic == '!') break;
                    sb.Append(ic.ToString());
                }

                sr.Close();
                fs.Close();

                if (DrivesLog != null) DrivesLog("DEFT2 Name Length = " + sb.ToString());
                return long.Parse(sb.ToString());
            }
            catch (Exception ex)
            {
                if (DrivesLog != null) DrivesLog("ERROR Reading DEFT2 Name Length " + ex.Message);
                if (sr != null) sr.Close();
                if (fs != null) fs.Close();
                return -1;
            }

        }

I have saved and loaded using Unicode Encoding so that is not the problem.. any ideas on why this exception is being thrown and how to resolve it?


Solution

  • The Rijndael cipher only works on specific block sizes (16, 24, or 32 bytes). You're getting an exception here because the length of data you're decrypting is not an exact multiple of the block size.