How to convert CryptoJS decryption code into C#?

I have this code in CryptoJS, inside browser:

var decrypt = function (cipherText) {
    var key = "a_long_key_goes_here";
    var iv = "initial_vector_goes_here";

    key = CryptoJS.enc.Hex.parse(key);
    iv = CryptoJS.enc.Hex.parse(iv);

    var decrypted = CryptoJS.TripleDES.decrypt({
        ciphertext: CryptoJS.enc.Hex.parse(cipherText)
    }, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC
    });
    var clearText = decrypted.toString(CryptoJS.enc.Utf8);
    return clearText;
};

This code is not written by me. Also the cipherText come from another server that I have no access to. However, I have access to key and to iv.

I can decrypt that cipherText inside a browser's console. But I want to use these keys to decrypt that cipherText inside C# code. Here's the code I've written:

public void Desrypt()
{
    ICryptoTransform decryptor;
    UTF8Encoding encoder;
    string key = "a_long_key_goes_here";
    string iv = "initial_vector_goes_here";
    var cipherText = "cipher_text_goes_here";
    string clearText = "";

    byte[] cipherBytes = FromHexString(cipherText);
    using (Aes aes = Aes.Create())
    {
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(key, new byte[] { });
        aes.Key = pdb.GetBytes(32);
        aes.IV = pdb.GetBytes(16);
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
                cs.Close();
            }
            clearText = Encoding.Unicode.GetString(ms.ToArray());
        }
    }
    return clearText;
}

public static byte[] FromHexString(string hexString)
{
    var bytes = new byte[hexString.Length / 2];
    for (var i = 0; i < bytes.Length; i++)
    {
        bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
    }
    return bytes;
}

I have some problems though. I don't understand if I'm correctly decoding the given cipherText from hexadecimal or not. Also I can't instantiate Rfc2898DeriveBytes, because I don't know what the second parameter (salt) should be.

Also I don't know where should I use that iv I've gotten from the CryptoJS code.

Could you please help?

Upvotes: 1

Views: 176

Answers (1)

Topaco
Topaco

Reputation: 49525

So that both codes are compatible, the following changes of the C# code are necessary:

  • The return type of the Decrypt method must be changed from void to string.
  • Key and IV have to be decoded hexadecimal like the ciphertext with FromHexString.
  • Instead of AES, TripleDES must be used.
  • Rfc2898DeriveBytes implements PBKDF2 and must not be applied (since the JavaScript code does not use PBKDF2 either).
  • The decrypted data must not be decoded with Encoding.Unicode (which corresponds to UTF16LE in .NET), but with Encoding.UTF8.

The C# code can handle 24 bytes keys (to support 3TDEA) and 16 bytes keys (to support the less secure 2TDEA). The posted CryptoJS code also handles these key sizes plus additionally 8 bytes keys (to support the least secure, DES compatible variant 1TDEA).

The following C# code decrypts a ciphertext generated with CryptoJS and 3TDEA:

public string Decrypt() 
{
    byte[] key = FromHexString("000102030405060708090a0b0c0d0e0f1011121314151617"); // 24 bytes (3TDEA)
    byte[] iv = FromHexString("0001020304050607"); // 8 bytes
    byte[] ciphertext = FromHexString("2116057c372e0e95dbe91fbfd148371b8e9974187b71e7c018de89c757280ad342d4191d29472040ee70d19015b025e1"); 

    string plaintext = "";
    using (TripleDES tdes = TripleDES.Create())    
    {
        tdes.Key = key;
        tdes.IV = iv;
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, tdes.CreateDecryptor(tdes.Key, tdes.IV), CryptoStreamMode.Write))
            {
                cs.Write(ciphertext, 0, ciphertext.Length);
            }
            plaintext = Encoding.UTF8.GetString(ms.ToArray()); 
        }
    }
    return plaintext;
}

The decryption is also possible with the posted JavaScript code, which shows the functional equivalence of both codes.

Note: Since AES is more performant than TripleDES, AES should be used if possible.

Upvotes: 2

Related Questions