Petit travaux pratiques:
Un développeur m'a récemment confié une application DOTNET utilisant un chiffrement TripleDES.
Le but de la manoeuvre était de pouvoir échanger des données chiffrées entre cette application DOTNET et une application php hébergée sur un serveur Ubuntu.
Retour d'exérience.
Pour commencer, le codeTripleDES provenant d'Internet a été facilement retrouvé.
http://nguyendoanhoa.googlecode.com/svn-history/r59/trunk/QuanLyTruongHoc_App/MyControls/CryptorEngine.cs
Les lignes essentielles sont les suivantes:
|
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
|
|
Ce choix d'initialisation peu judicieux implique plusieurs problèmes de compatibilité:
1. la clé de chiffrement est une clé courte de 128 bit (hash MD5) et non pas 192 bit.
Hors PHP ne sait pas utiliser des clés de 128 bit.
2. Vous noterez qu'il n'y a pas de vecteur d'initialisation.
Hors PHP en veut un de longueur 8.
3. Le mode de Padding est PKC7.
Hors PHP fait du padding avec des octets NULL.
Pour terminer, on peut s'interroger sur la pertinence d'utiliser ECB, mais passons...
A noter également que le code c# convertit les entrées en UTF8
Solution:
Pour assurer la compatibilité PHP, il faut tout d'abord régler le problème de la longueur de clé.
Triple DES fonctionne de la façon suivante:
La clé de 192 bit est divisée en 3 clés, puis le chiffrement s'effectue de la façon suivante en mode EDE par défaut (Encrypt Decrypt Encrypt):
Encrypt(K1) -> Decrypt(K2) -> Encrypt(K3).
Avec une clé de 128 bit, le chiffrement devient
Encrypt(K1) -> Decrypt(K2) -> Encrypt(K1)
En PHP, il suffit donc de couper la clé de 128bit en 2 et de rajouter à la suite de la clé les 64 premiers bits.
La clé doit ensuite être convertie en binaire.
|
$key = md5($initialkey);
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$key .= substr($key, 0, mcrypt_enc_get_key_size($td));
$key = utf8_encode($key);
// Convert to bin
$key_bin = pack('H*', $key);
|
Ensuite, il faut passer un IV null et désactiver les warnings sinon PHP vous remonte un warning sur l'utilisation d'un IV invalide.
Pour le problème de padding PKCS7, un petit bout de code à conserver dans les tablettes:
DES chiffre des blocs de 8 octets. Il faut rajouter au dernier bloc des octets pour compléter les blocs à 8 caractères.
Par défaut PHP rajoute des NULL. En PKCS7, le caractère rajouté est le code ascii du nombre d'octets manquants.
S'il manque 3 caractères, il rajoute 3x le code asci de 3 en fin de chaine.
$block = mcrypt_get_block_size('tripledes', 'ecb');
$len = strlen($buffer);
$padding = $block - ($len % $block);
$buffer .= str_repeat(chr($padding),$padding); |
Il ne reste plus qu'à mettre ensemble ces éléments:
|
<?php
error_reporting(0);
$buffer ="manu";
$initialkey = "abcd1234efgh5678";
// Calcule le hash MD5 et génère 128 bits
$key = md5($initialkey);
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
// Transforme en clé 192 bit.
$key .= substr($key, 0, mcrypt_enc_get_key_size($td));
$key = utf8_encode($key);
// Convert to bin
$key_bin = pack('H*', $key);
$iv = NULL;
$block = mcrypt_get_block_size('tripledes', 'ecb');
$len = strlen($buffer);
$padding = $block - ($len % $block);
$buffer .= str_repeat(chr($padding),$padding);
// Encode en UTF8
$buffer = utf8_encode($buffer);
/* Initialise le module de chiffrement */
if (mcrypt_generic_init($td, $key_bin, $iv) != -1)
{
/* Chiffre les données */
$Result = mcrypt_generic($td, $buffer);
mcrypt_generic_deinit($td);
/* Nettoye */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
// hex encode the return value
echo bin2hex($Result);
echo "<br/>";
echo base64_encode($Result);
?>
|