1

tripledes encryption not yielding same results in PHP and C#

    public static string Encrypt(string toEncrypt, string key, bool useHashing) 
    {     
        byte[] keyArray;     
        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);      

        if (useHashing)     
        {         
            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));     
        }     
        else
            keyArray = UTF8Encoding.UTF8.GetBytes(key);      

        TripleDESCryptoServiceProvider tdes 
            = new TripleDESCryptoServiceProvider();
        tdes.Key = keyArray;     
        tdes.Mode = CipherMode.ECB;     
        tdes.Padding = PaddingMode.PKCS7;      

        ICryptoTransform cTransform = tdes.CreateEncryptor();     
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,
            toEncryptArray.Length);      
        return Convert.ToBase64String(resultArray, 0, resultArray.Length); 
    }  

The above post and code has a bit of pretty straight forward (or so I thought) logic to encrypt/decrypt a string using 3DES and a private key.

I've found a few examples of how to implement something similar in nodejs using the crypt library but everything I've tried thus far has produced garbage (and rightfully so).

Thoughts?

UPDATE1:

Here is a bit of C# code to generate the data I'm working with:

    String key = "abcdefghijklmnop";
    String text = "12345";
    String encrypted = Encrypt(text, key, false);
    //Returns "QI3I65+aWSk="

And here's the latest revision of the nodejs code I was working with:

    var crypto = require('crypto');
    var key = 'abcdefghijklmnop';
    var encrypted = 'QI3I65+aWSk=';
    var expected = '12345';

    var algs = [ 'des3', 'des-ede', 'des-ede3', 'des-ecb', 'aes-128-ecb'];
    for(var i in algs)
    {
      var decipher = crypto.createDecipher(algs[i], key);
      var result = ''
      result += decipher.update(encrypted, 'hex', 'binary');
      result += decipher.final('binary');

      console.log('Algorithm: ' + algs[i] 
        + ', Matched Expected: ' + (result === expected));
    }

.. which returns

    Algorithm: des3, Matched Expected: false
    Algorithm: des-ede, Matched Expected: false
    Algorithm: des-ede3, Matched Expected: false
    Algorithm: des-ecb, Matched Expected: false
    Algorithm: aes-128-ecb, Matched Expected: false

I wasn't clear as to exactly which algorithm to use (my list was significantly longer in previous attempts) now was I sure as to what encoding combination (binary/hex) to use.

Thanks again.

UPDATE2: Copied over the Encrypt method from the referenced post:

tripledes encryption not yielding same results in PHP and C#

Community
  • 1
  • 1
user1153154
  • 11
  • 1
  • 3
  • Could ypu post what you tried. – fent Jan 17 '12 at 04:50
  • @DeaDEnD: I added a few samples to work with. Hopefully this will give us a bit more to work with. Thanks. – user1153154 Jan 17 '12 at 08:25
  • Updated the JavaScript code once more as my attempt to clean things up removed the proper 'expected' and was using that both for the encrypted input and expected output. The code has been updated but obviously it still doesn't work. – user1153154 Jan 17 '12 at 16:09
  • We can't help you much without knowing what's inside your C# `Encrypt` method. That's where all the **real** stuff will be :-) – poupou Jan 17 '12 at 16:11
  • Updated the post to include the Encrypt source from the referenced post. – user1153154 Jan 17 '12 at 21:19

2 Answers2

2

Well, it took me a while to get the bottom out of this.

First about DES algorithm, DES algorithm need an IV (initial vector) to encrypt, as well as the key. DES algorithm in System.Security.Cryptography will generate an IV randomly when it's not specified. And when decrypt you need both key and IV.

That's why your code will never work, for your decryption program just don't know what the IV used in encryption, and will generate another IV itself, which doesn't match the random one.

Second your javascript code is wrong in many places:

decipher.update(encrypted, 'hex', 'binary')

where encrypted is not encoded in hex but in base64, and you need output the result as base64 instead of binary. read the document.

['des3', 'des-ede', 'des-ede3', 'des-ecb', 'aes-128-ecb']

where all these algorithms are not the same as the encryption algorithm you use. As you specified to use CipherMode.ECB which is not recommended and openssh doesn't support at all, so it's impossible to decrypt.

All adds up, your code won't work.

Here is the right practice:

using System;
using System.Text;
using System.Security.Cryptography;

public class Test
{
    public static string Encrypt(string toEncrypt, string key, bool useHashing) 
    {     
        byte[] keyArray;     
        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);      

        if (useHashing)     
        {         
            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));     
        }     
        else
            keyArray = UTF8Encoding.UTF8.GetBytes(key);      

        var tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = keyArray;     
        // tdes.Mode = CipherMode.CBC;  // which is default     
        // tdes.Padding = PaddingMode.PKCS7;  // which is default

        Console.WriteLine("iv: {0}", Convert.ToBase64String(tdes.IV));

        ICryptoTransform cTransform = tdes.CreateEncryptor();     
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,
            toEncryptArray.Length);      
        return Convert.ToBase64String(resultArray, 0, resultArray.Length); 
    }  

    public static void Main()
    {
        Console.WriteLine("encrypted as: {0}", Encrypt("12345", "abcdefghijklmnop", false));
    }
}

which outputs

iv: pdMBMjdeFdo=
encrypted as: 3uDkdT6aQ3c=

And use the right algorithm des-ede-cbc in node.js:

var crypto = require('crypto');

var alg = 'des-ede-cbc';

var key = new Buffer('abcdefghijklmnop', 'utf-8');
var iv = new Buffer('pdMBMjdeFdo=', 'base64');

var encrypted = new Buffer('3uDkdT6aQ3c=', 'base64');
var source = '12345';

var cipher = crypto.createCipheriv(alg, key, iv);
var encoded = cipher.update(source, 'ascii', 'base64');
encoded += cipher.final('base64');

console.log(encoded, encrypted.toString('base64'));

var decipher = crypto.createDecipheriv(alg, key, iv);
var decoded = decipher.update(encrypted, 'binary', 'ascii');
decoded += decipher.final('ascii');

console.log(decoded, source);

which outputs

3uDkdT6aQ3c= 3uDkdT6aQ3c=
12345 12345

Now the problem solved.

xiaoyi
  • 6,641
  • 1
  • 34
  • 51
0

Your input encoding looks like it should be 'base64' ... it's worth noting as mentioned in your linked question that there may be a difference in padding technique as well.

Tracker1
  • 19,103
  • 12
  • 80
  • 106