0

I am trying to encrypt rather big files (up to 150mb) with PHP and decrypt them with Java.

When I encrypt it using Java and compare it to the encryption result from PHP, I see that the last few bytes are different.

Could this be a problem with the padding? How do I fix this?

The Java code is able to en and decrypt files correctly.

The code below is not secure at all and not meant to be.

PHP Encryption:

    public function encodeAndEncrypt($source, $target, $keyPhrase) {
    $hSrc = fopen($source,'rb');
    $hDst = fopen($target,'wb');

    $iv = substr(md5($keyPhrase, true), 0, 8);
    $key = substr(md5($keyPhrase, true), 0, 24);
    $opts = array('iv'=>$iv, 'key'=>$key);

    stream_filter_append($hDst, 'mcrypt.tripledes', STREAM_FILTER_WRITE, $opts);


    while ($chunk = fread($hSrc,8192)) {
            fwrite($hDst,$chunk, 8192);
    }

    fclose($hDst);
    fclose($hSrc);

Java Decryption:

private static final String ALGORITHM = "DESede/CBC/PKCS5Padding";


    void main() {

    MessageDigest md = MessageDigest.getInstance("md5");
    byte[] digestOfPassword = md.digest("O".getBytes("UTF-8"));
    byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
    byte[] ivBytes = Arrays.copyOf(digestOfPassword, 8);
    final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
    FileInputStream fis = new FileInputStream(new File("7za920.zip.enc"));
    FileOutputStream fos = new FileOutputStream(new File("7za920.zip"));
    decrypt(key, ivBytes, fis, fos);
    }


    private static void decrypt(SecretKey key, byte[] iv, InputStream is, OutputStream os) {
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void doCopy(InputStream is, OutputStream os) throws IOException {
    try {
        byte[] bytes = new byte[4096];
        int numBytes;
        while ((numBytes = is.read(bytes)) != -1) {
            os.write(bytes, 0, numBytes);
        }
        } finally {
            is.close();
            os.close();
        }
    }

    // only for demonstration
    private static byte[] encrypt(SecretKey key, IvParameterSpec iv, InputStream is, OutputStream os) {
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
        return cipher.getIV();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}
PhilW
  • 741
  • 6
  • 23
  • 1
    It would be helpful if you would also post the definition of `ALGORITHM`. Without it there is no mention of the padding used in the java version at all. – Perseids Apr 12 '14 at 23:48
  • Oh shoot - I added it. Thx. Also, the Java code works (encrypting and decrypting in Java works correctly. Encrypting in PHP and decrypting in Java does not. I therefore assume something is wrong with the PHP code (files differ only at the last few bytes, the rest is decrypted correctly). – PhilW Apr 13 '14 at 00:34

2 Answers2

1

Yes, your phenomenon is related to different padding strategies. The Java implementation uses PKCS5 (which is equivalent to PKCS7) while the PHP implementation uses zero padding (see under Parameteres->data), i.e. if the message is not large enough zero bytes are added.

For future questions I would ask you to do some more thorough initial research. For my answer I just followed your lead by looking in the code to see what padding was actually specified, googled for the default padding used by mcrypt and already the answer.

Perseids
  • 12,584
  • 5
  • 40
  • 64
  • Thank you. My initial research came to the same result - but how do I fix this? – PhilW Apr 13 '14 at 09:22
  • @PhilW Apparently you have to implement the padding yourself. See http://stackoverflow.com/a/7448878/371137 for an implementation. Frankly, `mcrypt` looks really bad (another example: the key is zero padded if it not long enough. _Seriously?_ O.o) If you can ditch it do so. – Perseids Apr 13 '14 at 09:56
0

Basically, as Perseids points out, mcrypt is bad for anything serious.

Funnily enough, I found that this code is working. It probably uses a lot more memory but it works.

And again, this is not safe. I only use it to confuse stupid virus-scanning proxies.

    public function encrypt($source, $target, $keyPhrase) {
            $iv = substr(md5($keyPhrase, true), 0, 8);
            $key = substr(md5($keyPhrase, true), 0, 24);
            $file = file_get_contents('../filesfile.zip');
            $Encrypt = mcrypt_encrypt(MCRYPT_3DES, $key, $file, MCRYPT_MODE_CBC, $iv);
            $fpE = fopen($target, 'wb') or die("can't open file");
            fwrite($fpE, $Encrypt);
            fclose($fpE);

    }
PhilW
  • 741
  • 6
  • 23