Wednesday, July 21, 2010

Xifrat i desxifrat entre Java i PHP

Se'ns va presentar la necessitat d'implementar un xifrat (encriptació no figura al diccionari) de dades des d'una aplicació Java que després s'havia de desxifrar des d'un PHP.

En teoria els algorismes de xifrat són genèrics i independents del llenguatge de programació. En la realitat sempre hi ha sorpreses. Ja vaig haver de patir per una altra interoperabilitat similar entre PL/SQL i Java, així que anava avisat.

Per xifrar i desxifrar des de Java es fa amb la Java Cryptography Architecture (JCA). Cal indicar l'algorisme i el tipus de padding, i sel·leccionar i una clau i un vector d'inicialització.

Per fer-ho des de PHP cal fer servir el mòdul mcrypt. En funció del sistema operatiu cal revisar que estigui instal·lat el mòdul (revisant el phpinfo) i mirar quins ciphers i modes són actius.

Caldrà triar un algorisme de xifrat que sigui vàlid als dos entorns i nosaltres vam triar compartir el resultat en format hexadecimal. També és important triar la mida de la clau i del vector d'incialització. Per encriptació de 128-bits la clau ha de ser de 16 bytes i per 256-bits de 32 bytes.

El problema entre els dos llenguatges és com tracten el padding, el farcit de caràcters fins arribar a la mida de cadena correcta. Mcrypt sempre farceix amb \0 (null) i no pas amb espais.

Jugant amb trims i NoPaddings de Java s'aconsegueix la màgia. Nosaltres vam triar encriptació amb AES de 128 bits.

El xifrat a Java es fa així:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
...
private String padString(String source) {
char paddingChar = ' ';
int size = 16;
int padLength = size - source.length() % size;

for (int i = 0; i < padLength; i++) {
source += paddingChar;
}

return source;
}

public String encrypt(String text) {
String encrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (Exception e) {
logger.error("Error creating cipher instance", e);
}

try {
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
if (cipher != null) {
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] res = cipher.doFinal(padString(text).getBytes());
encrypted = asHex(res);
}
} catch (Exception e) {
logger.error("Error ciphering", e);
}

return encrypted;
}


El desxifrat a PHP el fem així:
function decrypt($param) {
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$key128 = '0123456789ABCDEF';
$iv = 'FEDCBA9876543210';
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key128, hex2bin($param), MCRYPT_MODE_CBC, $iv);
$decrypted = trim($decrypted);
return $decrypted;
}

No comments: