user1197611
user1197611

Reputation: 11

3DES encryption implementation reporting different output from C# implementation

I have been trying rather unsuccessfully to encrypt a plaintext using 3DES in java by using the BouncyCastle suite. This result is supposed to match that produced by an existing C# implementation because I plan to be decrypting it later.

I keep getting different results though I am convinced I have produced the "equivalent" of the C# algo in Java. Could someone kindly look through both snippets and advise? I would be most grateful.

C# encryption:

public static byte[] encryptStringToBytes_3DES(string plainText, string passKey)
    {
        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");

        // Declare the streams used
        // to encrypt to an in memory
        // array of bytes.
        MemoryStream msEncrypt = null;
        CryptoStream csEncrypt = null;
        StreamWriter swEncrypt = null;
        ASCIIEncoding ascii = new System.Text.ASCIIEncoding();


        // used to encrypt the data.
        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        string passphrase = passKey;
        byte[] iv = ascii.GetBytes("AVREWASH");
        byte[] key = ascii.GetBytes(passphrase);

        try
        {
            // Create a TripleDES object
            // with the specified key and IV.
            //Console.WriteLine("Key size is " + tdes.KeySize+" and IV is "+tdes.IV+" and that of key is "+key.Length);
            tdes.Key = key;
            tdes.IV = iv;
            tdes.Padding = PaddingMode.Zeros;

            // Create a decrytor to perform the stream transform.
            ICryptoTransform encryptor = tdes.CreateEncryptor(tdes.Key, tdes.IV);

            // Create the streams used for encryption.
            msEncrypt = new MemoryStream();
            csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
            swEncrypt = new StreamWriter(csEncrypt);

            //Write all data to the stream.
            swEncrypt.Write(plainText);

        }
        catch (Exception ex)
        {
            Console.WriteLine("Error is " + ex.Message);
            while (true)
            {
            }
        }

        finally
        {
            // Clean things up.

            // Close the streams.
            if (swEncrypt != null)
                swEncrypt.Close();
            if (csEncrypt != null)
                csEncrypt.Close();
            if (msEncrypt != null)
                msEncrypt.Close();

            // Clear the TripleDES object.
            if (tdes != null)
                tdes.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return msEncrypt.ToArray();

}

There is this helper function I use in converting results to Hex...

public static string ByteArrayToString(byte[] ba)
    {
        string hex = BitConverter.ToString(ba);
        return hex.Replace("-", "");
    }

The Java snippet supposed to be doing the "equivalent" encryption follows too:

public void encrypt(String plaintext, String IV, String tripleDesKey){

try{

     SecretKey keySpec = new SecretKeySpec(tripleDesKey.getBytes("US-ASCII"),"DESede");

    IvParameterSpec iv = new IvParameterSpec(IV.getBytes("US-ASCII"));

    Cipher e_cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
    e_cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

    byte [] cipherText = e_cipher.doFinal(plaintext.trim().getBytes("US-ASCII"));

        System.out.println("Ciphertext: " + asHex(cipherText));
}
catch(Exception exc){
 ex.printStackTrace();
}
}

here is its corresponding Hex.. function

public static String asHex (byte buf[]) {
  StringBuffer strbuf = new StringBuffer(buf.length * 2);
  int i;

  for (i = 0; i < buf.length; i++) {
   if (((int) buf[i] & 0xff) < 0x10)
        strbuf.append("0");

   strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
  }

  return strbuf.toString();
 }

PLEASE HELP.

Upvotes: 1

Views: 2516

Answers (2)

Henrick Hellstr&#246;m
Henrick Hellstr&#246;m

Reputation: 2666

Some comments:

  1. The default mode of the .NET TripleDESCryptoServiceProvider is indeed CBC, which is what you specify explicitly in your JAVA code, but it wouldn't hurt if you specify it explicitly in your C# code as well.

  2. In your original code you use PaddingMode.Zeros in the C# code, but PKCS5Padding in your JAVA code. There is AFAIK no built in cipher provider in Java that provides the equivalent of PaddingMode.Zeros. If you can still change your C# code, you should use PaddingMode.Pkcs7 instead. Otherwise you have to search for a 3rd party cipher provider for Java that will do the job.

  3. Avoid using the ASCII encoding, unless you are able to guarantee that the input actually contains 7-bit ascii. If you pass in strings with other characters, the output is undefined.

  4. The key data you pass to the 3DES constructor should be of length 8, 16 or 24 and - generally - have the DES parity bits set. AFAIK both .NET and Java will ignore the parity bits, but they might behave differently if the key length does not belong to either of the right values. Hence, if you want your encryption to work for any possible key input, you should use a key derivation function that is supported by both .NET and Java. Try PBEWithHmacSHA1AndDESede in Java, and in C# add code that transforms the passKey using System.Security.Cryptography.Rfc2898DeriveBytes.

Upvotes: 3

werewindle
werewindle

Reputation: 3029

You are using different padding modes. In C# you wrote tdes.Padding = PaddingMode.Zeros; and in Java you used PKCS5Padding in CBC mode. This is not the same things.

Upvotes: 3

Related Questions