Link to home
Start Free TrialLog in
Avatar of Jeremy Leys
Jeremy LeysFlag for New Zealand

asked on

Can't decrypt AES RIJNDAEL 256 In PHP?

I am struggling to decrypt  AES RIJNDAEL 256 in PHP mycrypt

Output of code below:
1õ`d1„ÆÛ»›iE¸j † ÐG”œ$ è&“pÕÁ·sÿʤ)hç_×@û"9ص¸O„ªNºZÎÏ@•J׈²ï9ïx×Íç¥Ý’?ÝÙ-T´\Ü6«Á)ðVNÓ”¨[“Fÿ}qÐ`ò¤Q+³6ÞN

$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$key256 = '7RFZEZJ76rwiTzF3C0esOGKbiLVyutRp';
$iv =  'yfdmRJKZh6p2VkAs';

$token = "BbhHf5vkOwj7PGOmbiDCkVBo3KcYr+gcK9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA==";

mcrypt_generic_init($cipher, $key256, $iv);
$decrypted = mdecrypt_generic($cipher, base64_decode($token));

echo $decrypted;

Open in new window


This is the .Net class that produced the encrypted base64 string:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace CCare.Security
{
    public class Encryption
    {
        #region Rijndael/AES Declaration - MMH AES Encryptions

        private static string Passphrase = "7RFZEZJ76rwiTzF3C0esOGKbiLVyutRp";
        private static int Passworditerations = 8;
        private static string Initialvector = "yfdmRJKZh6p2VkAs";         

        #endregion


        #region Rijndael/AES MMH encryption
        /// <summary>
        /// This class uses a symmetric key algorithm (Rijndael/AES) to encrypt and 
        /// decrypt data. As long as encryption and decryption routines use the same
        /// parameters to generate the keys, the keys are guaranteed to be the same.
        /// </summary>
        /// 
        public static string Encrypt(string plainText)
        {
            return AESEncrypt(plainText, Passphrase, "", Passworditerations, Initialvector);
        }

        public static string Decrypt(string cipherText)
        {

            return AESDecrypt(cipherText, Passphrase, "", Passworditerations, Initialvector);

        }
        public static string Encrypt(string plainText, string PasswordPhrase, string InitializationVector)
        {
            return AESEncrypt(plainText, PasswordPhrase, "", Passworditerations, InitializationVector);
        }

        public static string Decrypt(string cipherText, string PasswordPhrase, string InitializationVector)
        {

            return AESDecrypt(cipherText, PasswordPhrase, "", Passworditerations, InitializationVector);

        }

        /// <summary>
        /// Encrypts specified plaintext using Rijndael symmetric key algorithm
        /// and returns a base64-encoded result.
        /// </summary>
        /// <param name="plainText">
        /// Plaintext value to be encrypted.
        /// </param>
        /// <param name="passPhrase">
        /// Passphrase from which a pseudo-random password will be derived. The
        /// derived password will be used to generate the encryption key.
        /// </param>
        /// <param name="saltValue">
        /// Salt value used along with passphrase to generate password.
        /// </param>
        /// <param name="passwordIterations">
        /// Number of iterations used to generate password. One or two iterations
        /// should be enough.
        /// </param>
        /// <param name="initVector">
        /// Initialization vector (or IV). This value is required to encrypt the
        /// first block of plaintext data. For RijndaelManaged class IV must be 
        /// exactly 16 ASCII characters long.
        /// </param>
        /// <returns>
        /// Encrypted value formatted as a base64-encoded string.
        /// </returns>
        private static string AESEncrypt(string plainText, string passPhrase, string saltValue, int passwordIterations, string initVector)
        {
            try
            {
                // Convert strings into byte arrays.
                // Let us assume that strings only contain ASCII codes.
                // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
                // encoding.
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);

                // Convert our plaintext into a byte array.
                // Let us assume that plaintext contains UTF8-encoded characters.
                byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

                // Create uninitialized Rijndael encryption object.
                RijndaelManaged symmetricKey = new RijndaelManaged();

                // It is reasonable to set encryption mode to Cipher Block Chaining
                // (CBC). Use default options for other symmetric key parameters.
                symmetricKey.Mode = CipherMode.CBC;

                // Generate encryptor from the existing key bytes and initialization 
                // vector. Key size will be defined based on the number of the key 
                // bytes.
                ICryptoTransform encryptor = symmetricKey.CreateEncryptor(getKeyBytes(passPhrase, saltValue, passwordIterations), initVectorBytes);

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream();

                // Define cryptographic stream (always use Write mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
                // Start encrypting.
                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

                // Finish encrypting.
                cryptoStream.FlushFinalBlock();

                // Convert our encrypted data from a memory stream into a byte array.
                byte[] cipherTextBytes = memoryStream.ToArray();

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();

                // Convert encrypted data into a base64-encoded string.
                string cipherText = Convert.ToBase64String(cipherTextBytes);

                // Return encrypted string.
                return cipherText;
            }
            catch //(Exception ex)
            {
                // ErrorLog(ex);
                return string.Empty;
            }
        }

        /// <summary>
        /// Decrypts specified ciphertext using Rijndael symmetric key algorithm.
        /// </summary>
        /// <param name="cipherText">
        /// Base64-formatted ciphertext value.
        /// </param>
        /// <param name="passPhrase">
        /// Passphrase from which a pseudo-random password will be derived. The
        /// derived password will be used to generate the encryption key.
        /// </param>
        /// <param name="saltValue">
        /// Salt value used along with passphrase to generate password.
        /// </param>
        /// <param name="passwordIterations">
        /// Number of iterations used to generate password. One or two iterations
        /// should be enough.
        /// </param>
        /// <param name="initVector">
        /// Initialization vector (or IV). This value is required to encrypt the
        /// first block of plaintext data. For RijndaelManaged class IV must be
        /// exactly 16 ASCII characters long.
        /// </param>
        /// <returns>
        /// Decrypted string value.
        /// </returns>
        /// <remarks>
        /// Most of the logic in this function is similar to the Encrypt
        /// logic. In order for decryption to work, all parameters of this function
        /// - except cipherText value - must match the corresponding parameters of
        /// the Encrypt function which was called to generate the
        /// ciphertext.
        /// </remarks>
        private static string AESDecrypt(string cipherText, string passPhrase, string saltValue, int passwordIterations, string initVector)
        {
            try
            {
                // Convert strings defining encryption key characteristics into byte
                // arrays. Let us assume that strings only contain ASCII codes.
                // If strings include Unicode characters, use Unicode, UTF7, or UTF8
                // encoding.
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);

                // Convert our ciphertext into a byte array.
                byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

                // Create uninitialized Rijndael encryption object.
                RijndaelManaged symmetricKey = new RijndaelManaged();

                // It is reasonable to set encryption mode to Cipher Block Chaining
                // (CBC). Use default options for other symmetric key parameters.
                symmetricKey.Mode = CipherMode.CBC;

                // Generate decryptor from the existing key bytes and initialization 
                // vector. Key size will be defined based on the number of the key 
                // bytes.
                ICryptoTransform decryptor = symmetricKey.CreateDecryptor(getKeyBytes(passPhrase, saltValue, passwordIterations), initVectorBytes);

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

                // Define cryptographic stream (always use Read mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

                // Since at this point we don't know what the size of decrypted data
                // will be, allocate the buffer long enough to hold ciphertext;
                // plaintext is never longer than ciphertext.
                byte[] plainTextBytes = new byte[cipherTextBytes.Length];

                // Start decrypting.
                int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();

                // Convert decrypted data into a string. 
                // Let us assume that the original plaintext string was UTF8-encoded.
                string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);

                // Return decrypted string.   
                return plainText;
            }
            catch //(Exception ex)
            {
                // ErrorLog(ex);
                return string.Empty;
            }
        }

        private static byte[] getKeyBytes(string passPhrase, string saltValue, int passwordIterations)
        {
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            // First, we must create a password, from which the key will be derived.
            // This password will be generated from the specified passphrase and 
            // salt value. The password will be created using the specified hash 
            // algorithm. Password creation can be done in several iterations.
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                            passPhrase,
                                                            saltValueBytes,
                                                            "SHA1",
                                                            passwordIterations);

            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = password.GetBytes(256 / 8);

            return keyBytes;
        }

        #endregion


    }
}

Open in new window

Avatar of Jeremy Leys
Jeremy Leys
Flag of New Zealand image

ASKER

Should produce a plain text string
For comparing passwords, you shouldn't try and decrypt the stored password, but encrypt the entered password and compare the two encrypted strings.
Really struggling with this one, I have another related question:
https://www.experts-exchange.com/questions/28513422/Encrypting-in-Net-decrypting-in-php.html
I will consolidate and post what I have tried so far
I notice that the question is captioned AES RIJNDAEL 256 but the first line of PHP code says this:
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

I think you would want to be consistent about that 128 vs 256 thing.  Here is a little script that demonstrates the two processes on the PHP side.  Hopefully you can use it to generate some test data that compares to the .NET
http://iconoun.com/demo/encrypt_decrypt.php

<?php // demo/encrypt_decrypt.php
error_reporting(E_ALL);

// REF: http://php.net/manual/en/ref.mcrypt.php
// REF: http://php.net/manual/en/mcrypt.ciphers.php
// NOTE PARALLEL CONSTRUCTION IN THE mcrypt_XXcrypt() FUNCTIONS

class Encryption
{
    protected $key;

    public function __construct($key='quay')
    {
        // THE KEY MUST BE KNOWN TO BOTH PARTS OF THE ALGORITHM
        $this->key = $key;
    }

    public function encrypt($text)
    {
        // ENCRYPT THE DATA
        $data = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->key, $text, MCRYPT_MODE_ECB);

        // MAKE IT base64() STRING SAFE FOR STORAGE AND TRANSMISSION
        return base64_encode($data);
    }

    public function decrypt($text)
    {
        // DECODE THE DATA INTO THE BINARY ENCRYPTED STRING
        $text = base64_decode($text);

        // DECRYPT THE STRING
        $data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->key, $text, MCRYPT_MODE_ECB);

        // DECLOP NUL-BYTES BEFORE THE RETURN
        return trim($data);
    }
}

// INSTANTIATE AN ENCRYPTION OBJECT FROM THE CLASS
$c = new Encryption();

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

// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST["clearstring"]))
{
    $encoded = $c->encrypt($_POST["clearstring"]);
    echo "<br/>{$_POST["clearstring"]} YIELDS ENCODED ";
    var_dump($encoded);
}

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

// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM
<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>
FORM;

echo $form;

Open in new window

Thanks Ray what you have provided is very helpful, this is my latest attempt closer but still not quite working:

Can't produce or decrypt:
BbhHf5vkOwj7PGOmbiDCkVBo3KcYr+gcK9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA==

<?php
error_reporting(0);
?>
<form action="trigger2.php" method="post" id="mainform" name="mainform" accept-charset="utf-8">
Text to encrypt (or key to decrypt):<input type="text" size="80" name="key" value="<?php if (isset($_POST['key'])) echo $_POST['key']; else echo '&un=medtech&pw=thvbxz4&fhpi=19BKBP&phpi=15BABA&gp=SamEntwistle&nzmc=A88984-3'; ?>"></br>
Passphrase:<input type="text" size="80" name="passphrase" value="<?php if (isset($_POST['passphrase'])) echo $_POST['passphrase']; else echo '7RFZEZJ76rwiTzF3C0esOGKbiLVyutRp'; ?>"></br>
Salt:<input type="text" size="80" name="salt" value="<?php if (isset($_POST['salt'])) echo $_POST['salt']; else echo ''; ?>"></br>
Iterations:<input type="text" size="80" name="iterations" value="<?php if (isset($_POST['iterations'])) echo $_POST['iterations']; else echo '8'; ?>"></br>
Init Vector:<input type="text" size="80" name="initvector" value="<?php if (isset($_POST['initvector'])) echo $_POST['initvector']; else echo 'yfdmRJKZh6p2VkAs'; ?>"></br>
Key Size:<input type="text" size="80" name="keysize" value="<?php if (isset($_POST['keysize'])) echo $_POST['keysize']; else echo '32'; ?>"></br>
<input type="submit" name="Encrypt" value="Encrypt"> <input type="submit" name="Decrypt" value="Decrypt">
</form>
<?php
function pbkdf2( $p, $s, $c, $kl, $a = 'sha1' ) {
$hl = strlen(hash($a, null, true)); # Hash length
$kb = ceil($kl / $hl); # Key blocks to compute
$dk = ''; # Derived key
# Create key
for ( $block = 1; $block <= $kb; $block ++ ) {
# Initial hash for this block
$ib = $b = hash_hmac($a, $s . pack('N', $block), $p, true);
# Perform block iterations
for ( $i = 1; $i < $c; $i ++ )
# XOR each iterate
$ib ^= ($b = hash_hmac($a, $b, $p, true));
$dk .= $ib; # Append iterated block
}
# Return derived key of correct length
return substr($dk, 0, $kl);
}
if (isset($_POST['key'])) {
// Make sure salt is 8 bytes length
$key = pbkdf2($_POST['passphrase'],$_POST['salt'], $_POST['iterations'], $_POST['keysize']);
//$text = "yamnuska"; // test plain text
$text = $_POST['key'];
$iv = $_POST['initvector'];
if(isset($_POST['Decrypt'])) {
// Use the output from above. This also works with Windows encrypted output and strips the padded characters
$encrypted = $text;
echo "Decrypted: <input type=\"text\" size=\"60\" value=\"".rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0")."\"><br/>";
} else {
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
echo "Encrypted: <input type=\"text\" size=\"60\" value=\"".base64_encode($crypttext)."\"><br/>";
}
}
?>

Open in new window

Found this GITHUB project which I have found helpful:
https://github.com/dchymko/.NET--PHP-encryption
I am thinking that maybe what I am lacking is the SALT value which I need to confirm with the company I am trying to integrate with. As far as I can see from the .Net class example I have given the SALT value is simply an empty string ""
What's the input (clear-text) string you're using for test data?
ASKER CERTIFIED SOLUTION
Avatar of gr8gonzo
gr8gonzo
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
Here is the string I am trying to encrypt:
$cleartext = '&un=medtech&pw=thvbxz4&fhpi=19BKBP&phpi=15BABA&gp=SamEntwistle&nzmc=A88984-3';
It is basically a string of URL params.
Thanks gr8gonzo,

I tried to use your sample to decrypt the Base64 encrypted string I have been provided:
BbhHf5vkOwj7PGOmbiDCkVBo3KcYr+gcK9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA==

This is the output I get:
IV = BbhHf5vkOwj7PGOmbiDCkQ== (16) Salt = UGjcpxiv6Bw= (8) Key = Ds4HOD1hd0DH29KBau3Tcbo6efmy0Itm3CuniHcwfAY= (32) Message = K9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA== (88) Decrypted = ± À”Oê™U¼ö'Ÿ8YÀY}*UùžÁK¦ù€OûÕ¥7“ÆÝ\ÓX_±Íýš:ÓùG§De€Òcê#ô· Ô˜£™Áø7Hý³´•Ë¿èõ1üÐ ê@°‰
gr8gonzo thank you for suggestions about the C# class, unfortunately I have no control over the .Net implementation of this which is done by a 3rd party that we are trying to integrate with. But I will definitely relay your instructions on how to improve the C# class.
Unfortunately, the .NET implementation uses PBKDF1 (PasswordDeriveBytes) with a SHA-1 algorithm and tries to return 32 bytes. The problem here is that a SHA-1 hash is 20 bytes long. So if you need 32 bytes, the PasswordDeriveBytes class basically runs an additional hash with a flipped bit in order to get the "extra" 12 bytes.

I don't know of a good way to do this in PHP. There might be someone out there who's figured out a pure PHP implementation of it. I know of a way to get the first 20 bytes (the keygen algorithm for PBKDF1 is pretty straightforward), but not entirely sure about the last 12 bytes.

Alternatively, if you can't change the C# code at all, then you -could- just hardcode the final resulting key, since it'll be the same every single time.
For the record, if you're certain that's the exact code used by the 3rd party, I would suggest that you look for alternatives and tell them you're looking for alternatives due to that code. (In other words, put pressure on them to fix it ASAP).

Bad code can sometimes represent the quality of the rest of the coding, and when it comes to security, you should be careful about dealing with 3rd parties that cannot properly create secure code. That may be an indication that they don't have sufficient security elsewhere and could put your own data / information at risk and even lead to an attack on you.
I am aware that this is a very hacky implementation and I have relayed that to my boss but she wants me to continue anyway :(

I am now able to decrypt :), the issue I am having now is producing an identical encrypted base64 encoded string the same as the .Net class. I believed it was a padding issue but I have outputted the padding of both the the string to be encrypted and the string that has been decrypted and they are the same.

Sample output:
String to be encrypted padding: 13
Decripted string padding: 13
Enc: 05b8477f9be43b08fb3c63a66e20c2915068dca718afe81c2bdb3045a0e6630ce78f655b359606181fa1eca37ec8ab9167c4bea74fa9fc7e8f14740d1c3f4a07acecc58665004f3117642231165b507db4955d62b1fa013ff3e62f7f4bba8398c3c1224a65eff6166631573842972bc4
Dec: &un=medtech&pw=thvbxz4&fhpi=19BKBP&phpi=15BABA&gp=SamEntwistle&nzmc=A88984-3&ts=2014-07-28:13:19:04
Encrypt base64: BbhHf5vkOwj7PGOmbiDCkVBo3KcYr+gcK9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA==
Re-Encrypt: ETeA7Ur0fzRDzwWpVOYpgkpfsvV6Ha5kS+vkZ9aWfnYH5UlfpcDgajP7gMG4/urGJfY4napsa5orL29jHjoAeMFHJad3Jh982QzfdrTjQCHZKwyz7u2pdh0pNYekmL4WbD+w67upvylcnArBwXQYDQ==

My very dirty code that I will be amending and cleaning up:

<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<?php
$plainTxt = "&un=medtech&pw=thvbxz4&fhpi=19BKBP&phpi=15BABA&gp=SamEntwistle&nzmc=A88984-3&ts=2014-07-28:13:19:04";
$encb64 = "BbhHf5vkOwj7PGOmbiDCkVBo3KcYr+gcK9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA==";

$pwd = "7RFZEZJ76rwiTzF3C0esOGKbiLVyutRp";
$salt = "";

$enc = base64_decode($encb64);
$decpad = Decrypt($enc, $pwd, $salt);
$plainTxt = padtxt($plainTxt);
$reenc = Encrypt($plainTxt, $pwd, $salt);
// Remove the padding
$pad = ord($decpad[($len = strlen($decpad)) - 1]);
echo "Decripted string padding: $pad<br />";
$dec = substr($decpad, 0, strlen($decpad) - $pad);

echo "Enc: " . bin2hex($enc) . "<br />";
echo "Dec: " . $dec . "<br />";
echo "Encrypt base64: " . $encb64 . "<br />";
echo "Re-Encrypt: " . $reenc = base64_encode(Encrypt($decpad, $pwd, $salt)) . "<br />";

function padtxt($value){
    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $len = strlen($value);
    $padding = $block - (($len % $block));
    echo "String to be encrypted padding: $padding<br />";
    $value .= str_repeat(chr($padding),$padding);
    return $value;
}

function Decrypt($ciphertext, $password, $salt)
{
  $key = PBKDF1($password, $salt, 8, 32);
  $iv = 'yfdmRJKZh6p2VkAs'; //PBKDF1($password, $salt, 8, 16);

  // NB: Need 128 not 256 and CBC mode to be compatible
  return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv);
}

function Encrypt($plainTxt, $password, $salt)
{
  $key = PBKDF1($password, $salt, 8, 32);
  $iv = 'yfdmRJKZh6p2VkAs'; //PBKDF1($password, $salt, 8, 16);
  return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plainTxt, MCRYPT_MODE_CBC, $iv);
}

?>

<?php
function PBKDF1($pass, $salt, $count, $cb)
{
  // This is very approximately the way that the Microsoft version of 
  // PasswordDeriveBytes works.

  ///
  /// !!!WARNING!!!
  ///
  // This is a BAD function!
  // Irrespective of the fact that the use of PBKDF1 is not recommended anyway.
  //
  // This really should be put into a class with a constructor taking the 
  // $pass, $salt and $count.
  // Then there should be a Reset() method to start from scratch each time a new pwd/salt is used.
  // And there should be a GetBytes(int) method to get the required info.
  // But for the sake of simplicity we are assuming the same pwd and salt for each call to 
  // this function. This will not stand up to any scrutiny!

  static $base;
  static $extra;
  static $extracount= 0;
  static $hashno;
  static $state = 0;

  if ($state == 0)
  {
    $hashno = 0;
    $state = 1;

    $key = $pass . $salt;
    $base = sha1($key, true);
    for($i = 2; $i < $count; $i++)
    {
      $base = sha1($base, true);
    }
  }

  $result = "";

  // Check if we have any bytes left over from a previous iteration.
  // This is the way MS appears to do it. To me it looks very badly wrong
  // in the line: "$result = substr($extra, $rlen, $rlen);"
  // I'm sure it should be more like "$result = substr($extra, $extracount, $rlen);"
  // Mono have provided what looks like a fixed version at
  // https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/PasswordDeriveBytes.cs
  // But I'm no cryptographer so I might be wrong.
  // But this seems to work for low values of $hashno and seems to work
  // with C# implementations.

  if ($extracount > 0)
  {
    $rlen = strlen($extra) - $extracount;
    if ($rlen >= $cb)
    {
      $result = substr($extra, $extracount, $cb);
      if ($rlen > $cb)
      {
        $extracount += $cb;
      }
      else
      {
        $extra = null;
        $extracount = 0;
      }
      return $result;
    }
    $result = substr($extra, $rlen, $rlen);
  }

  $current = "";
  $clen = 0;
  $remain = $cb - strlen($result);
  while ($remain > $clen)
  {
    if ($hashno == 0)
    {
      $current = sha1($base, true);
    }
    else if ($hashno < 1000)
    {
      $n = sprintf("%d", $hashno);
      $tmp = $n . $base;
      $current .= sha1($tmp, true);
    }
    $hashno++;
    $clen = strlen($current);     
  }

  // $current now holds at least as many bytes as we need
  $result .= substr($current, 0, $remain);

  // Save any left over bytes for any future requests
  if ($clen > $remain)
  {
    $extra = $current;
    $extracount = $remain;
  }

  return $result; 
}
?>
</body>
</html>

Open in new window

I managed to convince my boss to change tactics and go with your suggested solution. The company we are integrating with have done the same and now it is working like a charm.

Thank you so much for all your help!!! :)