Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decrypt string using AES/CBC/NoPadding algorithm

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 "

like image 992
Kumar Avatar asked Jun 25 '14 09:06

Kumar


1 Answers

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
    }
}
like image 116
Jim Flood Avatar answered Sep 29 '22 03:09

Jim Flood