0

After decrypting the file with the AES algorithm, I'm getting this message:

Image of message - The file cannot be opened because there are problems with the contents

But when I click OK, the file is opening propely without any loss of data.

It makes the user think there is something wrong with the file.

My code for encryption and decryption :

    /// <summary>
    /// AES strong encryption logic used
    /// </summary>
    /// <param name="encfilepath">file path which need to encrypt</param>
    /// <param name="enckey">key used to decrypt</param>
    public static void AESEncryptFile(string encfilepath, byte[] enckey)
    {
        byte[] bytesToBeEncrypted = System.IO.File.ReadAllBytes(System.Security.SecurityElement.Escape(encfilepath));

        // Hash the password with SHA256
        byte[] passwordBytes = SHA256.Create().ComputeHash(enckey);

        //create the new folder to for decryption
        var decryptedFilePath = Path.GetDirectoryName(encfilepath) + @"\" + Guid.NewGuid() + @"\" + Path.GetFileName(encfilepath);
        if (!Directory.Exists(Path.GetDirectoryName(decryptedFilePath)))
            Directory.CreateDirectory(Path.GetDirectoryName(decryptedFilePath));

        //decrypt the plain file and store in decryption folder
        AES_Encrypt(encfilepath, decryptedFilePath, passwordBytes);

        //move the file from decryption folder to actual folder
        if (File.Exists(encfilepath))
            File.Delete(encfilepath);
        File.Move(decryptedFilePath, encfilepath);
        Directory.Delete(Path.GetDirectoryName(decryptedFilePath));
    }

    /// <summary>
    /// AES strong encryption logic used
    /// </summary>
    /// <param name="inputFile">this is plain file path which want to encrypt</param>
    /// <param name="outputFile">this is output file where encrypt file need to save</param>
    /// <param name="passwordBytes">password byte</param>
    private static void AES_Encrypt(string inputFile, string outputFile, byte[] passwordBytes)
    {
        byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        string cryptFile = outputFile;
        FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);

        RijndaelManaged AES = new RijndaelManaged();

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


        var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
        AES.Key = key.GetBytes(AES.KeySize / 8);
        AES.IV = key.GetBytes(AES.BlockSize / 8);
        AES.Padding = PaddingMode.Zeros;

        AES.Mode = CipherMode.CBC;

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

        FileStream fsIn = new FileStream(inputFile, FileMode.Open);

        int data;
        while ((data = fsIn.ReadByte()) != -1)
            cs.WriteByte((byte)data);


        fsIn.Close();
        cs.Close();
        fsCrypt.Close();

    }


    /// <summary>
    /// AES strong encryption logic used
    /// </summary>
    /// <param name="encfilepath">file path which need to encrypt</param>
    /// <param name="enckey">key used to decrypt</param>

    /// <summary>
    /// Decrypt the encrypted file file using AES algorithm
    /// </summary>
    /// <param name="encryptedFilePath">Selected encrypted file</param>
    /// <param name="decryptFilePath">Decrypt file path</param>
    /// <param name="userFileName">Selected encrypted file user file name</param>
    /// <returns>Value of bool - File decrypted status</returns>
    //public bool DecryptFile(string encryptedFilePath, string decryptFilePath, string userFileName)
    public static bool AESDecryptFile(string encryptedFilePath, out string decryptFilePath, byte[] encryptionKey)
    {
        var isDecrypted = false;
        try
        {
            decryptFilePath = Path.GetDirectoryName(encryptedFilePath) + @"\" + Guid.NewGuid() + @"\" + Path.GetFileName(encryptedFilePath);
            if (!Directory.Exists(Path.GetDirectoryName(decryptFilePath)))
                Directory.CreateDirectory(Path.GetDirectoryName(decryptFilePath));
            var passwordBytes = SHA256.Create().ComputeHash(encryptionKey);
            AES_Decrypt(encryptedFilePath, decryptFilePath, passwordBytes);
            isDecrypted = true;
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return isDecrypted;
    }

    /// <summary>
    /// AES strong encryption logic used
    /// </summary>
    /// <param name="inputFile">this is cypertext file path which want to decrypt</param>
    /// <param name="outputFile">this is output file where decrypted file need to save</param>
    /// <param name="passwordBytes">password byte</param>
    public static void AES_Decrypt(string inputFile, string outputFile, byte[] passwordBytes)
    {

        byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);

        RijndaelManaged AES = new RijndaelManaged();

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


        var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
        AES.Key = key.GetBytes(AES.KeySize / 8);
        AES.IV = key.GetBytes(AES.BlockSize / 8);
        AES.Padding = PaddingMode.Zeros;

        AES.Mode = CipherMode.CBC;

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

        FileStream fsOut = new FileStream(outputFile, FileMode.Create);

        int data;
        while ((data = cs.ReadByte()) != -1)
            fsOut.WriteByte((byte)data);

        fsOut.Close();
        cs.Close();
        fsCrypt.Close();

    }

I have reused this code : (AES encryption on large files)

Draken
  • 3,134
  • 13
  • 34
  • 54
ManiMaranVasan
  • 83
  • 1
  • 10
  • 2
    Compare the original and the decrypted file. I suspect BOM encoding isn't perfect and that you'll be getting 2 alien characters at the start of the file. Just a theory though :) – Davesoft Aug 29 '18 at 15:45
  • 5
    That's not great code, it works a byte at a time, does not dispose anything, uses a non-random salt & using part of the key as the IV is a bad idea for several reasons, not least because it makes the output deterministic. I would suggest finding a better example before continuing. – Alex K. Aug 29 '18 at 15:57
  • Chained Block encryptions like AES pad out the final block to be encrypted with 0s. The decryption does not automatically remove these padding bytes. I'll bet the result of your decryption ends with padding bytes that Word does not like. – Kevin Aug 29 '18 at 16:06
  • 2
    @Kevin - no, providing you're using things sensibly, the decryption code *should* be automatically removing the padding. The padding mechanism is designed so that the padding in the final block can be identified, validated and stripped. – Damien_The_Unbeliever Aug 29 '18 at 16:18
  • @Damien_The_Unbeliever, correct, but you have to add code to remove the padding after the decrypt, the .Net crypto classes do not do it for you. If you use the old COM CryptoAPI, it removes the padding. – Kevin Aug 29 '18 at 18:00
  • 1
    @Kevin - yes, the .NET crypto classes absolutely *do* do this for you. Where have you come by this odd belief? Crypto libraries that add padding during encryption but fail to strip padding during decryption are broken by design. – Damien_The_Unbeliever Aug 29 '18 at 18:18
  • @Damien_The_Unbeliever I've been using the AESCryptoServiceProvider class since it was added in .Net 3.5 and if I check the resulting byte array after a decrypt it always has a number of 0 bytes at the end that I strip off. I've tried the same with the class returned by the AES.Factory in .Net Core and had to do the stripping myself. – Kevin Aug 29 '18 at 18:33
  • 1
    And is that because a) the libraries are flawed or b) because you're failing to check return values that tell you how much of the buffer contains valid data? I've used the crypto libraries too, in their various forms for years, and I've never encountered the situation you're describing. – Damien_The_Unbeliever Aug 29 '18 at 18:41
  • 1
    @Kevin - also, here's the thing. Pure zero padding isn't *possible* for an application to deal with. Because crypto libraries are designed to transfer exactly what you put in. If what you're sending is 3 `0` bytes, what you get out at the other end should be 3 `0` bytes. That's why the crypto library has to deal with padding/stripping - because it does more than just adding `0`s so that genuine trailing `0`s are distinguishable. – Damien_The_Unbeliever Aug 29 '18 at 18:48
  • @Damien_The_Unbeliever: There is this line in the code above: `AES.Padding = PaddingMode.Zeros;`. It is padded with zeros, which is not sensible of course. – President James K. Polk Aug 29 '18 at 21:25
  • In addition to the other problems mentioned by the commenters, it is a bug to use `PaddingMode.Zeros` because zero padding cannot be reliably removed: see Damien_The_Unbeliever's comment. Use `PaddingMode.PKCS7`. Also, for better performance, reliability, and simplicity, replace the read/write loops with a single call to `Stream.CopyTo()` and replace the calls to `Stream.Close()` with nested `using` statements. – President James K. Polk Aug 29 '18 at 21:43
  • @James : I have changes the code with `PaddingMode.PKCS7` and changed to using blocks, but since we are using .Net 3.5 `Stream.CopyTo` is not avaible so i have gone with loop for now. but after this changes im not getting proper decrypted file, its corrupted. Please post any example code for this issue – ManiMaranVasan Aug 31 '18 at 06:45
  • Hi Everyone, i agree the comment above, Please make sure the below code is fine for encryption. https://stackoverflow.com/a/32437759/4196957 – ManiMaranVasan Sep 07 '18 at 11:36

2 Answers2

1

Edit : This will not work for file of 500MB or more.

I used AES encryption last week in C#. I used this example :

https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=netframework-4.7.2

Here is my code :

Using the method to encrypt :

string password = "password";
using (Aes aes = Aes.Create())
        {
           //Encrypt the string
           byte[] encrypted = EncryptStringToBytes_Aes(password, aes.Key, aes.IV);
           //Decrypt the string
           string roundtrip = DecryptStringFromBytes_Aes(encrypted, aes.Key, aes.IV);
           //Convert the key, iv and encrypted message to base 64 string to get them in text
           Convert.ToBase64String(encrypted)
        }

Two methods for encrypting/decrypting :

static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
    {
        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");
        byte[] encrypted;

        // Create an Aes object
        // with the specified key and IV.
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;

            // Create an encryptor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }


        // Return the encrypted bytes from the memory stream.
        return encrypted;

    }

    static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
    {
        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        // Create an Aes object
        // with the specified key and IV.
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;

            // Create a decryptor to perform the stream transform.
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }

        }

        return plaintext;

    }
Lap
  • 31
  • 10
  • That code needs some tweaking to work in *production* (or a lot, it depends on who you talk to). But moving from `RijndaelManaged` to `AesManaged` is probably a good idea. I would agree that you should put here your own version of that code, since link-only (also, "external") answers are out of scope. – Jimi Aug 29 '18 at 16:40
  • 1
    @LapX: The link which you send will not work for large files, it will give MemoryOutOfExecption. But my scenario, i need to encrypt with 500MB above files – ManiMaranVasan Aug 29 '18 at 16:54
  • I used it to encrypt a password, not a whole file. I'll still post my version of the code. I did not know about the size restriction. – Lap Aug 29 '18 at 17:07
0

i agreed the comment for my post.

And finally i have used the following encryption logic. And its working fine for me.

AES encryption on large files

Please comment if there is any issue on this logic.

ManiMaranVasan
  • 83
  • 1
  • 10