1

Here is a problem. There is a know library 'Chilkat Crypt'. It contains 3des encryption method.

 public static void ChilkatEncryption(String cc, string tdesKey, string tdesIV)
    {
        Crypt2 crypt = new Chilkat.Crypt2();

        bool success = crypt.UnlockComponent("Anything for 30-day trial");
        if (success != true)
        {
            Console.WriteLine(crypt.LastErrorText);
            return;
        }

        //  Specify 3DES for the encryption algorithm:
        crypt.CryptAlgorithm = "3des";

        //  CipherMode may be "ecb" or "cbc"
        crypt.CipherMode = "cbc";

        //  KeyLength must be 192.  3DES is technically 168-bits;
        //  the most-significant bit of each key byte is a parity bit,
        //  so we must indicate a KeyLength of 192, which includes
        //  the parity bits.
        crypt.KeyLength = 192;

        //  The padding scheme determines the contents of the bytes
        //  that are added to pad the result to a multiple of the
        //  encryption algorithm's block size.  3DES has a block
        //  size of 8 bytes, so encrypted output is always
        //  a multiple of 8.
        crypt.PaddingScheme = 0;

        //  EncodingMode specifies the encoding of the output for
        //  encryption, and the input for decryption.
        //  It may be "hex", "url", "base64", or "quoted-printable".
        crypt.EncodingMode = "hex";

        //  An initialization vector is required if using CBC or CFB modes.
        //  ECB mode does not use an IV.
        //  The length of the IV is equal to the algorithm's block size.
        //  It is NOT equal to the length of the key.
        string ivHex = tdesIV;
        crypt.SetEncodedIV(ivHex, "hex");

        //  The secret key must equal the size of the key.  For
        //  3DES, the key must be 24 bytes (i.e. 192-bits).
        string keyHex = tdesKey;
        crypt.SetEncodedKey(keyHex, "hex");

        //  Encrypt a string...
        //  The input string is 44 ANSI characters (i.e. 44 bytes), so
        //  the output should be 48 bytes (a multiple of 8).
        //  Because the output is a hex string, it should
        //  be 96 characters long (2 chars per byte).
        string encStr = crypt.EncryptStringENC(cc);
        Console.WriteLine(encStr);

        //  Now decrypt:
        string decStr = crypt.DecryptStringENC(encStr);
        Console.WriteLine(decStr);
    }

When i am trying to do the same without this third-party library using a standard provider the result is quite different:

    private static string EncryptData(String cc, byte[] tdesKey, byte[] tdesIV)
    {
        //Create the file streams to handle the input and output files.
        MemoryStream fin = new MemoryStream();
        MemoryStream fout = new MemoryStream();
        StreamWriter sw = new StreamWriter(fin);
        sw.Write(cc);
        sw.Flush();
        fin.Position = 0;
        fout.SetLength(0);

        //Create variables to help with read and write.
        byte[] bin = new byte[100]; //This is intermediate storage for the encryption.
        long rdlen = 0;              //This is the total number of bytes written.
        long totlen = fin.Length;    //This is the total length of the input file.
        int len;                     //This is the number of bytes to be written at a time.

        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Mode=CipherMode.CBC;
        tdes.Padding = PaddingMode.None;
        CryptoStream encStream = new CryptoStream(fout, tdes.CreateEncryptor(tdesKey, tdesIV), CryptoStreamMode.Write);

        Console.WriteLine("Encrypting...");

        //Read from the input file, then encrypt and write to the output file.
        while (rdlen < totlen)
        {
            len = fin.Read(bin, 0, 100);
            encStream.Write(bin, 0, len);
            rdlen = rdlen + len;
            Console.WriteLine("{0} bytes processed", rdlen);
        }

        byte[] encBytes = fout.ToArray();

        return BitConverter.ToString(encBytes);


    }

Does anyone know, what should be a parameter set for standard .NET encryption to get the same 3DES result?

Thank you!

MagisterCrazy
  • 225
  • 1
  • 10
  • 1
    Can you give an example encoded ciphertext so that it is easier to find a match? – Artjom B. Nov 30 '15 at 20:46
  • 2
    Do you mean so that you get the same ciphertext? The example above uses CBC, which will give you a different output depending on the IV. – mfanto Nov 30 '15 at 20:51

1 Answers1

1

Incorrect Padding

According to the Chilkat documentation here, a PaddingScheme value of 0 means the library will use PKCS#5 padding. PKCS#5 is essentially just a special case of PKCS#7 which is specified only for block ciphers of size 8 bytes, suchs as Triple DES. Using the .NET provider, you should specify PaddingMode.PKCS7 instead of PaddingMode.None as above.

Additionally, you need to make sure to explicitly close the CryptoStream, so that it knows that you are done writing to it so that it can encrypt the final (padded) block:

encStream.Close();
byte[] encBytes = fout.ToArray();

Incorrect Encoding

Another problem that may or may not be giving you issues is the fact that the two different examples use different text encodings. The Chilkat library looks like it defaults to using "ANSI" encoding. In the second example, however, you don't explicitly specify an encoding in the StreamWriter constructor, so it defaults to UTF-8.

Depending upon the data that you are encrypting, this may or may not give you issues, but basically if you have any characters outside the range of plain old ASCII, you will get inconsistent results between the two functions since you won't actually be encrypting the same thing.

The quick fix is to specify the encoding in the StreamWriter constructor:

StreamWriter sw = new StreamWriter(fin, Encoding.Default);

This will give you a StreamWriter which will write bytes from strings based on whatever the default ANSI codepage for your system. The big problem with this is that whatever "ANSI" means on your system won't necessarily be the same thing as someone else's system (for a detailed explanation, see this question), so this could lead to issues if you need to interoperate.

For this reason, I'd highly suggest that you specify a more specific encoding, such as UTF-8.

For the Chilkat library, you can do this:

crypt.Charset = "utf-8";    

For the .NET provider example, you can specify the encoding explicitly in the StreamWriter constructor:

StreamWriter sw = new StreamWriter(fin, Encoding.UTF8);

You could also just omit the argument, since UTF-8 is the default encoding used by the StreamWriter class.


Incidentally, rather than using a StreamWriter to encode/write your input string to the memory stream fin at the beginning and then reading it back to write to the CryptoStream 100 bytes at a time, you could just encode directly to a byte array and write to the CryptoStream all at once:
var buffer = Encoding.UTF8.GetBytes(cc);
encStream.Write(buffer, 0, buffer.Length);
Community
  • 1
  • 1
Ben
  • 360
  • 1
  • 2
  • 6
  • Hi Ben, thank you for a perferct answer! It's not solving my problem, but it helps me to find a proper solution - there was a problem of interpritation IV parameter in two encryption systems. Anyway I am pretty sure this post would be very useful for other people who will need to deal with this Chilkat library. – MagisterCrazy Dec 01 '15 at 13:45