6

I want to decrypt an Encrypted Sting using AES/CBC/Nopadding in c# Windows Phone 8 application. My string is in file of IsolatedSorage. I pasted the string HERE which is junk.

From this Article I am using AesManaged class to decrypt. But how to set padding to NoPadding because by default the padding set to PKCS7 from here.

        string fileName = "titlepage.xhtml";

        if (fileStorage.FileExists(fileName))
        {
            IsolatedStorageFileStream someStream = fileStorage.OpenFile(fileName, System.IO.FileMode.Open, FileAccess.Read);
            using (StreamReader reader = new StreamReader(someStream))
            {
                str1 = reader.ReadToEnd();

                MessageBox.Show(str1);

                try
                {
                    string text = Decrypt(str1, "****************", "****************");

                    MessageBox.Show(text);
                }
                catch (CryptographicException cryptEx)
                {
                    MessageBox.Show(cryptEx.Message, "Encryption Error", MessageBoxButton.OK);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "General Error", MessageBoxButton.OK);
                }
            }
        }

    public string Decrypt(string dataToDecrypt, string password, string salt)
    {
        AesManaged aes = null;
        MemoryStream memoryStream = null;

        try
        {
            //Generate a Key based on a Password and HMACSHA1 pseudo-random number generator
            //Salt must be at least 8 bytes long
            //Use an iteration count of at least 1000
            Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), 10000);               

            //Create AES algorithm
            aes = new AesManaged();
            //Key derived from byte array with 32 pseudo-random key bytes
            aes.Key = rfc2898.GetBytes(32);
            //IV derived from byte array with 16 pseudo-random key bytes
            aes.IV = rfc2898.GetBytes(16);

            //Create Memory and Crypto Streams
            memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write);
            
            byte[] data = Convert.FromBase64String(dataToDecrypt);
            cryptoStream.Write(data, 0, data.Length);
            cryptoStream.FlushFinalBlock();

            //Return Decrypted String
            byte[] decryptBytes = memoryStream.ToArray();

            //Dispose
            if (cryptoStream != null)
                cryptoStream.Dispose();

            //Retval
            return Encoding.UTF8.GetString(decryptBytes, 0, decryptBytes.Length);
        }
        finally
        {
            if (memoryStream != null)
                memoryStream.Dispose();

            if (aes != null)
                aes.Clear();
        }            
    }

Edit 1:

When I am decrypting my Encrypted string in thins line

 byte[] data = Convert.FromBase64String(dataToDecrypt);

Moving to Finally block and getting exception of The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters in decrypted string.

It is bit of confuse on this which is supported class to Decrypt in windows phone.

If I am completely wrong suggest me url of article regarding algorithm in Windows Phone

Edit 2:

As Below answer suggested " I am getting cyperText as bytes it is fine in decryption side. But it is giving an exception with the description

       [Cryptography_SSD_InvalidDataSize]
    Arguments: 
    Debugging resource strings are unavailable. Often the key and arguments provide 
sufficient information to diagnose the problem

I believe that problem is IV[salt key] or setting padding to AesManged. But I can't change padding property to AesManaged in Windows Phone. By default padding to AesManged is PKCS7. I want to change to NoPadding. Because my cyperText is encrypted using AES/CBC/NoPadding algorithm "

Community
  • 1
  • 1
Kumar
  • 864
  • 1
  • 22
  • 46

2 Answers2

7

If I understand the problem, you have data that is already encrypted in AES CBC mode, with no padding. But on the phone where you want to decrypt the data, the only option you have is PKCS#7 padding.

Well, you are in luck! You can decrypt the ciphertext using PKCS#7 padding. All you need to do is add the padding to the ciphertext, on the phone, and then decrypt it.

To add padding after the fact, you will encrypt a small bit of data and append it to the ciphertext. Then, you decrypt the modified ciphertext, and take that small bit of data off, and you have the original plaintext.

Here is how you do it:

  1. Take a ciphertext on the phone. This is a multiple of 16 bytes, even if there is no padding. There is no other possibility -- AES ciphertext is always a multiple of 16 bytes.

  2. Take the LAST 16 bytes of the ciphertext aside, and set that as the IV of your AES ENCRYPT. (Encrypt, not decrypt.) Use the same key as you are going to use to decrypt later.

  3. Now encrypt something smaller than 16 bytes, for example, the character '$'. The phone is going to add PKCS#7 padding to this.

  4. Append the resulting 16-bytes of ciphertext to the original ciphertext from step 1, and you now have a properly PKCS#7-padded ciphertext which includes the original plaintext plus the added '$'.

  5. Use the original IV, and the same key, and now DECRYPT this combined ciphertext. You can now remove the '$' that will appear at the end of your plaintext (or whatever you added in step 3.)

When the small bit is encrypted with the last 16-bytes of the original ciphertext, you are actually extending the ciphertext in true AES CBC mode, and you happen to be doing that with PKCS#7 padding, so you can now decrypt the whole thing and take the small bit off. You will have the original plaintext which had no padding.

I thought this would be interesting to show in code:

var rfc2898 = new Rfc2898DeriveBytes("password", new byte[8]);

using (var aes = new AesManaged())
{
    aes.Key = rfc2898.GetBytes(32);
    aes.IV = rfc2898.GetBytes(16);

    var originalIV = aes.IV; // keep a copy

    // Prepare sample plaintext that has no padding
    aes.Padding = PaddingMode.None;
    var plaintext = Encoding.UTF8.GetBytes("this plaintext has 32 characters");
    byte[] ciphertext;
    using (var encryptor = aes.CreateEncryptor())
    {
        ciphertext = encryptor.TransformFinalBlock(plaintext, 0, plaintext.Length);
        Console.WriteLine("ciphertext: " + BitConverter.ToString(ciphertext));
    }

    // From this point on we do everything with PKCS#7 padding
    aes.Padding = PaddingMode.PKCS7;

    // This won't decrypt -- wrong padding
    try
    {
        using (var decryptor = aes.CreateDecryptor())
        {
            var oops = decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length);
        }
    }
    catch (Exception e)
    {
        Console.WriteLine("caught: " + e.Message);
    }

    // Last block of ciphertext is used as IV to encrypt a little bit more
    var lastBlock = new byte[16];
    var modifiedCiphertext = new byte[ciphertext.Length + 16];

    Array.Copy(ciphertext, ciphertext.Length - 16, lastBlock, 0, 16);
    aes.IV = lastBlock;

    using (var encryptor = aes.CreateEncryptor())
    {
        var dummy = Encoding.UTF8.GetBytes("$");
        var padded = encryptor.TransformFinalBlock(dummy, 0, dummy.Length);

        // Set modifiedCiphertext = ciphertext + padded
        Array.Copy(ciphertext, modifiedCiphertext, ciphertext.Length);
        Array.Copy(padded, 0, modifiedCiphertext, ciphertext.Length, padded.Length);
        Console.WriteLine("modified ciphertext: " + BitConverter.ToString(modifiedCiphertext));
    }

    // Put back the original IV, and now we can decrypt...
    aes.IV = originalIV;

    using (var decryptor = aes.CreateDecryptor())
    {
        var recovered = decryptor.TransformFinalBlock(modifiedCiphertext, 0, modifiedCiphertext.Length);
        var str = Encoding.UTF8.GetString(recovered);
        Console.WriteLine(str);

        // Now you can remove the '$' from the end
    }
}
Jim Flood
  • 8,144
  • 3
  • 36
  • 48
  • Hi, FOA I have to apologize for very late reply. Meanwhile I'm checking with different cases on your suggestion cause It is not working for my cyperText. You can check my cypertext in my question which I gave link. It is giving same text after Decryption. But For normal text(the example what you gave) it is working fine. Is anything missing here...? – Kumar Jul 02 '14 at 07:34
  • I can't check your ciphertext as pasted text, without key, iv and plaintext. Encrypt some plaintext, and give the plaintext, key, iv and ciphertext of original, and ciphertext, key, iv and recovered plaintext on the other end. Any simple message and any random key would do to compare results. – Jim Flood Jul 03 '14 at 00:00
  • Hi.., Here are my Plain Text file & Encrypted File [ http://1drv.ms/1iZsJBB ] uploaded to OneDrive. I placed my IV & Key in Key.txt file which are used in encryption using AES/CBC/NoPadding algorithm . – Kumar Jul 03 '14 at 06:49
  • Here is decrypted Result which seems like Junk [http://1drv.ms/1o4nInB ]. Try to download if you want check the file. I used same IV & Key to Encryption & Decryption which is in txt file. If you want to know any more info about this, Please let me know – Kumar Jul 03 '14 at 13:30
  • I don't see what key and IV you used to get Encrypted.html from Normal.html. Your "key" (passphrase?) is 17 chars and your "IV" (salt?) is 16 chars. If I use Rfc2898 that derives key 00FF7C180B71058092974F31919B1266E367836B213C5B3E2F8B8E6765B7069E and IV AB286375D07B79C333F47E6A06748AD6 which don't produce the same result. I tried monkeying with the derivation and just using the raw bytes (padded with SPACE or NULL) and could not find a combination that matches your result. Find out exactly what 32 bytes of key and 16 bytes of IV the data is encrypted with. – Jim Flood Jul 03 '14 at 21:08
  • Oh my Bad.. It's my mistake. I supposed to create a new key for testing purpose. I think I misplaced a character in the Key while creating. & In general I am using 16 bytes of Key and 16 bytes of IV. Here are my new Normal File, Encrypted File, Decrypted Result file & the Key [ http://1drv.ms/1zbWXGp ] – Kumar Jul 04 '14 at 04:38
  • Nitpick: AES-CBC cyphertext is indeed always a multiple of 16 bytes. AES-CTR cyphertext may not be if the last counter block was not completely used. /Nitpick. – rossum Jul 04 '14 at 11:21
  • @JimFlood Hi, have you tried with My new files [ http://goo.gl/pcLAAX ], Still looking for answer... – Kumar Jul 06 '14 at 09:51
  • @JimFlood Hey.. I am trying in few scenarios.. But still I am getting Decrypted string coming as junk – Kumar Jul 08 '14 at 09:32
  • I'll take a look when I have a chance. Can you output the key as bytes on both ends: who encrypts, who decrypts, the actual bytes of the key. Your Key New shows 'Z9zyDkLQWinDowrt' but then is that 16 bytes of ASCII character codes, or 32 bytes of UTF-16 codes (big or little endian?) or is that given to a password-to-key derivation algorithm? I can see exactly what the C# code does above, but what does the encrypting code do, and is it the same key byte values? – Jim Flood Jul 08 '14 at 20:20
  • Ok.. My key is 16 bytes of UTF-8. BTW encryption did in PHP. You can refer the PHP code what they used in Encryption from HERE [ http://www.androidsnippets.com/encrypt-decrypt-between-android-and-php ] & They Used same key `Z9zyDkLQWinDowrt` in encryption for this HTML file – Kumar Jul 11 '14 at 11:41
1

The string you linked to is not Base-64. It looks as if it is raw encrypted bytes, interpreted as characters. Either work on the encryption side to output a Base-64 string encoding of the raw bytes or else work on the decryption side to read the cyphertext as raw bytes, not as text, and forget about removing the Base-64.

Generally better to work on the encryption side since passing Base-64 text is a lot less error-prone than passing raw bytes.

rossum
  • 15,344
  • 1
  • 24
  • 38
  • I changed in Decryption Side. But Not Succeed; Please, Look at my **Edit 2:** in question. – Kumar Jun 26 '14 at 11:45
  • Look at the C# documentation for `AESManaged`. You have a getter and a setter for the padding property. `PaddingMode.None` would appear to be what you need. – rossum Jun 26 '14 at 12:26
  • For Windows phone it is not supported. Look at the platforms list in AesManaged.Padding property documtation. [http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.padding.aspx#platformsTitleToggle ] – Kumar Jun 26 '14 at 12:29
  • Then you need to 1) make a change on the encryption side, 2) use a different platform on the decryption side or 3) build your own AES/CBC/NoPadding decode function from basics. If you have an AES/ECB mode that can process a single block then it is not too difficult. – rossum Jun 26 '14 at 13:12
  • Thank You for Suggestions. Let me try these Cases. But I can't change in Encryption Side cause of files are already encrypted. – Kumar Jun 26 '14 at 13:15