Java – PHP equivalent of Java Triple DES encryption/decryption

PHP equivalent of Java Triple DES encryption/decryption… here is a solution to the problem.

PHP equivalent of Java Triple DES encryption/decryption

I’m trying to decrypt a key encrypted by the Java Triple DES function using the PHP mcrypt function, but without success. Find the Java code below

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

public class Encrypt3DES {

private byte[] key;
     private byte[] initializationVector;

public Encrypt3DES(){

}

public String encryptText(String plainText, String key) throws Exception{

----  Use specified 3DES key and IV from other source --------------
        byte[] plaintext = plainText.getBytes();
        byte[] myIV = key.getBytes();
        byte[] tdesKeyData = {(byte)0xA2, (byte)0x15, (byte)0x37, (byte)0x08, (byte)0xCA, (byte)0x62,
        (byte)0xC1, (byte)0xD2, (byte)0xF7, (byte)0xF1, (byte)0x93, (byte)0xDF,
        (byte)0xD2, (byte)0x15, (byte)0x4F, (byte)0x79, (byte)0x06, (byte)0x67,
        (byte)0x7A, (byte)0x82, (byte)0x94, (byte)0x16, (byte)0x32, (byte)0x95};

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);
        sun.misc.BASE64Encoder obj64=new sun.misc.BASE64Encoder();
        return obj64.encode(cipherText);

}

public String decryptText(String encryptText, String key) throws Exception{

byte[] initializationVector = key.getBytes();
        byte[] tdesKeyData = {(byte)0xA2, (byte)0x15, (byte)0x37, (byte)0x08, (byte)0xCA, (byte)0x62,
        (byte)0xC1, (byte)0xD2, (byte)0xF7, (byte)0xF1, (byte)0x93, (byte)0xDF,
        (byte)0xD2, (byte)0x15, (byte)0x4F, (byte)0x79, (byte)0x06, (byte)0x67,
        (byte)0x7A, (byte)0x82, (byte)0x94, (byte)0x16, (byte)0x32, (byte)0x95};

byte[] encData = new sun.misc.BASE64Decoder().decodeBuffer(encryptText);
          Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
          SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
          IvParameterSpec ivspec = new IvParameterSpec(initializationVector);
          decipher.init(Cipher.DECRYPT_MODE, myKey, ivspec);
          byte[] plainText = decipher.doFinal(encData);
          return new String(plainText);

}
}

I want to write a PHP function equivalent to the decryptText Java function above. I find it difficult to generate the exact IV value generated by Java code used for encryption, which is required for decryption.

Solution

This is the PHP equivalent of Java code (I started with comments on The mcrypt reference 20- Sep-2006 07:56 copied PKCS#5-padding).

function encryptText($plainText, $key) {
    $keyData = "\xA2\x15\x37\x08\xCA\x62\xC1\xD2"
        . "\xF7\xF1\x93\xDF\xD2\x15\x4F\x79\x06"
        . "\x67\x7A\x82\x94\x16\x32\x95";

$padded = pkcs5_pad($plainText,
        mcrypt_get_block_size("tripledes", "cbc"));

$encText = mcrypt_encrypt("tripledes", $keyData, $padded, "cbc", $key);

return base64_encode($encText);
}

function decryptText($encryptText, $key) {
    $keyData = "\xA2\x15\x37\x08\xCA\x62\xC1\xD2"
        . "\xF7\xF1\x93\xDF\xD2\x15\x4F\x79\x06"
        . "\x67\x7A\x82\x94\x16\x32\x95";

$cipherText = base64_decode($encryptText);

$res = mcrypt_decrypt("tripledes", $keyData, $cipherText, "cbc", $key);

$resUnpadded = pkcs5_unpad($res);

return $resUnpadded;
}

function pkcs5_pad ($text, $blocksize)
{
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

function pkcs5_unpad($text)
{
    $pad = ord($text{strlen($text)-1});
    if ($pad > strlen($text)) return false;
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
    return substr($text, 0, -1 * $pad);
}

But there are some issues you should pay attention to:

  • In your Java code, you call String.getBytes() without specifying the encoding. If your plaintext contains non-ASCII characters, such as diacritics, this makes your code non-portable because Java uses the system default character set. If you could change, of course I would. I recommend that you use utf-8 on both ends (Java and PHP).
  • You have hardcoded the password key and used the IV as the “key”. I’m by no means an expert in crypto, but for me it just doesn’t feel right and can create huge security holes.
  • Create a random IV and concatenate it at the beginning or end of the message. Since the size of the IV is as far as I know is equal to the block size of the password, you can easily separate the IV from the message by simply removing that many bytes from the beginning or end.
  • As for the key, it is better to use some kind of key derivation. A method to generate a right-sized key from a “human-generated” password.

Of course, you can’t change your approach if you have to meet some given requirements.

Related Problems and Solutions