skip to Main Content

I am writing a program using Java 1.6 that should generate a message of the format:

"Your invoice #123 for 100.00 is at https://my.site.com/documents/invoice?p=xxxxxxxxxxx"

with xxxxx containing an encrypted JSON string. The website at my.site.com runs PHP. It would open the URL with the invoice info:

<?php
$cipher = "AES-128-CTR";
$encryption_iv = "1234567891011121";
$encryption_key = "MyPassword";
$encryption_options = 0;
$enrypted_parm = urldecode( $this->input->get('p') );
$iv_length = openssl_cipher_iv_length( $cipher );
$decrypted_parm = openssl_decrypt( $enrypted_parm, $cipher, $encryption_key, $encryption_options, $encryption_iv );
$parms = json_decode($decrypted_parm);

echo "id=" . $parms->id . "&db=" . $parms->db . "&archived=" . $parms->archived;
?>

To generate the message from Java, I have tried this:

encryption_key = "MyPassword";
JsonObject joParms = new JsonObject();
joParms.addProperty("id", invoiceId);
joParms.addProperty("db", db);
joParms.addProperty("archived", isArchive);

SecretKeySpec secretKeySpec = new SecretKeySpec(encryption_key.concat("           ").substring(0,16).getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] byteEncryptedString = cipher.doFinal(joParms.toString().getBytes());
String encodedString = Base64.encodeBase64String(byteEncryptedString);

String message = messageTemplate.replace("{INVNO}", invNo).replace("{AMOUNT}", amount).concat(" ").concat(siteUrl).concat("?p=").concat(encodedString);

The encrypted string generated thus does not get decrypted by PHP. I think the two don’t quite match in specs. Can someone help?

2

Answers


  1. For compatibility with the PHP code, in the Java code:

    • mode and padding must be specified. Otherwise, platform-dependent default values are used.
    • the IV must be passed with IvParameterSpec (or if no user-defined IV is applied, the automatically generated IV must be retrieved with cipher.getIV(), as this is required for decryption).
    • for AES-128 a too short key must be padded with 0x00 values to 16 bytes and a too long key must be truncated after 16 bytes (as PHP/OpenSSL does).
    • a URL encoding must be carried out, since in the PHP code a URL decoding is done.

    In addition, for security reasons:

    • an encoding should be specified in getBytes(). Otherwise, a platform-dependent default encoding is used (at least in earlier Java versions).
    • no static IV should be applied, but a randomly generated IV for each encryption, which is passed along with the ciphertext (usually concatenated). Note that the IV is not a secret.
    • no password/text should be used directly as key. Instead, a key derivation function (like e.g. PBKDF2) should be applied in conjunction with a random salt.
    • a supported Java version should be used whenever possible.

    Sample code for Java 1.6:

    import java.net.URLEncoder;
    import java.util.Arrays;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import org.apache.commons.codec.binary.Base64;
    
    ...
    
    String encryption_key = "MyPa§sword";
    String iv = "1234567891011121"; 
    String plaintext = "{"key1":"value1","key2":"value2"}"; // some JSON string
    String utf8 = "UTF8";
    
    SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOf(encryption_key.getBytes(utf8), 16), "AES"); // pad/truncate key 
    Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); // specify mode and padding 
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(iv.getBytes(utf8))); // specify IV
    byte[] byteEncryptedString = cipher.doFinal(plaintext.getBytes(utf8));
    String encodedString = URLEncoder.encode(Base64.encodeBase64String(byteEncryptedString), utf8); // add Url encoding
    
    System.out.println(encodedString); // JxTY7z6mhpSTP46W0bkoJ%2BfDes0%2Fts7HWK5BBlwjTLZH
    

    which can be decrypted with the following PHP code:

    <?php
    $ciphertext = "JxTY7z6mhpSTP46W0bkoJ%2BfDes0%2Fts7HWK5BBlwjTLZH";
    $cipher = "AES-128-CTR";
    $encryption_iv = "1234567891011121";
    $encryption_key = "MyPa§sword";
    $encryption_options = 0;
    $enrypted_parm = urldecode($ciphertext);
    $iv_length = openssl_cipher_iv_length( $cipher );
    $decrypted_parm = openssl_decrypt( $enrypted_parm, $cipher, $encryption_key, $encryption_options, $encryption_iv );
    $parms = json_decode($decrypted_parm);
    print_r($parms); /* stdClass Object
                        (
                            [key1] => value1
                            [key2] => value2
                        )
                     */
    ?>
    
    Login or Signup to reply.
  2. One common approach is using AES encryption with a shared secret key. Below is an example (Ecnrypt Java):

    import javax.crypto.Cipher; import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    import java.util.Base64;
    
    public class Encryptor {
    private static final String ALGORITHM = "AES";
    
    public static String encrypt(String data, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedData);
    }
    
    public static void main(String[] args) throws Exception {
        String originalString = "Hello, PHP!";
        String key = "1234567890123456"; // 16-byte key (128-bit)
        
        String encryptedString = encrypt(originalString, key);
        System.out.println("Encrypted: " + encryptedString);
    }
    }
    

    Decrypt PHP example :

        <?php
    function decrypt($encrypted, $key) {
        $cipher = "aes-128-ecb"; // Must match the Java AES mode
        $decoded = base64_decode($encrypted);
        $decrypted = openssl_decrypt($decoded, $cipher, $key, OPENSSL_RAW_DATA);
        return $decrypted;
    }
    
    $encryptedString = "EncryptedStringFromJava"; // Replace with actual encrypted string
    $key = "1234567890123456"; // 16-byte key (same as used in Java)
    
    $decryptedString = decrypt($encryptedString, $key);
    echo "Decrypted: " . $decryptedString;
    ?>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search