0

I have a client side file encryption in javascript using CryptoJs. I have a server side file decryption using RijndaelManaged. If I do both encryption and decryption using CryptoJs, it is working fine. However, when I try to decrypt using C# code, it is throwing the below error. I tried setting different paddings, modes, etc to no avail.

CryptographicException length of the data to decrypt is invalid

CRyptoJS code:

function encryptFile() {
selectedFiles = document.getElementById("MainContent_fileinput");

$.each(selectedFiles.files, function (i, file) {
    var reader = new FileReader();
    var strKey = " ";
    var strIv = " ";
    var byteArrKey = [169,204,147,221,70,76,207,92,102,12,237,65,5,205,34,106,178,141,138,117,224,153,37,124,54,17,74,223,224,153,72,209];
    var byteArrIV = [169,204,147,221,70,76,207,92,102,12,237,65,5,205,34,106];
    var byteVal;
    var byteValIv;

    reader.onloadend = function (e) {
        for (var i = 0; i < byteArrKey.length; i++) {
            byteVal = byteArrKey[i];
            if (byteVal < 16) { strKey += "0"; }
            strKey += byteVal.toString(16);
        };
        for (var i = 0; i < byteArrIV.length; i++) {
            byteValIv = byteArrIV[i];
            //if (byteValIv < 16) { strIv += "0"; }
            strIv += byteVal.toString(16);
        };
        var encrypted1 = CryptoJS.AES.encrypt(reader.result, strKey, { 'iv': strIv });
        //            var encrypted1 = CryptoJS.AES.encrypt(reader.result, key,
        //                                { keySize: 128 / 8, iv: iv1, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });

        var ct1 = encrypted1.toString();
        var encodedData1 = window.btoa(ct1);
        $.ajax({
            async: 'true',
            url: "MYWEBSERVICE URL",
            method: "POST",
            processData: 'false',
            headers: {
                'content-type': "application/x-www-form-urlencoded",
                'cache-control': "no-cache"
            },
            data: { 'folderPath': folderPath, 'uploadData': encodedData1, 'fileName': file.name + '.encrypted' },

            success: function (response) {
                debugger;
                console.log(response);
            },
            error: function (xhr, textStatus, error) {
                debugger;
                console.log(xhr.statusText);
            }
        });
    };
    reader.readAsDataURL(file);
})
}

Decryption using c#:

private static byte[] CreateKey(string pwd)
{
    byte[] bytKey;
    byte[] bytSalt = Encoding.ASCII.GetBytes(pwd);
    PasswordDeriveBytes pdb = new PasswordDeriveBytes(pwd, bytSalt);
    bytKey = pdb.GetBytes(32);
    return bytKey;
}

private static byte[] CreateIV(string pwd)
{
    byte[] bytIV;
    byte[] bytSalt = Encoding.ASCII.GetBytes(pwd);
    PasswordDeriveBytes pdb = new PasswordDeriveBytes(pwd, bytSalt);
    bytIV = pdb.GetBytes(16);
    return bytIV;
}       

private static bool DecryptFile(string strInputFile, string strOutputFile)
{
   bool returnValue = true;
FileStream fsInput = null;
FileStream fsOutput = null;
Int32 intBytesInCurrentBlock;
CryptoStream csCryptoStream = null;

byte[] bytBuffer;   // = new byte[fsInput.Length];

bytKey = CreateKey("123456");
bytIV = CreateIV("123456");

try
{

     using (var fsInput = File.OpenRead(strInputFile))
             using (var fsOutput = File.Create(strOutputFile))
             using (Aes aes = Aes.Create())
             using (ICryptoTransform decryptor = aes.CreateDecryptor(bytKey, bytIV))
             using (var decryptionStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
             using (var base64Decode = new FromBase64Transform())
             using (var cryptoStream = new CryptoStream(decryptionStream, base64Decode, CryptoStreamMode.Write))
             {
                 fsInput.CopyTo(cryptoStream);
                 cryptoStream.Dispose();
                 cryptoStream.FlushFinalBlock();
                 decryptionStream.Dispose();
                 decryptionStream.FlushFinalBlock();
             }
}
catch
{
    throw;
}
finally
{
    csCryptoStream.Close();
    fsInput.Close();
    fsOutput.Close();
}
return returnValue;
}

WebService method:

byte[] byteUploadFile = Convert.FromBase64String(uploadData);
BinaryWriter binWriter = new         BinaryWriter(File.Open(Path.Combine(folderPath, fileName), FileMode.Create, FileAccess.ReadWrite));
binWriter.Write(byteUploadFile);
binWriter.Close();
Noob
  • 57
  • 3
  • 12

2 Answers2

0

From javascript you seem to write to the file the output of window.btoa(ct1), which is Base64-encoded. In C# you read the contents of the file as binary data.

Easy to read:

string base64 = File.ReadAllText(strInputFile);
byte[] decoded = Convert.FromBase64String(base64);

using (Aes aes = Aes.Create())
using (ICryptoTransform decryptor = aes.CreateDecryptor(bytKey, bytIV))
using (var fsOutput = File.Create(strOutputFile))
using (var cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
{
    cryptoStream.Write(decoded, 0, decoded.Length);
}

Better performance (especially for large data):

using (var fsInput = File.OpenRead(strInputFile))
using (var fsOutput = File.Create(strOutputFile))
using (Aes aes = Aes.Create())
using (ICryptoTransform decryptor = aes.CreateDecryptor(bytKey, bytIV))
using (var decryptionStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
using (var base64Decode = new FromBase64Transform())
using (var cryptoStream = new CryptoStream(decryptionStream, base64Decode, CryptoStreamMode.Write))
{
    fsInput.CopyTo(cryptoStream);
}

In the second example the data flow is:

fsInput.CopyTo(cryptoStream) ->
    read some data from fsInput
    write data to cryptoStream
        Base64Decode the data in progress
        write decoded data to decryptionStream
            decrypt the data in progress
                write decrypted to fsOutput
    loop until reading says it's out of data.

Then on the } (calling Dispose on everyone in reverse order)

cryptoStream.Dispose() -> cryptoStream.FlushFinalBlock() ->
    base64Decode will throw if there's bad data remaining
decryptionStream.Dispose() -> decryptionStream.FlushFinalBlock() ->
    throw if padding is bad, otherwise write the final block to fsOutput
bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • So, I added the code per your suggestion, See the `DecryptFile()` in the original question. Now I get the **Padding is invalid and cannot be removed** error. – Noob Jan 17 '17 at 18:20
  • Also, I added the webservice mehtod I am using to create/save the file on server. Looks like the Bsae64 is being converted to Binary while writing to a file. – Noob Jan 17 '17 at 18:24
  • I also added this `aes.Padding = PaddingMode.Zeros;`. Now the error is gone. However, the decrypted file is not same as the original. Original file: 4KB, ecnrypted: 7KB, decrypted: 5KB. – Noob Jan 17 '17 at 18:36
  • AES-CBC only adds (or, for decryption, removes) up to 16 bytes. If it grew by 3KB (75% increase) clearly some other sort of transform is going on. Base64 is only a 33% increase (3 bytes => 4 characters). Your original exception is that you are decrypting data which is not a multiple of 16 bytes, meaning that AES-CBC encryption was not the last transform applied. – bartonjs Jan 18 '17 at 00:59
  • Can you tell me what is format of this string `var ct1 = encrypted1.toString();`? Is it Base64 encoded? Utf-8? – Noob Jan 18 '17 at 15:36
  • I am still stuck. – Noob Jan 23 '17 at 00:13
0

Resolved as below:

File encryption using CryptoJS:

function esp() {
    selectedFiles = document.getElementById("MainContent_file1");
    var sfile = selectedFiles.files[0];
    var read = new FileReader();
    read.onload = function (e) {
        var key = CryptoJS.enc.Utf8.parse('7061737323313233');
        var iv = CryptoJS.enc.Utf8.parse('7061737323313233');
        var encrypted = CryptoJS.AES.encrypt(reader.result, key, { keySize: 128 / 8, iv: iv,
            mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
        });
        var ct = encrypted.toString();
        debugger;
        $.ajax({
            async: 'true',
            url: "http://localhost:51936/WebService1.asmx/FileUpload",
            method: "POST",
            processData: 'false',
            headers: {
                'content-type': "application/json",
                'cache-control': "no-cache"
            },
            data: JSON.stringify({ 'folderPath': folderPath, 'uploadData': ct, 'fileName': sfile.name + '.encrypted' }),
            success: function (response) {
                console.log(response);
            },
            error: function (xhr, textStatus, error) {
                console.log(xhr.statusText);
            }
        });
    }
    read.readAsDataURL(sfile);
}

Decryption using C#:

  [WebMethod]
public void Decrypt(object sender, EventArgs e)
{
    string folderPath = "path";
    DirectoryInfo d = new DirectoryInfo(folderPath).GetDirectories().OrderByDescending(ds => ds.LastWriteTimeUtc).First();

    try
    {
        foreach (FileInfo file in d.GetFiles())
        {
            string plaintext = "";
            string filename = file.Name;
            byte[] cipherText = new byte[file.Length];
            FileStream fs = file.OpenRead();
            fs.Read(cipherText, 0, (int)file.Length);
            byte[] keybytes = Encoding.UTF8.GetBytes("7061737323313233");
            byte[] iv = Encoding.UTF8.GetBytes("7061737323313233");
            MyWebService.MyWebServicedts = new MyWebService.MyWebService();
            using (var rijAlg = new RijndaelManaged())
            {
                rijAlg.Mode = CipherMode.CBC;
                rijAlg.Padding = PaddingMode.PKCS7;
                rijAlg.FeedbackSize = 128;

                rijAlg.Key = keybytes;
                rijAlg.IV = iv;
                var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
                using (var msDecrypt = new MemoryStream(cipherText))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (var srDecrypt = new StreamReader(csDecrypt))
                        {
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
            plaintext = plaintext.Substring(23);
            string name = filename.Substring(filename.LastIndexOf("/") + 1);
            name = name.Replace(".encrypted", "");
            dts.FileUpload(folderPath, plaintext, name);
        }
    }
    catch (Exception ex)
    {
        string err = ex.Message;
    }
}

Webservice to save data as file on server:

byte[] byteUploadFile = Convert.FromBase64String(uploadData);
BinaryWriter binWriter = new         BinaryWriter(File.Open(Path.Combine(folderPath, fileName), FileMode.Create, FileAccess.ReadWrite));
binWriter.Write(byteUploadFile);
binWriter.Close();
Noob
  • 57
  • 3
  • 12