Link to home
Start Free TrialLog in
Avatar of wwarby
wwarby

asked on

Two-way Encryption that works consistently across Classic ASP and PHP

I have what should be a fairly simple requirement. I want two pairs of functions, to encode and decode a string in ASP and PHP respectively, where the encryption key is hard-coded into the functions. It isn't to protect a bank vault so as long as the only vulnerability is access to the source code, that's good enough. I'm not wedded to any particular implementation method or algorithm so long as it's a good algorithm like AES or TripleDES and not a home-made one or something. The critical thing is that a string encoded with the function in ASP must be decoded correctly in PHP and vice-versa. I am licenced for Chilkat.Crypt so I planned to use that on the ASP side. I'm having terrible trouble writing functions that work consistently across the two platforms, although I feel like I'm very close to a solution. Current working code attached (the keys are made up and not sensitive).
<?php
	function aes_encrypt_string($text) {
        return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, 'GpAeScw2LQWaYnRddbh4cPksce76gQ1', $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND))));
    }
    function aes_decrypt_string($text) {
        return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, 'GpAeScw2LQWaYnRddbh4cPksce76gQ1', base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND)));
    }
	
	echo('Encrypted: '.aes_encrypt_string('ch1ck!NLi77L£').'<br /><br />');
	//Output: TWF88j6Tt/YHrIyCqMCWiA==   <-- note the output is completely different from the ASP output
	
	echo('Decrypted (PHP): '.aes_decrypt_string('TWF88j6Tt/YHrIyCqMCWiA==').'<br /><br />');
	//Expected output: ch1ck!NLi77L£
	//Actual output:   ch1ck!NLi77L£   <-- note the insertion of an "Â" character
	
	echo('Decrypted (ASP): '.aes_decrypt_string('OIKi5I0lkUU3m5dt7fBDUw==').'<br /><br />');
	//Expected output: ch1ck!NLi77L£
	//Actual output:   ch1ck!NLi77L£   <-- note the insertion of an "Â" character and two weird characters on the end
?>

Open in new window

<%
	Function AESEncryptString(text)
		Dim objCrypt
		Set objCrypt = CreateObject("Chilkat.Crypt2")
		With objCrypt
			.UnlockComponent("LONDONCrypt_SkO3CEBVVREY")
			.CryptAlgorithm = "rijndael"
			.CipherMode = "ECB"
			.EncodingMode = "base64"
			.SetEncodedKey "GpAeScw2LQWaYnRddbh4cPksce76gQ1", "ascii"
			.Charset = "utf-8"
			AESEncryptString = .EncryptStringENC(text)
		End With
	End Function
	
	Function AESDecryptString(text)
		Dim objCrypt
		Set objCrypt = CreateObject("Chilkat.Crypt2")
		With objCrypt
			.UnlockComponent("LONDONCrypt_SkO3CEBVVREY")
			.CryptAlgorithm = "rijndael"
			.CipherMode = "ECB"
			.EncodingMode = "base64"
			.SetEncodedKey "GpAeScw2LQWaYnRddbh4cPksce76gQ1", "ascii"
			.Charset = "utf-8"
			AESDecryptString = .DecryptStringENC(text)
		End With
	End Function
	
	'Note the use of a string for encryption containing a "£" character. When I tried encrypting the string "This is a test", everything worked perfectly (albeit with very slightly different output from the encryption functions but both decryption functions decrypted both outputs perfectly.)
	
	Response.Write "Encrypted: " & AESEncryptString("ch1ck!NLi77L£") & "<br /><br />"
	'Output: OIKi5I0lkUU3m5dt7fBDUw==   <-- note the output is completely different from the PHP output
	
	Response.Write "Decrypted (ASP): " & AESDecryptString("TWF88j6Tt/YHrIyCqMCWiA==") & "<br /><br />"
	'Expected output: ch1ck!NLi77L£
	'Actual output:   ch1ck!NLi77L£   <-- note the output is correct 
	
	Response.Write "Decrypted (PHP): " & AESDecryptString("OIKi5I0lkUU3m5dt7fBDUw==") & "<br /><br />"
	'Expected output: ch1ck!NLi77L£
	'Actual output:   ch1ck!NLi77L£   <-- again note the output is correct even though the encrypted string is completely different
%>

Open in new window

Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

I can show you a teaching example of how I have done this in all-PHP.  Maybe you can adapt those principles to the ASP side.  Note the use of the base64 functions.  Encryption will create a lot of bizarre binary characters that do not play well with HTTP.  base64 encoding makes the encrypted data into something that can be sent over HTTP.  It also increases the size of the transmission string by a lot, but that is just the price you pay for safety. :-)  HTH, ~Ray
<?php // RAY_crypt.php
error_reporting(E_ALL);

class Encryption
{
    private $eot;
    private $key;
    private $ivs;
    private $iv;

    public function __construct()
    {
        $this->eot = '___EOT'; // END OF TEXT DELIMITER
        $this->key = 'quay';
        $this->ivs = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $this->iv  = mcrypt_create_iv($this->ivs);
    }

    public function Encrypt($text)
    {
        $text .= $this->eot; // APPEND END OF TEXT DEIMITER
        $data = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, $text, MCRYPT_MODE_ECB, $this->iv);
        $data = base64_encode($data);
        return $data;
    }

    public function Decrypt($text)
    {
        $text = base64_decode($text);
        $data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, $text, MCRYPT_MODE_ECB, $this->iv);
        $data = explode($this->eot, $data); // REMOVE END OF TEXT DELIMITER
        return $data[0];
    }
}

// INSTANTIATE THE CLASS
$crypt = new Encryption();

// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$encoded = '';
$decoded = '';

// IF ANYTHING WAS POSTED
if (!empty($_POST["clearstring"]))
{
    $encoded = $crypt->Encrypt($_POST["clearstring"]);
    echo "<br/>{$_POST["clearstring"]} YIELDS "; var_dump($encoded);
}

if (!empty($_POST["cryptstring"]))
{
    $decoded = $crypt->Decrypt($_POST["cryptstring"]);
    echo "<br/>{$_POST["cryptstring"]} YIELDS "; var_dump($decoded);
}

?>
<form method="post">
<input name="clearstring" value="<?=$decoded?>" />
<input type="submit" value="ENCRYPT" />
<br/>
<input name="cryptstring" value="<?=$encoded?>" />
<input type="submit" value="DECRYPT" />
</form>

Open in new window

Avatar of Beverley Portlock
Many of these codes are BLOCK ciphers and must be made up to a given block length. You may need to pad your stings. I had similar behaviour when doing some Blowfish encryption and wound up padding code

          $dataLen = strlen( $data );
          if ( ( $dataLen % $this->blockSize ) > 0  ) {
               $extra   = $this->blockSize - ( $dataLen % $this->blockSize );
               $data    = $data . str_pad(" ", $extra);
          }

Once I did that everything was ticketty-boo!
"pad your stings"????

Whoops! I meant "pad your strings".
Avatar of wwarby
wwarby

ASKER

@Ray:

I hadn't thought it worth mentioning before (stupidly) but I'm stuck on PHP 4 (the code I'm writing is an extension to a third party product and to much huge annoyance they still have not upgraded to PHP 5). When your code didn't work on my PHP 4 server (it didn't like the keyword "private") I tried it on a PHP 5 server, and from there discovered the core of the problem:- there's something wrong with PHP 4 that breaks both your code and mine. I tried an identical test file on both servers. They produce completely different encoded results, and the one produced in PHP 4 fails to decode correctly on either PHP 4 or PHP 5. The attached sample illustrates the problem.

So I guess the focus of my question has shifted to: how do I circumvent what looks like a bug in PHP4's mcrypt_encrypt function?
<?php

function encrypt($text) {
	$eot = '___EOT';
	$key = 'quay';
	$ivs = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
	$iv  = mcrypt_create_iv($ivs, MCRYPT_RAND);
	$text .= $eot;
	$data = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB, $iv);
	$data = base64_encode($data);
	return $data;
}

function decrypt($text) {
	$eot = '___EOT';
	$key = 'quay';
	$ivs = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
	$iv  = mcrypt_create_iv($ivs, MCRYPT_RAND);
	$text = base64_decode($text);
	$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB, $iv);
	$data = explode($eot, $data);
	return $data[0];
}

echo('Encrypted: '.encrypt('ch1ck!NLi77L£').'<br /><br />');
//Note the completely different output strings produced by PHP4 and PHP5. The one produced by PHP4 is corrupted with a "Â" character when decoded on either PHP4 or PHP5.
echo('PHP5 Output Decrypted: '.decrypt('JlS3HtZ4wmVcgaqRuMbmr8CLrwidlTAtZ8+5KPzMfEk=').'<br /><br />');
echo('PHP4 Output Decrypted: '.decrypt('fkBHqfFbPsgyjjwFXvwBNM8OH6zxWP6vUzE00WCCFkc=').'<br /><br />');

?>

Open in new window

Interesting - PHP4 has been dead so long I do not think about it any more.  Let me try to recast the class for PHP4, but please understand that I do not have a test bed for PHP4 (nor for my Model T Ford :-)
Nother question... Are you good at translating PHP into ASP?  We might be able to build our own "encryption" scheme.  Not too secure, but better than ROT13 and good enough to temporarily confound prying eyes.
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of wwarby

ASKER

Well, I finally managed to uncover the basic nature of the problem - it had to do with inserting the text for decryption directly into the source code. Where I had 'ch1ck!NLi77L£' as a string right in the source code, it failed. When I swapped that out for $_REQUEST['password'] and added "password=ch1ck!NLi77L£" to the query string, it worked. The same did not occur on my PHP5 server, but I suspect that has more to do with the configuration of the servers than the distinction between PHP4 and PHP5.

In the end I looked up how to use Chilkat's ActiveX control in PHP and realised it was incredibly simple, so now I've opted to use that in preference over PHP's functions. I figure using the same control to do the same thing in both languages offers an extra level of assurance that the output will be the same.

Thanks for all your help - you definitely put me on the path that let to working out what the problem was.
Thanks for the points - I think the Chilkat control sounds like the best answer all around!