Encryption Between PHP & Java

16,216

Solution 1

A working version can be found - https://github.com/chaudhuri-ab/CrossPlatformCiphers

Some things to keep in mind is the that if you do not specify OPENSSL_RAW_DATA in PHP the data will be encrypted as base64. That was throwing me off.

PHP:

class PHP_AES_Cipher {

    private static $OPENSSL_CIPHER_NAME = "aes-128-cbc"; //Name of OpenSSL Cipher 
    private static $CIPHER_KEY_LEN = 16; //128 bits

    /**
     * Encrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * @param type $key - key to use should be 16 bytes long (128 bits)
     * @param type $iv - initialization vector
     * @param type $data - data to encrypt
     * @return encrypted data in base64 encoding with iv attached at end after a :
     */

    static function encrypt($key, $iv, $data) {
        if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0"); //0 pad to len 16
        } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); //truncate to 16 bytes
        }

        $encodedEncryptedData = base64_encode(openssl_encrypt($data, PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, $iv));
        $encodedIV = base64_encode($iv);
        $encryptedPayload = $encodedEncryptedData.":".$encodedIV;

        return $encryptedPayload;

    }

    /**
     * Decrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * @param type $key - key to use should be 16 bytes long (128 bits)
     * @param type $data - data to be decrypted in base64 encoding with iv attached at the end after a :
     * @return decrypted data
     */
    static function decrypt($key, $data) {
        if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0"); //0 pad to len 16
        } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); //truncate to 16 bytes
        }

        $parts = explode(':', $data); //Separate Encrypted data from iv.
        $decryptedData = openssl_decrypt(base64_decode($parts[0]), PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, base64_decode($parts[1]));

        return $decryptedData;
    }

}

Java:

package ciphers;

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

import java.util.Base64;

public class Java_AES_Cipher {

    private static String CIPHER_NAME = "AES/CBC/PKCS5PADDING";
    private static int CIPHER_KEY_LEN = 16; //128 bits

    /**
     * Encrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * 
     * @param key  - key to use should be 16 bytes long (128 bits)
     * @param iv - initialization vector
     * @param data - data to encrypt
     * @return encryptedData data in base64 encoding with iv attached at end after a :
     */
    public static String encrypt(String key, String iv, String data) {
        try {
            if (key.length() < Java_AES_Cipher.CIPHER_KEY_LEN) {
                int numPad = Java_AES_Cipher.CIPHER_KEY_LEN - key.length();

                for(int i = 0; i < numPad; i++){
                    key += "0"; //0 pad to len 16 bytes
                }

            } else if (key.length() > Java_AES_Cipher.CIPHER_KEY_LEN) {
                key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes
            }


            IvParameterSpec initVector = new IvParameterSpec(iv.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initVector);

            byte[] encryptedData = cipher.doFinal((data.getBytes()));

            String base64_EncryptedData = Base64.getEncoder().encodeToString(encryptedData);
            String base64_IV = Base64.getEncoder().encodeToString(iv.getBytes("UTF-8"));

            return base64_EncryptedData + ":" + base64_IV;

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * Decrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * @param key - key to use should be 16 bytes long (128 bits)
     * @param data - encrypted data with iv at the end separate by :
     * @return decrypted data string
     */

    public static String decrypt(String key, String data) {
        try {

            String[] parts = data.split(":");

            IvParameterSpec iv = new IvParameterSpec(Base64.getDecoder().decode(parts[1]));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            byte[] decodedEncryptedData = Base64.getDecoder().decode(parts[0]);

            byte[] original = cipher.doFinal(decodedEncryptedData);

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

}

Solution 2

I can't figure out why your method fails. By the way, Here's how i did it,

Java

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;

public class MyClass {

    public static void main(String[] args) {
        String data = "Arnab C";
        final String enc = DarKnight.getEncrypted(data);
        System.out.println("Encrypted : " + enc);
        System.out.println("Decrypted : " + DarKnight.getDecrypted(enc));
    }

    static class DarKnight {

        private static final String ALGORITHM = "AES";

        private static final byte[] SALT = "tHeApAcHe6410111".getBytes();// THE KEY MUST BE SAME
        private static final String X = DarKnight.class.getSimpleName();

        static String getEncrypted(String plainText) {

            if (plainText == null) {
                return null;
            }

            Key salt = getSalt();

            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.ENCRYPT_MODE, salt);
                byte[] encodedValue = cipher.doFinal(plainText.getBytes());
                return Base64.encode(encodedValue);
            } catch (Exception e) {
                e.printStackTrace();
            }

            throw new IllegalArgumentException("Failed to encrypt data");
        }

        public static String getDecrypted(String encodedText) {

            if (encodedText == null) {
                return null;
            }

            Key salt = getSalt();
            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.DECRYPT_MODE, salt);
                byte[] decodedValue = Base64.decode(encodedText);
                byte[] decValue = cipher.doFinal(decodedValue);
                return new String(decValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        static Key getSalt() {
            return new SecretKeySpec(SALT, ALGORITHM);
        }

    }
}

PHP

<?php

$key = "tHeApAcHe6410111";

function encrypt($text,$key){
     $block = mcrypt_get_block_size('rijndael_128', 'ecb');
     $pad = $block - (strlen($text) % $block);
     $text .= str_repeat(chr($pad), $pad);
     return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB));
}

function decrypt($str, $key){ 
     $str = base64_decode($str);
     $str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
     $block = mcrypt_get_block_size('rijndael_128', 'ecb');
     $pad = ord($str[($len = strlen($str)) - 1]);
     $len = strlen($str);
     $pad = ord($str[$len-1]);
     return substr($str, 0, strlen($str) - $pad);
}

$enc =  encrypt("Arnab C",$GLOBALS['key']);
echo "Encrypted : ".$enc."</br>";
$dec = decrypt($enc,$GLOBALS['key']);
echo "Decrypted : ".$dec;

Java Output

Encrypted : PJG1Uu6SjJuuVGf7ApuHAw==

Decrypted : Arnab C

PHP Output

Encrypted : PJG1Uu6SjJuuVGf7ApuHAw==

Decrypted : Arnab C

Solution 3

I used the solution from theapache64, but in PHP 7.2 it stopped working since Mcrypt has been deprecated and later removed. So I changed the code and it works:

function encrypt($data, $key) {
    return base64_encode(openssl_encrypt($data, "aes-128-ecb", $key, OPENSSL_RAW_DATA));
}

function decrypt($data, $key) {
    return openssl_decrypt(base64_decode($data), "aes-128-ecb", $key, OPENSSL_RAW_DATA);
}
Share:
16,216
ControlAltDelete
Author by

ControlAltDelete

Updated on June 04, 2022

Comments

  • ControlAltDelete
    ControlAltDelete almost 2 years

    I was looking to encrypt data between a PHP server and a Java Client. Individually the code works fine and I would like to stick with OpenSSL on the PHP server.

    Do any of you see anything that I am missing here as I get an error when trying to decode the PHP encrypted string:

    PHP:

    <?php
    
    $iv = 'fedcba9876543210'; #Same as in JAVA
    $key = '0123456789abcdef'; #Same as in JAVA
    
    
    
    $ciphers = openssl_get_cipher_methods(FALSE);
    $ciphers_and_aliases = openssl_get_cipher_methods(true);
    $cipher_aliases = array_diff($ciphers_and_aliases, $ciphers);
    
    print_r($ciphers);
    
    //print_r($cipher_aliases);
    
    
    // DEFINE our cipher
    define('AES_CBC', 'aes-128-cbc');
    // Generate a 256-bit encryption key
    // This should be stored somewhere instead of recreating it each time
    $encryption_key = "test_key";
    // Generate an initialization vector
    // This *MUST* be available for decryption as well
    //$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(AES_CBC));
    
    // Create some data to encrypt
    $data = "Hello World!!!";
    $data_b64= base64_encode($data);
    echo "Before encryption: $data<br><br>Before Base64: $data_b64<br><br>";
    // Encrypt $data using aes-256-cbc cipher with the given encryption key and
    // our initialization vector. The 0 gives us the default options, but can
    // be changed to OPENSSL_RAW_DATA or OPENSSL_ZERO_PADDING
    $encrypted = openssl_encrypt($data_b64, AES_CBC, $encryption_key, 0, $iv);
    $len = strlen($encrypted);
    echo "Encrypted Len: $len  <br><br>";
    $encrypted64 = base64_encode($encrypted);
    echo "Encrypted b64: $encrypted64<br><br>";
    // If we lose the $iv variable, we can't decrypt this, so:
    // - $encrypted is already base64-encoded from openssl_encrypt
    // - Append a separator that we know won't exist in base64, ":"
    // - And then append a base64-encoded $iv
    $encrypted = $encrypted64 . ':' . base64_encode($iv);
    echo "Encrypted: $encrypted<br><br>";
    // To decrypt, separate the encrypted data from the initialization vector ($iv).
    $parts = explode(':', $encrypted);
    // $parts[0] = encrypted data
    // $parts[1] = base-64 encoded initialization vector
    // Don't forget to base64-decode the $iv before feeding it back to
    //openssl_decrypt
    $decrypted64 = openssl_decrypt(base64_decode($parts[0]), AES_CBC, $encryption_key, 0, base64_decode($parts[1]));
    $decrypted = base64_decode($decrypted64);
    echo "Decrypted: $decrypted\n";
    ?>
    

    PHP output:

    Before encryption: Hello World!!!

    Before Base64: SGVsbG8gV29ybGQhISE=

    Encrypted Len: 44

    Encrypted b64: U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0=

    Encrypted: U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0=:ZmVkY2JhOTg3NjU0MzIxMA==

    Decrypted: Hello World!!!

    Java Code:

    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import java.util.Base64;
    import java.security.*;
    
    public class Sandbox {
    
        public static String encrypt(String key, String initVector, String value) {
            try {
                IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
                SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
    
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
    
                byte[] encrypted = cipher.doFinal(Base64.getEncoder().encode(value.getBytes()));
                System.out.println("encrypted string: "
                        + Base64.getEncoder().encodeToString(encrypted));
    
                return Base64.getEncoder().encodeToString(encrypted);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
            return null;
        }
    
        public static String decrypt(String key, String initVector, String encrypted) {
            try {
                IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
                SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
    
    
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
    
                byte[] temp = Base64.getDecoder().decode(encrypted);
    
                System.out.println((new String(temp)).length());
                byte[] original = cipher.doFinal(temp);
                original = Base64.getDecoder().decode(original);
    
                return new String(original);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
            return null;
        }
    
        public static void main(String[] args) {
            String key = "0123456789abcdef"; // 128 bit key
            String initVector = "fedcba9876543210"; // 16 bytes IV
    
    //        for (Provider provider : Security.getProviders()) {
    //            System.out.println(provider.getName());
    //            for (String key2 : provider.stringPropertyNames()) {
    //                System.out.println("\t" + key2 + "\t" + provider.getProperty(key2));
    //            }
    //        }
            System.out.println(decrypt(key, initVector,
                    encrypt(key, initVector, "Hello World!!!")));
    
            System.out.println(decrypt(key, initVector, "R090NDcvclAyY2E1cmxLWG9kSGlnUktHdEI5U05sRGxNdWF4NFFjUUV0OD0="));
        }
    }
    

    Java Output:

    > 
    30
    Hello World!!!
    
    44
    
    null
    
    javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
        at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:913)
        at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
        at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
        at javax.crypto.Cipher.doFinal(Cipher.java:2165)
        at Sandbox.decrypt(Sandbox.java:43)
        at Sandbox.main(Sandbox.java:67)
    BUILD SUCCESSFUL (total time: 1 second)
    
  • ControlAltDelete
    ControlAltDelete almost 7 years
    Does it work for you with OpenSSL on the php side vs Mcrypt, which is a no longer actively developed library? Also, if you used an Iv does it work for you? Thx
  • ControlAltDelete
    ControlAltDelete almost 7 years
    I did got it working. OpenSSL options made a difference. Understanding that by default OpenSSL in PHP will produces a Base64 encoded string. Answer Above. Thx and +1 for your reply.
  • theapache64
    theapache64 almost 7 years
    Okay brother (y)
  • PHP User
    PHP User about 5 years
    This is OK but with API >=26 and I can't find similar solution for lower API levels can you help please?
  • Maarten Bodewes
    Maarten Bodewes over 4 years
    A key is not a string, you need to use algorithms like PBKDF2 to encrypt with a password. And CBC mode is completely insecure for transporting messages, read up on plaintext / padding oracle attacks.
  • Maarten Bodewes
    Maarten Bodewes over 4 years
    Warning: this code uses ECB mode and no password derivation method, and is therefore insecure.
  • Kairos
    Kairos over 4 years
    Hi Maarten, what could be a better way? Yours, Albert van Harten
  • Maarten Bodewes
    Maarten Bodewes over 4 years
    A password based key derivation method like PBKDF2 or Argon2 and e.g. GCM mode. But for transport mode security you really need a secure transport mode (TLS, SSH, Signal etc.) - there is no real way around that.
  • Kairos
    Kairos over 4 years
    Thanks, Maarten. I am using TLS for transporting data, no doubt about that :) I do use a password derivation method - but probably not a method you meant :) Albert van Harten
  • Maarten Bodewes
    Maarten Bodewes over 4 years
    You are not using any key derivation at all, you're just using a salt as a key, all stringified. If you cannot distinguish between a salt and a key, you may not want to post code for encryption. Your code is utterly insecure and as providing security is the goal of encryption, I personally don't see any use for it.
  • Kairos
    Kairos over 4 years
    I apologize for the fact that I haven't posted my entire code ...
  • hoomb
    hoomb about 3 years
    I cannot find where you've defined the variable $str in your PHP Code
  • Nasz Njoka Sr.
    Nasz Njoka Sr. almost 2 years
    @hoomb just replace $str with $key and one thing I've noticed on the if(key.lengh()) instead of using else if just use else for both java and php