01 October 2008

PHP encrypt and Java decrypt

For a project I needed to transfer data from a PHP server to a Java server. The data was a PHP array and it must be serialized in a portable way. Because it must be transferred safely, I needed encryption. And because encrypted data is binary data and has to be transferred in the query part of the URL, I needed encoding.

At the sender side I had to do this steps:
  • Serialization: For transferring data structures between different programming languages I like JSON. JSON encoding is small and easy to implement; you can find libraries for almost each language.
  • Encryption: I tried to decrypt a PHP Crypt_RSA encrypted string with Java, but PHP and Java were behaving slightly different. I could not manage to get it working. After a failed attempt with Crypt_Blowfish I searched the internet again. And I found a file-based solution, so I decided to use AES encryption.
  • Encoding: The most common string encoding to use for binary data is Base64 encoding. However Java does not have native support for it, there are enough libraries. You still have to urlencode the resulting string.
At the receiver side I did the opposite steps:
  • Decoding: Urldecoding depending of your servlet engine. Base64 decoding with an Apache library.
  • Decryption: AES decryption.
  • Deserialization: Converting the JSON string to a native Java object using a JSON library.

The code

For testing I used this PHP code to encrypt on the command-line:
<?php
if ($_SERVER['argc'] != 2) {

file_put_contents('php://stderr',
"ERROR: specify one string to be encrypted\n\n");
exit(
1);
}
// set keys
$secret_key = "1234567890123456";
$iv = "abcdefghijklmnop";
$plaintext = $_SERVER['argv'][1];
// encryption
$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$secret_key, $plaintext, MCRYPT_MODE_CBC, $iv);
// base64 encoding
$enc64 = base64_encode($enc);
echo
$enc64 . "\n";
?>

And this Java code to decrypt on the command-line:
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import org.apache.commons.codec.binary.Base64;

public class decrypt {

public static void main(String args[]) throws Exception {
if (args.length != 1) {
System.err.println("ERROR: specify a base64 encoded encrypted string as parameter\n");
System.exit(1);
}
// define keys
String secret = new String("HQ5s-YmCzwwhjIcH");
String iv = new String("mZsewcXovdHVMRsE");
// base64 decoding
int i = 0;
String enc64 = args[0];
byte[] enc64bytes = enc64.getBytes();
byte[] dec64bytes = Base64.decodeBase64(enc64bytes);
// because the toString method does not work you have to cast each array element separately
String dec64 = new String();
for (i = 0; i < dec64bytes.length; i++) {
dec64 += (char)dec64bytes[i];
}
// set decrypt params
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
ByteArrayInputStream fis = new ByteArrayInputStream(dec64bytes);
CipherInputStream cis = new CipherInputStream(fis, cipher);
ByteArrayOutputStream fos = new ByteArrayOutputStream();
// decrypting
byte[] b = new byte[8];
while ((i = cis.read(b)) != -1) {
fos.write(b, 0, i);
}
fos.flush();
fos.close();
cis.close();
fis.close();
System.out.println(fos.toString());
}

}

I have studied long on why the base64 decoded byte array could not be converted to a string with the toString method. I think Java supposes it is a Unicode string because it is binary and then I get too few character. Use the loop it is ok, because that handles each character separately. If your source is binary, you should consider to use a loop instead of the fos.toString() call.

1 comment:

Unknown said...

Hi,
Do you have any idea how to encrypt in Java and decrypt in PHP using the same AES algorithm?
Thanks,
Jeyanthi