Sunday, May 26, 2013

AES256 between php and java


There can be several cases, where two completely independent systems communicate over an unsecure channel. The systems themselves are secure, but the communication is visible by anyone. For this scenarios, encryption is the way to go. In the following case, the AES256 encryption is demonstraded between php and java. The ise case is the following:
there is some information which should be transfered from the system written in php to the system written in java
the encryption key is kept secret, and is available for both systems

First, lets write the php end of the encryption. In this example, openssl extension is used for the php framework:

private function AES_Encode($plain_text,$key )
{
  $encoded = base64_encode(
     openssl_encrypt($plain_text, "aes-256-cbc", $key, true, 
           str_repeat(chr(0), 16)));
  return rtrim(strtr($encoded, '+/', '-_'), '=');
}
The encoded byte array is base64 encoded to be easily inluded in an url (this is whí the rtrim and strstr string manipulator functions are invoked, to provide an url-safe encoding).

The next step is to decrypt the encoded value message in java. To do this, the following methods are used:

private static void logError(final boolean encode, final String encoded,
  final String password, final Exception e) {
  LOGGER.log(Level.SEVERE, (encode) ? " encode: " : " decode: " + encoded + ":"+ password, e);
}
private static final Logger LOGGER = Logger.getLogger(Crypto.class.getName());

public static byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

public static String decode(final String str, final String key) {
  try {
    byte[] textBytes = Base64.decodeBase64(str);
    AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    SecretKeySpec newKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
    return new String(cipher.doFinal(textBytes), "UTF-8");
  } catch (NoSuchAlgorithmException e) {
    logError(false, str, key, e);
    return null;
  } catch (NoSuchPaddingException e) {
    logError(false, str, key, e);
    return null;
  } catch (InvalidKeyException e) {
    logError(false, str, key, e);
    return null;
  } catch (InvalidAlgorithmParameterException e) {
    logError(false, str, key, e);
    return null;
  } catch (IllegalBlockSizeException e) {
    logError(false, str, key, e);
    return null;
  } catch (BadPaddingException e) {
    logError(false, str, key, e);
    return null;
  } catch (UnsupportedEncodingException e) {
    logError(false, str, key, e);
    return null;
  }
}

There are several pitfalls, which can cause the AES encryption to fail.

First, the key must be exactly the same length as the specification of the used encryption algorithm states, because if shorter key is used, is in undefined what kind of data is used to fill the necessary bytes.

Second, the initial vector must be the same. In java, the ivBytes field provided the necessary value, in php, the str_repeat(chr(0), 16)) function call is responsible for that.

Third, the character encoding must match on both platform.

Last, but not least, on the java side, the UnlimitedJCEPolicy must be used if not prohibited by law at the location the code is used and/or deployed. This is needed for AES256, but not for AES128 and weaker encryptions.