Ik had behoefte aan een manier om strings van Silverlight te encrypten en die met PHP te decrypten. Mijn vraag was hoe doe je dat?
In eerste instantie dacht ik dat het nodig was om een encryptie/decryptie algoritme te kiezen en daarvan een versie te hebben voor Silverlight en voor PHP. Daarbij kwam ik op een elegant algoritme uit, dat niet te veel processorkracht nodig had het TEA-algoritme. Maar daarmee was het probleem niet direct lekker opgelost. Want je moet dan ook een implementatie hebben. In ons geval aan de server kant een PHP-versie. En aan de client kant het liefst een C# versie. Maar bij het zoeken naar zo’n implementatie is het erg oppassen geblazen. Want er blijken verschillende varianten van het TEA algoritme te zijn. Een originele versie (TEA), een verbeterde versie (XTEA) en een nogmaals verbeterde versie (XXTEA). Soms is het duidelijk om welke versie het gaat, maar soms moet je het uit de code zelf halen. Bovendien geven sommige implementaties slechts een interface naar ruwe encryptie / decryptie methodes, waarbij je byte-arrays van een lengte van 8 bytes converteert. Om dan strings te kunnen converteren, moet je nog wat extra code maken, die string pad met spaties.
Al met al had ik op basis van een aantal artikelen een C# versie geproduceerd, die compatibel was met een PHP en Javascript implementatie van Babelfish.nl. Maar na verschillende mislukte pogingen om de Silverlight zijde met de PHP-zijde te laten praten, heb ik het maar opgegeven om op deze weg verder te gaan.
Volgende stap was om gebruik te maken van algoritmes, die al standaard in Silverlight en PHP zitten. En dan maar hopen dat die algoritmes ook met elkaar willen praten. Dat bleek nog een hele klus. Maar uiteindelijk is het me gelukt om een goed werkend encryptie / decryptie mechanisme te bewerkstelligen tussen Silverlight en PHP. Het algoritme dat ik daarvoor heb gebruikt is het AES algoritme. Het artikel, waar ik uiteindelijk de code aan te danken heb was: The PHP-silverlight Cryptography Challenge (hoewel in dat artikel nog een foutje zat met verkeerde lengtes van de key en de initialization vector en, naar zeggen van de auteur, de MD5 hash over het password). Verder heb ik veel hulp gehad van een artikel van Chilkat Software, Inc.:Understanding PHP AES Encryption
Uiteindelijk ziet mijn code er nu als volgt uit:
AesCryptographer.cs
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Aduba.Cryptography
{
public class AesCryptographer
{
private byte[] mKey = null;
private byte[] mIV = null;
public string Key { set { mKey = Encoding.UTF8.GetBytes(value); } }
public string IV { set { mIV = Encoding.UTF8.GetBytes(value); } }
public string EncryptAes128(string plainText)
{
byte[] data;
byte[] encryptedData;
// convert the plain text to a byte array
data = Encoding.UTF8.GetBytes(plainText);
// encrypt the plain text
encryptedData = EncryptAes128(data);
// convert to Base64 in order to transfer over internet.
return Convert.ToBase64String(encryptedData);
}
public byte[] EncryptAes128(byte[] plainData)
{
MemoryStream MStream = new MemoryStream(); // Memory stream to write encrypted data to.
CryptoStream CryptoStream = null;
AesManaged A128 = new AesManaged();
try
{
//Create aes128 Encryptor from this instance.
ICryptoTransform A128Encrypt = A128.CreateEncryptor(mKey, mIV);
//Create Crypto Stream that transforms memory stream using aes encryption.
using (CryptoStream = new CryptoStream(MStream, A128Encrypt, CryptoStreamMode.Write))
{
//Write out and flush AES encrypted data to memory stream.
CryptoStream.Write(plainData, 0, plainData.Length);
CryptoStream.FlushFinalBlock();
}
// Return encrypted data.
return MStream.ToArray();
}
catch (Exception ex)
{
return null;
}
finally
{
if (CryptoStream != null) { CryptoStream.Close(); };
if (MStream != null) { MStream.Close(); }
}
}
public byte[] DecryptAes128(byte[] EncryptedByteData)
{
MemoryStream MStream = null; // Memory stream to write encrypted data to.
CryptoStream CryptoStreamDecr = null;
AesManaged A128 = new AesManaged();
try
{
//Create aes128 instance and Decryptor.
ICryptoTransform A128Decrypt = A128.CreateDecryptor(mKey, mIV);
//Create crypto stream set to read and do a AES decryption transform on incoming bytes.
MStream = new MemoryStream(EncryptedByteData);
CryptoStreamDecr = new CryptoStream(MStream, A128Decrypt, CryptoStreamMode.Read);
//Get the decrypted bytes.
byte[] DestArray = new BinaryReader(CryptoStreamDecr).ReadBytes((int)MStream.Length * 2);
//Return decrypted data.
return DestArray;
}
catch (Exception ex)
{
return null;
}
finally
{
if (MStream != null) { MStream.Close(); }
}
}
}
}
En de PHP-code voor de decryptie:
function aes128cbcDecrypt($key, $encrypted_text, $iv) {
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $key, $iv);
$decrypted = mdecrypt_generic($td, $encrypted_text);
$decrypted = removePadding($decrypted);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return trim($decrypted);
}
function removePadding($text) {
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$packing = ord($text{strlen($text) - 1});
if ($packing and ($packing < $block)) {
for ($P = strlen($text) - 1; $P >= strlen($text) - $packing; $P--) {
if (ord($text{$P}) != $packing) {
$packing = 0;
}
}
}
return substr($text, 0, strlen($text) - $packing);
}
Deze decryptie functie wordt aangeroepen door:
$encryptedData = $_REQUEST['Cipher']; $data = base64_decode($encryptedData); $plainText = aes128cbcDecrypt($key, $data, $iv);
Een volledige PHP AES Klasse is te vinden via de volgende link:
http://www.phpclasses.org/browse/file/23124.html