1

I'm trying to encrypt jpeg on server and decrypt it on browser like below but failed at step #3.

  1. Encrypt jpeg by C# on server
  2. Get encrypted data, vector, and passphrase on client browser
  3. Decrypt on client browser

I tried to things below, but nothing helped.

  1. use RijndaelManaged instead of AesManaged on server.
  2. use 'CryptoJS.enc.Utf16.parse' instead of 'CryptoJS.enc.Utf8.parse' on client.

Encryption on Server

public byte[] Encrypt(byte[] bytes, string password, string vector)
{
    AesManaged aes = new AesManaged();
    aes.KeySize = _keySize;
    aes.BlockSize = _blockSize;
    aes.Mode = CipherMode.CBC;
    aes.IV = Encoding.UTF8.GetBytes(vector);
    aes.Key = Encoding.UTF8.GetBytes(password);
    aes.Padding = PaddingMode.PKCS7;

    byte[] encrypted = aes.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);

    return encrypted;
}

Decryption on Client

// These values are same as above
var encrypted = ... //  byte[]
var vector = ... // string
var password = ... // string

var cipherParams = CryptoJS.lib.CipherParams.create({
    iv: CryptoJS.enc.Utf8.parse(vector),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});
var cipherText = CryptoJS.lib.WordArray.create(encrypted);
var passwordWordArray = CryptoJS.enc.Utf8.parse(password);
var decrypted = CryptoJS.AES.decrypt(cipherText, passwordWordArray, cipherParams);
// decrypted.words is empty here

Please advise.

I'm using .Net Core MVC 2.1, Crypto-JS 3.1.9-1, and Chrome74.0.3729.169 on Windows 10.

THIS PROBLEM RESOLVED

As Topaco mentioned, there was a flaw in javascript. The corrected code is as below.

var encrypted = ... //  byte[]
var vector = ... // string
var password = ... // string

var cipherParams = CryptoJS.lib.CipherParams.create({
    iv: CryptoJS.enc.Utf8.parse(vector),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});
var cipherText = CryptoJS.lib.WordArray.create(encrypted);
var cipherTextParam = CryptoJS.lib.CipherParams.create({
            ciphertext: cipherText
        });
var passwordWordArray = CryptoJS.enc.Utf8.parse(password);
var decrypted = CryptoJS.AES.decrypt(cipherTextParam, passwordWordArray, cipherParams);

Thank you for your help.

smk
  • 11
  • 2
  • Check for AES padding related questions, they have problems where OP has trouble decrypting because the data being padded. – Cleptus Jun 08 '19 at 11:53
  • Have you heard of SSL ? – Avin Kavish Jun 08 '19 at 12:29
  • The easy way would be to require https.... Any other scheme makes you 'hide' the key somewhere, there are no good solutions for that. – H H Jun 08 '19 at 12:40
  • Thank you for your comment, I will check about padding and try another padding option. – smk Jun 09 '19 at 05:19
  • I should have mentioned connection. I will use VPN, so connection will be secured. My goal is to store encrypted data on server and decrypt it on client. It is not to secure connection by AES. – smk Jun 09 '19 at 05:31

1 Answers1

1

There are two flaws in the JavaScript-code that can be corrected as follows:

  • In the JavaScript-code the line:

    var cipherText = CryptoJS.lib.WordArray.create(encrypted);
    

    must be replaced by:

    var cipherText = byteArrayToWordArray(encrypted); 
    

    Here, the function byteArrayToWordArray is used:

    function byteArrayToWordArray(ba) {
        var wa = [], i;
        for (i = 0; i < ba.length; i++) 
            wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
        return CryptoJS.lib.WordArray.create(wa, ba.length);
    }
    

    This function generates a word-array from the byte-array by generating a word (4 bytes) from 4 bytes of the byte-array. In the old code, a word of the same value is generated for each byte, i.e. both arrays have the same number of elements, which is wrong.

    Alternatively:

    var cipherTextHex = bytesToHex(encrypted);
    var cipherText = CryptoJS.enc.Hex.parse(cipherTextHex);
    

    can also be used. Here, the function bytesToHex is used:

    function bytesToHex(bytes) {
        for (var hex = [], i = 0; i < bytes.length; i++) {
            var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
            hex.push((current >>> 4).toString(16));
            hex.push((current & 0xF).toString(16));
        }
        return hex.join("");
    }
    

    The function generates a hex-string from the byte-array. From this a word-array is derived using the appropriate encoder.

  • In the JavaScript-code, the line:

    var decrypted = CryptoJS.AES.decrypt(cipherText, passwordWordArray, cipherParams);
    

    must be replaced by:

    var cipherParamsCipherText = CryptoJS.lib.CipherParams.create({
        ciphertext: cipherText
    });
    var decrypted = CryptoJS.AES.decrypt(cipherParamsCipherText, passwordWordArray, cipherParams);
    

    since the decrypted-function expects a CipherParams-object as the first argument instead of a WordArray.

    Alternatively, a Base64-encoded string can also be passed:

    var cipherTextB64Enc = CryptoJS.enc.Base64.stringify(cipherText);   
    var decrypted = CryptoJS.AES.decrypt(cipherTextB64Enc, passwordWordArray, cipherParams);
    
  • Test: The C#-code provides for the following input:

    byte[] bytes = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
    string password = "0123456789012345"; // 16 byte -> AES-128
    string vector = "5432109876543210";   // 16 byte
    

    the following byte-array as ciphertext:

    170, 27, 161, 209, 42, 247, 234, 191, 38, 167, 22, 74, 34, 139, 115, 0, 75, 207, 119, 161, 97, 142, 179, 93, 41, 12, 177, 128, 52, 151, 75, 231, 76, 157, 14, 197, 59, 111, 63, 206, 136, 218, 189, 244, 116, 43, 25, 20
    

    If the modified JavaScript-code is tested with those data:

    var encrypted = [170,27,161,209,42,247,234,191,38,167,22,74,34,139,115,0,75,207,119,161,97,142,179,93,41,12,177,128,52,151,75,231,76,157,14,197,59,111,63,206,136,218,189,244,116,43,25,20]; // byte[]
    var vector = "5432109876543210"    // string
    var password = "0123456789012345"; // string
    

    it is decrypted correctly.

    In the test AES-128 has been used. It can be switched to AES-256 by simply using a 32-byte key instead of a 16-byte key.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thank you so much for detailed explanation. Your second point helped! Actually, I tried "byteArrayToWordArray" before and observed that the function and "CryptoJS.lib.WordArray.create" returns the same result. – smk Jun 09 '19 at 09:30