1

I am trying to encrypt and decrypt the file. Using AES methods in a MVC web application. I am able to encrypt file and decrypt only once. If I try for second time it gives me "Padding is invalid and cannot be removed error."

  1. I have tried almost every combination with different properties of AES properties.
  2. I have tried using statement for disposing the objects.
  3. I have tried using FlushFinalBlock() after CryptoStream write.
  4. I have tried with using AES.Padding to Zero (gives me no error but the file doesnt decrypt ), AES.Padding to None gives me error('Length of the data to encrypt is invalid.'). AES.Padding to PKCS7 gives me error (padding is invalid.)

Please find my code below.

public class EncryptionDecryption
{
    //  Call this function to remove the key from memory after use for security
    [DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
    public static extern bool ZeroMemory(IntPtr Destination, int Length);

    /// <summary>
    /// Creates a random salt that will be used to encrypt your file. This method is required on FileEncrypt.
    /// </summary>
    /// <returns></returns>
    public static byte[] GenerateRandomSalt()
    {
        byte[] data = new byte[32];

        using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
        {
            // Ten iterations.
            for (int i = 0; i < 10; i++)
            {
                // Fill buffer.
                rng.GetBytes(data);
            }
        }
        return data;
    }


    /// <summary>
    /// Encrypts a file from its path and a plain password.
    /// </summary>
    /// <param name="inputFile"></param>
    /// <param name="password"></param>
    public static void FileEncrypt(string inputFile, string password)
    {

        //generate random salt
        byte[] salt = GenerateRandomSalt();

        //create output file name
        using (FileStream fsCrypt = new FileStream(inputFile + ".aes", FileMode.Create))
        {
            //convert password string to byte arrray
            byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);

            //Set Rijndael symmetric encryption algorithm
            using (AesManaged AES = new  AesManaged())
            {
                AES.KeySize = 256;
                AES.BlockSize = 128;
                AES.Padding = PaddingMode.None;

                //http://stackoverflow.com/questions/2659214/why-do-i-need-to-use-the-rfc2898derivebytes-class-in-net-instead-of-directly
                //"What it does is repeatedly hash the user password along with the salt." High iteration counts.
                var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = key.GetBytes(AES.BlockSize / 8);

                //Cipher modes: http://security.stackexchange.com/questions/52665/which-is-the-best-cipher-mode-and-padding-mode-for-aes-encryption
                AES.Mode = CipherMode.CBC;

                // write salt to the begining of the output file, so in this case can be random every time
                fsCrypt.Write(salt, 0, salt.Length);

                using (CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write))
                {

                    using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
                    {

                        //create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
                        byte[] buffer = new byte[1048576];
                        int read;

                        try
                        {
                            while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                // Application.DoEvents(); // -> for responsive GUI, using Task will be better!
                                cs.Write(buffer, 0, read);

                            }

                            // Close up
                            fsIn.Close();
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Error: " + ex.Message);
                        }
                        finally
                        {
                            if (!cs.HasFlushedFinalBlock)
                                cs.FlushFinalBlock();
                            cs.Close();
                            fsCrypt.Close();
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// Decrypts an encrypted file with the FileEncrypt method through its path and the plain password.
    /// </summary>
    /// <param name="inputFile"></param>
    /// <param name="outputFile"></param>
    /// <param name="password"></param>
    public static void FileDecrypt(string inputFile, string outputFile, string password)
    {
        byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
        byte[] salt = new byte[32];

        using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
        {
            fsCrypt.Read(salt, 0, salt.Length);

            using (AesManaged AES = new AesManaged ())
            {
                AES.KeySize = 256;
                AES.BlockSize = 128;
                var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = key.GetBytes(AES.BlockSize / 8);
                AES.Padding = PaddingMode.PKCS7;
                AES.Mode = CipherMode.CBC;



                using (CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read))
                {

                    using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
                    {
                        int read;
                        byte[] buffer = new byte[1048576];
                        try
                        {                            
                            while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                //Application.DoEvents();
                                fsOut.Write(buffer, 0, read);
                                //if (!cs.HasFlushedFinalBlock)
                                    cs.FlushFinalBlock();
                            }                          
                        }
                        catch (CryptographicException ex_CryptographicException)
                        {
                            Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Error: " + ex.Message);
                        }
                        try
                        {
                            cs.Close();
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Error by closing CryptoStream: " + ex.Message);
                        }
                        finally
                        {
                            fsOut.Close();
                            fsCrypt.Close();
                        }
                    }
                }
            }
        }
    }

}

calling methods Encryption

       string password = "ThePasswordToDecryptAndEncryptTheFile";

        // For additional security Pin the password of your files
        GCHandle gch = GCHandle.Alloc(password, GCHandleType.Pinned);

        // Encrypt the file
        EncryptionDecryption.FileEncrypt(inputFilePath, password);

        // To increase the security of the encryption, delete the given password from the memory !
        EncryptionDecryption.ZeroMemory(gch.AddrOfPinnedObject(), password.Length * 2);
        gch.Free();

Decryption

GCHandle gch2 = GCHandle.Alloc(password, GCHandleType.Pinned);

        // Decrypt the file
        EncryptionDecryption.FileDecrypt(encryptedFilePath, outputPath, password);

        // To increase the security of the decryption, delete the used password from the memory !
        EncryptionDecryption.ZeroMemory(gch2.AddrOfPinnedObject(), password.Length * 2);
        gch2.Free();
John Kuriakose
  • 4,065
  • 2
  • 13
  • 20
  • 1
    The error in the title usually indicates that the key/iv pair used to encrypt does not match the key/iv pair used to attempt the decrypt. – Kevin Dec 28 '17 at 21:01
  • 1
    I do notice though, in Encrypt you are setting PaddingMode to None and in Decrypt setting it to PKCS7. – Kevin Dec 28 '17 at 21:12
  • Not sure I'd try setting all of the parameters either. AES is always CBC, Padding Mode defaults to PKCS7, the block size is always 128, and the key size will set itself to the size of any valid key you use (valid sizes are 128, 192, and 256). – Kevin Dec 28 '17 at 21:17
  • Well I tried to keep all the setting same for encryption and decryption. But it didnt work out. I got it resolved by removing AES properties so that it takes default and just keeping which are required. AES.KeySize = 256; AES.BlockSize = 128; var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000); AES.Key = key.GetBytes(AES.KeySize / 8); AES.IV = key.GetBytes(AES.BlockSize / 8); – John Kuriakose Dec 28 '17 at 22:51

1 Answers1

0

I got it resolved by removing all the AES properties to default and just keeping few. Find the code below. Specially the padding fields.

public static void FileEncrypt(string inputFile, string outputFile, string password, byte[] salt)
    {
        try
        {
            using (RijndaelManaged AES = new RijndaelManaged())
            {
                byte[] passwordBytes = ASCIIEncoding.UTF8.GetBytes(password);

                AES.KeySize = 256;
                AES.BlockSize = 128;

                var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = key.GetBytes(AES.BlockSize / 8);



                /* This is for demostrating purposes only. 
                 * Ideally you will want the IV key to be different from your key and you should always generate a new one for each encryption in other to achieve maximum security*/
                //byte[] IV = ASCIIEncoding.UTF8.GetBytes(skey);

                using (FileStream fsCrypt = new FileStream(outputFile, FileMode.Create))
                {
                    using (ICryptoTransform encryptor = AES.CreateEncryptor(AES.Key, AES.IV))
                    {
                        using (CryptoStream cs = new CryptoStream(fsCrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
                            {
                                int data;
                                while ((data = fsIn.ReadByte()) != -1)
                                {
                                    cs.WriteByte((byte)data);
                                }
                                if (!cs.HasFlushedFinalBlock)
                                    cs.FlushFinalBlock();
                            }
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
    public static void FileDecrypt(string inputFile, string outputFile, string password, byte[] salt)
    {
        try
        {
            using (RijndaelManaged AES = new RijndaelManaged())
            {
                //byte[] key = ASCIIEncoding.UTF8.GetBytes(password);

                /* This is for demostrating purposes only. 
                 * Ideally you will want the IV key to be different from your key and you should always generate a new one for each encryption in other to achieve maximum security*/
                //byte[] IV = ASCIIEncoding.UTF8.GetBytes(password);

                byte[] passwordBytes = ASCIIEncoding.UTF8.GetBytes(password);

                AES.KeySize = 256;
                AES.BlockSize = 128;

                var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = key.GetBytes(AES.BlockSize / 8);

                using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
                {
                    using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
                    {
                        using (ICryptoTransform decryptor = AES.CreateDecryptor(AES.Key, AES.IV))
                        {
                            using (CryptoStream cs = new CryptoStream(fsCrypt, decryptor, CryptoStreamMode.Read))
                            {
                                int data;
                                while ((data = cs.ReadByte()) != -1)
                                {
                                    fsOut.WriteByte((byte)data);
                                }
                                if (!cs.HasFlushedFinalBlock)
                                    cs.FlushFinalBlock();
                            }
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            // failed to decrypt file
        }
    }

}
John Kuriakose
  • 4,065
  • 2
  • 13
  • 20