Thursday, July 16, 2009

Triple DES Encryption implementation compatible between Java and .NET!

One problem that I have had to solve in the past was making encryption between Java and .NET possible.  Specifically, I was charged to read an encrypted query string parameter from a Java system in my ASP.NET application.  After a few iterations of incompatible implementations, I created an implementation in both .NET (c#) and Java to ensure compatibility.

Since this caused me so much pain, I thought I would share my implementation with you in hopes that it will work for you as well.

Let me know if you have any questions or want to provide a decrypt implementation for the java class.

DOWNLOAD C# CLASS FILE

DOWNLOAD JAVA CLASS FILE

 

.NET (C#) Implementation

/// <summary>
/// Encryption utility class that implements Triple DES algorithm
/// </summary>
public class TripleDESImplementation
{
    //Encryption Key
    private byte[] EncryptionKey { get; set; }
    // The Initialization Vector for the DES encryption routine
    private byte[] IV { get; set; }

    /// <summary>
    /// Constructor for TripleDESImplementation class
    /// </summary>
    /// <param name="encryptionKey">The 24-byte encryption key (24 character ASCII)</param>
    /// <param name="IV">The 8-byte DES encryption initialization vector (8 characters ASCII)</param>
    public TripleDESImplementation(string encryptionKey, string IV)
    {
        if (string.IsNullOrEmpty(encryptionKey))
        {
            throw new ArgumentNullException("'encryptionKey' parameter cannot be null.", "encryptionKey");
        }
        if (string.IsNullOrEmpty(IV))
        {
            throw new ArgumentException("'IV' parameter cannot be null or empty.", "IV");
        }

        EncryptionKey = Encoding.ASCII.GetBytes(encryptionKey);
        // Ensures length of 24 for encryption key
        Trace.Assert(EncryptionKey.Length == 24, "Encryption key must be exactly 24 characters of ASCII text (24 bytes)");

        this.IV = Encoding.ASCII.GetBytes(IV);
        // Ensures length of 8 for init. vector
        Trace.Assert(IV.Length == 8, "Init. vector must be exactly 8 characters of ASCII text (8 bytes)");
    }

    /// <summary>
    /// Encrypts a text block
    /// </summary>
    public string Encrypt(string textToEncrypt)
    {
        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = EncryptionKey;
        tdes.IV = IV;

        byte[] buffer = Encoding.ASCII.GetBytes(textToEncrypt);
        return Convert.ToBase64String(tdes.CreateEncryptor().TransformFinalBlock(buffer, 0, buffer.Length));
    }

    /// <summary>
    /// Decrypts an encrypted text block
    /// </summary>
    public string Decrypt(string textToDecrypt)
    {
        byte[] buffer = Convert.FromBase64String(textToDecrypt);

        TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
        des.Key = EncryptionKey;
        des.IV = IV;

        return Encoding.ASCII.GetString(des.CreateDecryptor().TransformFinalBlock(buffer, 0, buffer.Length));
    }
}

Java Implementation

public class TripleDesImplementation {
    private String key;
    private String initializationVector;
    public TripleDesImplementation(String key, String initializationVector)
    {
        this.key = key;
        this.initializationVector = initializationVector;
    }

    public String encryptText(String plainText) throws Exception{
    //----  Use specified 3DES key and IV from other source --------------
      byte[] plaintext = plainText.getBytes();
      byte[] tdesKeyData = key.getBytes();

      byte[] myIV = initializationVector.getBytes();

      Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
      SecretKeySpec    myKey = new SecretKeySpec(tdesKeyData, "DESede");
      IvParameterSpec ivspec = new IvParameterSpec(myIV);

      c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
      byte[] cipherText = c3des.doFinal(plaintext);

      return Base64Coder.encodeString(new String(cipherText));
    }
}

14 comments:

strokov said...

Hey great info, I am also dealing with this problem, but I am doing it from C# Desktop application to a Java servlet, so I need to decrypt on the Java side, I tried to replicate the C# code for decrypt in Java but I only get a 40% of the message decrypted fine, any idea?

For example I sent this as the text to encrypt "_2G4WS52M6W1459365" and I get this "_2G4WS52J���iS�5_2G4WS52J���iS�5"

Sam Walker said...

From first glance, it looks like an encoding issue, but I'd have to see your code to make a better guess.

Yann said...

Hi Sam, I've tried your code and it seems to work excepted when you get an encryted text from java wi th at least one "/" . The C# method can't decrypt the message.Have you already face to this problem ?

Yann said...

Sorry, in fact it's not due to to the "/"

I tried to encrypt this random string "AeRTYhgFD678de4" with these key "4d89g13j4j91j27c582ji693" and iv "zerocool".

In Java i got : cANU9X90FvhHP5wH3t9VMQ==

In C# I got:cANU9X90FvhHgZwH3t9VMQ==

So I can't decrypt the encrypted text from java.

Sam Walker said...

Yann, I haven't experienced this problem before, but I'll take a look. In the mean time, if you end up figuring out the solution, make sure you post it. Thanks!

Sam

cawineguy said...

When I found this post I got excited, because this is precisely the problem I'm trying to solve, except the other direction. Here is the .NET code I want to convert to java:
Decryptor dec = new Decryptor(EncryptionAlgorithm.TripleDes);

dec.IV = Encoding.ASCII.GetBytes(ssIV);
byte[] key = Encoding.ASCII.GetBytes(ssKEY);

byte[] plainText = dec.Decrypt(Convert.FromBase64String(encryptedCCNumber), key);
return Encoding.ASCII.GetString(plainText);

The issue is that the key is 16 characters, and that is an illegal length for the java decryption. Am I using the wrong algorithm on the java side?

Sam Walker said...

cawineguy, I'm afraid I'm not much of a java expert, so I'm afraid I can't tell you what's going on right off the bat. I'll take a look at it and get back to you if I find anything.

cawineguy said...

Sam - thanks, I found it in the article you originally referenced. The problem is that Java requires a 24 byte key; so I needed to take the 16 byte password, and pad with the first 8 bytes of the password. now it works like a champ!

Mr.Madoogun.Com said...

So Many Thank Thank Thank.

Very good topic

Mark D said...

Sam,
THANK YOU VERY MUCH!!! This is 75% of what I have been looking high and low for. Can you please post the Java Decryptor function for the remaining 25% of the functionality I need??? I am not a java developer, so am having a hard time with this.
Thanks a lot for this great post...

Author Nitin said...

have modified the java code somewhat, but it works fine for me, I encrypting the string in .net and decrypting it on java... hope it'll be helpful for you.

package com.bay.bizlogic;

/**
* Created by Nitin Gaur.
* User: admin
* Date: 4 Feb, 2010
* Time: 12:46:23 PM
* To change this template use File | Settings | File Templates.
*/
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class TripleDES {
private String key;
private String initializationVector;
public TripleDES(String key, String initializationVector)
{
this.key = key;
this.initializationVector = initializationVector;

}

public String encryptText(String plainText) throws Exception{
//---- Use specified 3DES key and IV from other source --------------
byte[] plaintext = plainText.getBytes();
byte[] tdesKeyData = key.getBytes();

// byte[] myIV = initializationVector.getBytes();

Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(initializationVector.getBytes());

c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
byte[] cipherText = c3des.doFinal(plaintext);

return new sun.misc.BASE64Encoder().encode(cipherText);
}
public String decryptText(String cipherText)throws Exception{
byte[] encData = new sun.misc.BASE64Decoder().decodeBuffer(cipherText);
Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
byte[] tdesKeyData = key.getBytes();
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(initializationVector.getBytes());
decipher.init(Cipher.DECRYPT_MODE, myKey, ivspec);
byte[] plainText = decipher.doFinal(encData);
return new String(plainText);
}
}


Thanks...
India

chitgoks said...

hi. is it possible in both .net and java sides so create a key and IV according to our own preferred string?

rather than be restricted to 24 bytes and 8 bytes respectively?

Bryan said...

Thank you very much. This really helped me out.

Anu said...

Hi Sam,

My knowledge on the whole encryption bit is very poor. We recieve a java generated Triple DES key from a third party. I want to encrypt/decrypt the key in our VB.NET app. Do we have to modify both the .NET and the Java code?
Please pardon my ignorance but vould you please tell me how do i use the key in my app.

Like when i use the key in this line of code:
Dim desTransform As ICryptoTransform = desCrypto.CreateEncryptor(EncryptKey(Key), EncryptIV(IV))

I get the error: 'specified key is not a valid size for this algorithm'.

Could you show me how to use the key here.

Thanks.