.NET TripleDESCryptoServiceProvider equivalent in

2019-02-01 15:33发布

问题:

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"

回答1:

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



回答2:

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.



回答3:

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.



回答4:

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)));
    }

}