0

For our current project we need to send encripted data in TripleDES MD5 CBC mode to another company. The values for the pass, salt and init vector are given by the company and are not the ones given here (but with the same length).

They have sent us the code they use to encript it (in .Net C#) which is the following:

//Encription parameter def
static private string _pass = "12345678901234";
static private string _salt = "123456";
static private string _alg = "MD5";
static private string _iv = "1234567890123456";

public static string EncriptString(string textPlain)
{
    return EncriptString(textPlain, _pass, _salt, _alg, 1, _iv, 128); 
}

public static string EncriptString(string textPlain, string passBase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
{
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(textPlain);

    PasswordDeriveBytes password = new PasswordDeriveBytes(passBase, saltValueBytes, hashAlgorithm, passwordIterations);
    byte[] keyBytes = password.GetBytes(keySize / 8);
    RijndaelManaged symmetricKey = new RijndaelManaged();
    symmetricKey.Mode = CipherMode.CBC;
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
    MemoryStream memoryStream = new MemoryStream();
    CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
    cryptoStream.FlushFinalBlock();
    byte[] cipherTextBytes = memoryStream.ToArray();
    memoryStream.Close();
    cryptoStream.Close();

    string cipherText = Convert.ToBase64String(cipherTextBytes);
    return cipherText;
}

I've being searching for a day on Google and Stackoverflow trying to translate this into JAVA and I'm not able. This is my current approach:

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * @author alvaro
 */
public class Encriptor {

    // Constants -----------------------------------------------------

    private static final String PASS_PHRASE = "12345678901234";//says wrong length
    private static final String SALT_VALUE = "123456";
    private static final int PASSWORD_ITERATIONS = 1;
    private static final String INIT_VECTOR = "1234567890123456";
    private static final int KEY_SIZE = 128;

    // Attributes ----------------------------------------------------

    // Static --------------------------------------------------------

    // Constructors --------------------------------------------------

    // Public --------------------------------------------------------

    public String encrypt(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
        MessageDigest digest = MessageDigest.getInstance(DIGEST);
        digest.update(SALT_VALUE.getBytes());
        byte[] bytes = digest.digest(PASS_PHRASE.getBytes(ENCODING));
        SecretKey password = new SecretKeySpec(bytes, "AES");

        //Initialize objects
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] IV = INIT_VECTOR.getBytes();
        IvParameterSpec ivParamSpec = new IvParameterSpec(IV);
        cipher.init(Cipher.ENCRYPT_MODE, password, ivParamSpec);
        byte[] encryptedData = cipher.doFinal(text.getBytes(ENCODING));
        return new BASE64Encoder().encode(encryptedData).replaceAll("\n", "");
    }

    // Package protected ---------------------------------------------

    // Protected -----------------------------------------------------

    // Private -------------------------------------------------------

    // Inner classes -------------------------------------------------

}

The problem is that the MD5 hash is not done the same way in Java than in C#.

Test code:

import org.junit.Test;

import static org.junit.Assert.*;

public class EncriptorTest {

    @Test
    public void shouldEncriptTextCorrectly() {
        // GIVEN
        String input = '<xml><oficina>1234</oficina><empleado>123456</empleado></xml>'
        String expected = 'Lz1aG3CFYoyzjGcMzJXDB7DQgscrv9scP+d5JY8/fiUN6LV2RsnSPqDU/E5BGKz3QbeSl3RyhUgnYyN3uBBRJA=='
        // WHEN
        String output = new Encriptor().encrypt(input)
        //THEN
        assertEquals('Wrong encription', expected, output)
    }
}

SOLUTION

At the end I used a solution given in another Stackoverlow question in which a port of PasswordDeriveBytes to Java is given.

Community
  • 1
  • 1
Alfergon
  • 5,463
  • 6
  • 36
  • 56
  • 1
    PasswordDeriveBytes doesn't directly perform the MD5 hash, the MD5 hash method is just a parameter for the Microsoft implementation of PBKDF1. – Maarten Bodewes Apr 14 '15 at 18:10
  • 1
    First make sure you compare the derived secret (key) before doing the encryption. If the key is not the same your ciphertext won't decrypt either. – Maarten Bodewes Apr 15 '15 at 07:41
  • I've done what you said and ended reading one of your answers on another stackoverflow question in which you give a port of PasswordDeriveBytes to Java that was exactly what I needed. Thank you very much again @MaartenBodewes – Alfergon Apr 15 '15 at 07:55

1 Answers1

1

You've had a miscommunication with your other party.

You say that you're trying to use Triple DES, MD5, CBC; however the .Net code they're using says this:

RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;

Rijndael is the name of the algorithm that was chosen as the AES; the example they gave you is using AES, not Triple DES.

Triple DES takes 3*56 bits for the key (expanded to 3*64), which is 168 or 192 bits, depending on how you count the bits; the API probably consumes and expects 168 bits. AES only supports 128, 192, and 256 bit key sizes, which explains why you're probably getting the wrong size errors.

You need to find out whether they intend to use Triple DES or AES and fix both sides of the code.

As an aside:

  • Single DES is known to be very insecure; a couple dollars spent on Amazon EC2 can crack DES. Triple DES, however, is roughly safe, but should be discouraged for new development.

  • MD5 is known to be very insecure, in particular, it is trivially vulnerable to length extension attacks, pre-image attacks, collision attacks, etc. Consider SHA 1, 2, or 3 instead.

antiduh
  • 11,853
  • 4
  • 43
  • 66
  • Thank you very much for your answer. I was already doubting that the code was actually using AES instead of 3DES. I'll get in contact with the other company and get back here with more info. – Alfergon Apr 14 '15 at 15:45
  • I've confirmed the use of AES with the other company. Thank you very much for your answer. – Alfergon Apr 15 '15 at 07:59