1

Why do I get different result in my encryption on TriplesDes using c# and JavaScript cryptojs? Please see my code below.

c#

public static string EncryptTxt()
{
    SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();

    using (var tdes = new TripleDESCryptoServiceProvider())
    {
        var msg = 'jvofs:JCV XXXXX:201911141547:12345678';
        var key = 'jjvofs';
        var keyOffset = 10;
        
        System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
        TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

        byte[] Results;
        byte[] newKeyx = new byte[24];

        byte[] keybyte = sha.ComputeHash(Encoding.UTF8.GetBytes(key));
        
        Array.Copy(keybyte, keyOffset, newKeyx, 0, newKeyx.Length);

        TDESAlgorithm.Key = newKeyx;
        TDESAlgorithm.Mode = CipherMode.ECB;
        TDESAlgorithm.Padding = PaddingMode.PKCS7;
        byte[] DataToEncrypt = UTF8.GetBytes(msg);
        try
        {
            ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
            Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
        }
        finally
        {
            TDESAlgorithm.Clear();
        }
        var a = Convert.ToBase64String(DataToEncrypt);
        var b = Convert.ToBase64String(newKeyx);
        var c = Convert.ToBase64String(Results);
        return Convert.ToBase64String(Results);
    }
}

JavaScript using cryptojs

txtEncrypter = () => {
    const msg = 'jvofs:JCV XXXXX:201911141547:12345678';
    const key = 'jjvofs';
    const keyOffset = 10;
    const keybyte: any = this.wordArrayToByteArray(crypto.SHA512(key), 100);

    // For message
    const dataToEncrypt = crypto.enc.Utf8.parse(msg);
    const dte = this.wordArrayToByteArray(dataToEncrypt, 100);
    const dataToEncryptx = this._arrayBufferToBase64(dte);
    const dataToEncryptxx = crypto.enc.Utf8.parse(dataToEncryptx);

    // For key
    let newKeyx = keybyte.slice(keyOffset, 34);
    const newKeyxB4Splice = newKeyx;
    const newKeyxB4Splicex = this._arrayBufferToBase64(newKeyx);
    newKeyx = crypto.enc.Utf8.parse(newKeyx);
    
    const options = {
      mode: crypto.mode.ECB,
      padding: crypto.pad.Pkcs7
    };
    
    const encrypted = crypto.TripleDES.encrypt(dataToEncrypt, newKeyx, options);
    const base64String = encrypted.toString();
    console.log(base64String);
  }



wordArrayToByteArray(wordArray, length) {
    if (wordArray.hasOwnProperty('sigBytes') && wordArray.hasOwnProperty('words')) {
      length = wordArray.sigBytes;
      wordArray = wordArray.words;
    }

    const result = [];
    let bytes: any;
    let i = 0;
    while (length > 0) {
      bytes = this.wordToByteArray(wordArray[i], Math.min(4, length));
      length -= bytes.length;
      result.push(bytes);
      i++;
    }
    return [].concat.apply([], result);
  }

  wordToByteArray(word: any, length: any) {
    const ba = [], xFF = 0xFF;
    if (length > 0) {
      // tslint:disable-next-line:no-bitwise
      ba.push(word >>> 24);
    }
    if (length > 1) {
      // tslint:disable-next-line:no-bitwise
      ba.push((word >>> 16) & xFF);
    }
    if (length > 2) {
      // tslint:disable-next-line:no-bitwise
      ba.push((word >>> 8) & xFF);
    }
    if (length > 3) {
      // tslint:disable-next-line:no-bitwise
      ba.push(word & xFF);
    }
    return ba;
  }

  byteArrayToWordArray(ba) {
    const wa = [];
    let i = 0;
    for (i = 0; i < ba.length; i++) {
      // tslint:disable-next-line:no-bitwise
      wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
    }

    return crypto.lib.WordArray.create(wa);
  }

  toUTF8Array(str) {
    const utf8 = [];
    for (let i = 0; i < str.length; i++) {
        let charcode = str.charCodeAt(i);
        if (charcode < 0x80) { utf8.push(charcode); } else if (charcode < 0x800) {
            // tslint:disable-next-line:no-bitwise
            utf8.push(0xc0 | (charcode >> 6), 
                      // tslint:disable-next-line: no-bitwise
                      0x80 | (charcode & 0x3f));
        } else if (charcode < 0xd800 || charcode >= 0xe000) {
            // tslint:disable-next-line: no-bitwise
            utf8.push(0xe0 | (charcode >> 12), 
                      // tslint:disable-next-line: no-bitwise
                      0x80 | ((charcode>>6) & 0x3f), 
                      // tslint:disable-next-line: no-bitwise
                      0x80 | (charcode & 0x3f));
        } else {
            i++;
            // UTF-16 encodes 0x10000-0x10FFFF by
            // subtracting 0x10000 and splitting the
            // 20 bits of 0x0-0xFFFFF into two halves
            // tslint:disable-next-line:no-bitwise
            charcode = 0x10000 + (((charcode & 0x3ff)<<10)
                      // tslint:disable-next-line:no-bitwise
                      | (str.charCodeAt(i) & 0x3ff));
            // tslint:disable-next-line:no-bitwise
            utf8.push(0xf0 | (charcode >>18), 
                      // tslint:disable-next-line:no-bitwise
                      0x80 | ((charcode>>12) & 0x3f), 
                      // tslint:disable-next-line: no-bitwise
                      0x80 | ((charcode>>6) & 0x3f), 
                      // tslint:disable-next-line: no-bitwise
                      0x80 | (charcode & 0x3f));
        }
    }
    return utf8;
}

 _arrayBufferToBase64( buffer ) {
  let binary = '';
  const bytes = new Uint8Array( buffer );
  const len = bytes.byteLength;
  for (let i = 0; i < len; i++) {
      binary += String.fromCharCode( bytes[ i ] );
  }
  return window.btoa( binary );
}

When parsing the data for message and key, both c# and JavaScript are the same:

Message:

[C#] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==

[cryptojs] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==

Key:

[C#] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW

[cryptojs] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW

But as soon as the "crypto.TripleDES.encrypt(....)" runs, I get different result for c# and javascript:

[C#] 1pjvBOB81iAOqsskZ+cM080yDU37XBoCwMhbYULwva/Nql5vbEMiPQ==

[cryptojs] ncCcCYNy3jVsB/95SaC2N1rH5Q+hX04WvScMvwmtkPkrnL7Ki1bmPg==

halfer
  • 19,824
  • 17
  • 99
  • 186
Ibanez1408
  • 4,550
  • 10
  • 59
  • 110
  • in c#, KeyOffSet != keyOffset. Then you start copy at ix 10, but your newKeyx is only 24 bytes long, so your hash is only 14 bytes? are you sure? in the javascript its at least 34. check how long keybyte is. then the javascript might fill the first 10 bytes with salt or rounds, you have to do that in the c# as well. – Charles Nov 15 '19 at 03:58
  • @Charles, am sorry. It was just a typo. I tried to make the code simpler so it can be clearer. I made the changes now. Please see my edit. Thank you. – Ibanez1408 Nov 15 '19 at 04:03
  • try to compare the byte[] arrays line by line and see when they're different. is keybyte js == keybyte c#, etc. i cant test your javascript, because there are functions missing – Charles Nov 15 '19 at 04:33
  • @Charles I'll post everything. I compared each line and they're exactly the same. – Ibanez1408 Nov 15 '19 at 05:34
  • @Charles Please see my edit that includes all methods – Ibanez1408 Nov 15 '19 at 05:36

1 Answers1

1

After the key has been determined from the byte-array of the hash as a sub-array (incl. index 10 to excl. index 34), this sub-array must be converted back into a WordArray with equivalent content, i.e. the line:

newKeyx = crypto.enc.Utf8.parse(newKeyx);

must be replaced by:

newKeyx = byteArrayToWordArray(newKeyx);  

With this change, the NodeJS-code returns the same result as the C#-code.


The conversion WordArray <-> byte-array (and thus all functions required for these conversions) aren't really necessary, because as an alternative the key can also be derived using only the CryptoJS-encoders:

...
const key = 'jjvofs';
const keyOffset = 10;
const keyLength = 24;
const keyHash = crypto.enc.Hex.stringify(crypto.SHA512(key));
const newKey = crypto.enc.Hex.parse(keyHash.slice(keyOffset * 2, (keyOffset + keyLength) * 2));
...

By the way: The ECB-mode is insecure and instead of TripleDES the more performant AES should be used.

Topaco
  • 40,594
  • 4
  • 35
  • 62