I'm trying to encrypt a tif file using RijndaelManaged. Initially I was getting "Padding is invalid and cannot be removed" error. Got that fixed by adding FlushFinalBlock() to my Encryption method. Now, however, when I try to decrypt a file that has been encrypted in a different application (they both use the same class to encrypt and decrypt) I'm getting an error "Length of data to decrypt is invalid".

The code below is from a class that is compiled into a library that both applications use.
In both the Decrypt and Encrypt methods below, the parameter arg is the .tif file. Maybe I should be using a different Mode or Padding than the default. Sure would appreciate some help.

TIA, edepperson

private byte[] DecryptRijndaelManaged(string inputkey, string saltval, byte[] arg)
{
    byte[] earg = null;
    initializeRJ(inputkey, saltval);
    ICryptoTransform decryptor = alg.CreateDecryptor();
    CryptoStream decryptstream = null;
    using (MemoryStream ms = new MemoryStream(arg))
    {
        int argL = arg.Length;
        earg = new byte[argL];
        try
        {
            decryptstream = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
            ms.Seek(0, SeekOrigin.Begin);
            decryptstream.Read(earg, 0, earg.Length);
        }
        finally
        {
            decryptstream.Close();
            ms.Close();
        }
    }
    return earg;
}

private byte[] EncryptRijndaelManaged(string inputkey, string saltval, byte[] arg)
{
    byte[] earg = null;
    initializeRJ(inputkey, saltval);
    using (MemoryStream ms = new MemoryStream())
    {
        ICryptoTransform encryptor = alg.CreateEncryptor();
        using (CryptoStream encryptstream = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            encryptstream.Write(arg, 0, arg.Length);
            encryptstream.FlushFinalBlock();
            int argL = (int)ms.Length;
            ms.Seek(0, SeekOrigin.Begin);
            earg = new byte[argL];
            ms.Read(earg, 0, argL);
            ms.Flush();
            encryptstream.Close();
        }
        ms.Close();
    }
    return earg;
}

private void initializeRJ(string inputkey, string saltval)
{
    alg = new RijndaelManaged();
    string pwd = inputkey;
    byte[] salt = Encoding.ASCII.GetBytes(saltval);
    Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(pwd, salt);
    alg.Key = key.GetBytes(alg.KeySize / 8);
    alg.IV = key.GetBytes(alg.BlockSize / 8);
}

Recommended Answers

All 10 Replies

In your Decrypt method you create a memory stream but there is nothing in this stream and you try to read it into your byte array.

No, this statement initializes a memorystream length and reads the buffer in:
using (MemoryStream ms = new MemoryStream(arg))
if you don't believe it, then try this...

using (MemoryStream ms = new MemoryStream(arg))
            {
                byte[] b = new byte[arg.Length];
                ms.Seek(0, SeekOrigin.Begin);
                ms.Read(b, 0, arg.Length);
            }

then inspect the buffer b

You are correct, I must not have been scrolling all the way back up to the top and was confusing the two MemoryStreams :) Let me look at it again (and you don't define alg in that code, but I'm sure I can figure out what it is :)

I've changed it a bit to have the key size set at exactly 256bits and the IV at 128. This helped when the encryption and decryption is done in the same application. But when the encryption is done in app1 and the decryption is done in app2, I get "length of data to decrypt is invalid" or the "Padding is invalid and cannot be removed".

Also, I've encluded the full class below.

public class Crypto
{
    const int bitsz256 = 32;
    const int bitsz128 = 16;

    private RijndaelManaged alg;

    public Crypto()
    { }

    private void initializeRJ(string inputkey, string saltval)
    {
        alg = new RijndaelManaged();
        alg.Key = getLegalKey(inputkey);
        alg.IV = getLegalIV(saltval);
        alg.Mode = CipherMode.CBC;
        alg.Padding = PaddingMode.PKCS7;
    }

    private byte[] getLegalKey(string inputkey)
    {
        // set key to 256 bits
        StringBuilder sb = new StringBuilder();
        string skey = "";
        do
        {
            sb.Append(inputkey);
        } while (sb.Length < bitsz256);

        skey = sb.ToString();
        return UTF8Encoding.UTF8.GetBytes(skey.Substring(0, bitsz256));

    }

    private byte[] getLegalIV(string inputIV)
    {
        //match IV size to key size
        StringBuilder sb = new StringBuilder();
        string sIV = "";
        do
        {
            sb.Append(inputIV);
        } while (sb.Length < bitsz128);

        sIV = sb.ToString();
        return UTF8Encoding.UTF8.GetBytes(sIV.Substring(0, bitsz128));
    }


    public byte[] EncryptDocument(string key, string saltval, string enctype, byte[] arg)
    {
        byte[] encarg = null;
        switch (enctype)
        {
            case ".rij":
                encarg = EncryptRijndaelManaged(key, saltval, arg);
                break;
        }


        return encarg;
    }

    private byte[] EncryptRijndaelManaged(string inputkey, string saltval, byte[] arg)
    {
        byte[] earg = null;
        initializeRJ(inputkey, saltval);
        using (MemoryStream ms = new MemoryStream())
        {
            ICryptoTransform encryptor = alg.CreateEncryptor(alg.Key, alg.IV);
            using (CryptoStream encryptstream = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            {
                encryptstream.Write(arg, 0, arg.Length);
                encryptstream.FlushFinalBlock();
                int argL = (int)ms.Length;
                ms.Seek(0, SeekOrigin.Begin);
                earg = new byte[argL];
                ms.Read(earg, 0, argL);
                ms.Flush();
                encryptstream.Close();
            }
            ms.Close();
        }
        return earg;
    }

    public bool DecryptDocument(ref string scanid, string key, string saltval, ref byte[] arg)
    {
        byte[] encarg = null;
        bool bdecrypt = false;
        string sext = scanid.Substring(scanid.Length - 4);
        switch (sext)
        {
            case ".rij":
                encarg = DecryptRijndaelManaged(key, saltval, arg);
                arg = new byte[encarg.Length];
                Array.Copy(encarg, arg, encarg.Length);
                scanid = scanid.Remove(scanid.Length - 4);
                bdecrypt = true;
                break;
            default:
                break;
        }
        return bdecrypt;
    }

    private byte[] DecryptRijndaelManaged(string inputkey, string saltval, byte[] arg)
    {
        byte[] earg = null;
        initializeRJ(inputkey, saltval);
        ICryptoTransform decryptor = alg.CreateDecryptor(alg.Key, alg.IV);
        CryptoStream decryptstream = null;
        using (MemoryStream ms = new MemoryStream(arg))
        {
            int argL = (int)ms.Length;
            earg = new byte[argL];
            try
            {
                decryptstream = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
                ms.Seek(0, SeekOrigin.Begin);
                
                decryptstream.Read(earg, 0, argL);                    
            }
            finally
            {
                decryptstream.Close();
            }
        }
        return earg;
    }
}

ms.Read(earg, 0, argL); Why are you reading from the MemoryStream in the encrypt method? Shouldn't you be writing to it?

I created these two methods to see if I could reproduce your error, which leads me to believe your manipulation of the MemoryStream is the problem. I don't manipulate it at all. Also, you don't need to close streams that you use the using clause on, that's why you use it :)

static byte[] Encrypt(String file, string password, byte[] salt) {
            RijndaelManaged rm = new RijndaelManaged();
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt);

            rm.Key = key.GetBytes(rm.KeySize / 8);
            rm.IV = key.GetBytes(rm.BlockSize / 8);

            string input = File.ReadAllText(file);

            ICryptoTransform encryptor = rm.CreateEncryptor();
            MemoryStream ms = new MemoryStream();
            using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) {
                using (StreamWriter sw = new StreamWriter(cs)) {
                    sw.Write(input);
                }
            }

            return ms.ToArray();
        }

        static string Decrypt(String file, string password, byte[] salt) {
            String text;

            RijndaelManaged rm = new RijndaelManaged();
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt);

            rm.Key = key.GetBytes(rm.KeySize / 8);
            rm.IV = key.GetBytes(rm.BlockSize / 8);

            byte[] input = File.ReadAllBytes(file);

            ICryptoTransform dencryptor = rm.CreateDecryptor();
            MemoryStream ms = new MemoryStream(input);
            using (CryptoStream cs = new CryptoStream(ms, dencryptor, CryptoStreamMode.Read)) {
                using (StreamReader sr = new StreamReader(cs)) {
                    text = sr.ReadToEnd();
                }
            }

            return text;
        }

I know that about the using clause, but by the time I brought it to Daniweb, I was coving every possible angle, including setting the defaults on the RijndaelManaged class to their defaults.

I noticed you went back to key.GetBytes(rm.KeySize / 8) & key.GetBytes(rm.BlockSize / 8)
I went to setting my key to 256 bits and the IV to 128. Thats seemed to work better for me.

But I know either should work.
I don't think its the memory stream, or it wouldn't work in either scenario I tried above. that is, it works not when the decrypt / encrypt is done in 1 application, but not when done in separate apps.

Which brings me to a couple of other issues. I wonder if trying to encrypt a .tif file is the problem. Four days on this blasted problem. I know more about RijndaelManaged than I ever wanted to ! :-))

I marked it sovlved because the problem seems to have gone away. But I'm not sure why.
Here are the last two changes I've made. As of right now, its handling all files correctly.

I went back to the more traditional method on the memory stream.
MemoryStream ms = new MemoryStream()
ms.Write(arg, 0, arg.Lenght);

The second change is where I implemented it in the application. Before, when I was having trouble, I was taking these steps.
1) load tif to buffer
2) copy buffer to transport class
3) copy buffer in transport to local buffer
4) encrypt local buffer
5) copy local buffer back to transport class
6) do the rest of the code.

Changed it to this.
1) load tif to local buffer
2) encrypt local buffer
3) write local buffer back to tif file
4) load (encrypted) tif to buffer
5) copy buffer to class
6) do the rest of my code.

Since there were two changes, I'm not sure which one resolved it, but I suspect it was where I implemented the class in the application.

Its all good now and thanks Momerath for taking the challenge of trying to help.

Hi edepperson

Can you please share your latest code with me.

Regards

its all here, in the class I wrote above. Just change this line in the DecryptRijndaelManaged from this:

    using (MemoryStream ms = new MemoryStream(arg))        
    {

to this:

    using (MemoryStream ms = new MemoryStream())
    {
        ms.Write(arg, 0, arg.Length);

and you'll have it

Thanks for your response

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.