views:

983

answers:

4

Hello nice people,

Please, just don't ask me why. I just have this code in .NET that encrypt/decrypt strings of data. I need now to make 'exactly' the same funcionality in java. I have tried several examples for DESede crypt, but none of them gives the same results as this class in .net.

I even though on making a .net webserbvice behind ssl to serve this two methods writen in .net but it is just too stupid to do without exhausting all the posibilities.

Maybe some of you java people which are more related in the area will have on top of your heads how to make it.

Thanks !!!

public class Encryption
{
  private static byte[] sharedkey = {...};
  private static byte[] sharedvector = {...};

  public static String Decrypt(String val)
  {
    TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
    byte[] toDecrypt = Convert.FromBase64String(val);
    MemoryStream ms = new MemoryStream();
    CryptoStream cs = new CryptoStream(ms, tdes.CreateDecryptor( sharedkey, sharedvector ), CryptoStreamMode.Write);

    cs.Write(toDecrypt, 0, toDecrypt.Length);
    cs.FlushFinalBlock();
    return Encoding.UTF8.GetString(ms.ToArray());
  }

  public static String Encrypt(String val)
  {
    TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
    byte[] toEncrypt = Encoding.UTF8.GetBytes(val);
    MemoryStream ms = new MemoryStream();
    CryptoStream cs = new CryptoStream(ms, tdes.CreateEncryptor( sharedkey, sharedvector ), CryptoStreamMode.Write);
    cs.Write(toEncrypt, 0, toEncrypt.Length);
    cs.FlushFinalBlock();
    return Convert.ToBase64String(ms.ToArray());
  }
}


Samle input/output

String plain = "userNameHere:passwordHere";
Console.WriteLine("plain: " + plain);


String encrypted = Encrypt(plain);
Console.WriteLine("encrypted: " + encrypted);
// "zQPZgQHpjxR+41Bc6+2Bvqo7+pQAxBBVN+0V1tRXcOc="

String decripted = Decrypt(encrypted);
Console.WriteLine("decripted: " + decripted); 
// "userNameHere:passwordHere"
A: 

Have you made sure the .NET code uses the same padding as the Java code? I see no padding specified in the .NET code, that's why I ask.

Do you happen to have the source for the Java code, it will help find mistakes.

Kyle Rozendo
I actually don't remember how many things I've tried, surely I am not even close to the rigth code. So better is someone know how to make it from the scratch
David Hofmann
Do you not have the Java code? And #2 we'll also need sample in and out from the Java code.
Kyle Rozendo
A: 

Try the following. For actual usage, I would get a base64 library like commons codec or use the codec that comes with BouncyCastle

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class Encryption {

    private static SecretKey sharedkey;
    private static byte [] sharedvector;

    static {
        int keySize = 168;
        int ivSize = 8;
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
            keyGenerator.init(keySize);
            sharedkey = keyGenerator.generateKey();

            sharedvector = new byte [ivSize];
            byte [] data = sharedkey.getEncoded();

            int half = ivSize / 2;
            System.arraycopy(data, data.length-half, sharedvector, 0, half);
            System.arraycopy(sharedvector, 0, sharedvector, half, half);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public static void main(String [] args) throws Exception {
        System.out.println(Decrypt(Encrypt("Hello World")));

    }

    public static String Encrypt(String val) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, sharedkey, new IvParameterSpec(sharedvector));

        return new sun.misc.BASE64Encoder().encode(cipher.doFinal(val.getBytes()));
    }

    public static String Decrypt(String val) throws GeneralSecurityException, IOException {
        Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, sharedkey, new IvParameterSpec(sharedvector));

        return new String(cipher.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(val)));
    }

}
Kevin
+1  A: 

You got a few problems,

  1. Your key must be 24 bytes if you want generate the same key materials on both .NET and Java.
  2. The IV must be block size, which is 8 bytes for Triple DES.
  3. In Java, you need to specify the default mode and padding, which is "DESede/CBC/NoPadding".

Once you make these changes, you should be able to decrypt it on Java side.

ZZ Coder
NoPadding won't work in this case.
erickson
It works for me. I can decrypt his text with NoPadding.
ZZ Coder
But can you encrypt his plaintext? The SunJCE will reject any input that isn't a multiple of 8 bytes.
erickson
In JCE term, NoPaddding is equivalent to the zero-padding. In CBC mode, it will get padded to the blocksize. Say you have 3 extra bytes after paddding. NoPadding will fill it with "00 00 00". PKCS5 will have "03 03 03". When testing decrypting, I always use NoPadding first, it always works. Then you can figure out what padding is.
ZZ Coder
That's a great tip!
erickson
+3  A: 

Code follows, but first a few notes.

  1. A different initialization vector must be chosen for every message. Hard-coding the initialization vector does not make sense. The IV should be sent along with the cipher text to the message recipient (it's not secret).
  2. I used my own utility class for base-64 encoding. You can use sun.misc.BASE64Encoder and sun.misc.BASE64Decoder instead, use a third-party library like BouncyCastle, or write your own.
  3. You are using 2-key triple DES, where the first key and the third key is the same. I modified sharedkey to reflect this, since the Java DESede cipher always requires a 192-bit key; it's up to the key generator to handle the keying option.
  4. A CBC IV is only 64 bits. I've used only the first 64 bits of sharedvector.

This class should inter-operate with the C# version.

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Encryption
{

  private static byte[] sharedkey = {
    0x01, 0x02, 0x03, 0x05, 0x07, 0x0B, 0x0D, 0x11, 
    0x12, 0x11, 0x0D, 0x0B, 0x07, 0x02, 0x04, 0x08, 
    0x01, 0x02, 0x03, 0x05, 0x07, 0x0B, 0x0D, 0x11
  };

  private static byte[] sharedvector = {
    0x01, 0x02, 0x03, 0x05, 0x07, 0x0B, 0x0D, 0x11
  };

  public static void main(String... argv)
    throws Exception
  {
    String plaintext = "userNameHere:passwordHere";
    String ciphertext = encrypt(plaintext);
    System.out.println(ciphertext);
    System.out.println(decrypt(ciphertext));
  }

  public static String encrypt(String plaintext)
    throws Exception
  {
    Cipher c = Cipher.getInstance("DESede/CBC/PKCS5Padding");
    c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sharedkey, "DESede"), new IvParameterSpec(sharedvector));
    byte[] encrypted = c.doFinal(plaintext.getBytes("UTF-8"));
    return Base64.encode(encrypted);
  }

  public static String decrypt(String ciphertext)
    throws Exception
  {
    Cipher c = Cipher.getInstance("DESede/CBC/PKCS5Padding");
    c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sharedkey, "DESede"), new IvParameterSpec(sharedvector));
    byte[] decrypted = c.doFinal(Base64.decode(ciphertext));
    return new String(decrypted, "UTF-8");
  }

}

Output:

zQPZgQHpjxR+41Bc6+2Bvqo7+pQAxBBVN+0V1tRXcOc=

userNameHere:passwordHere

erickson
Wow ! I've 'no idea' of what you are talking about :D But it works. I promise to have a read on the subject for a good understanding of encryption algorithms. THANKS AGAIN !!!
David Hofmann