Encrypting in .Net decrypting in php

Hi,

I am trying to use mcrypt in PHP to decrypt (Rijndael/AES). I am integrating with a company that is using .Net. From the look of this .Net class they are using Rijndael/AES in CBC mode and 8 password iterations. I am looking for a good, easy to use PHP class that will do the equivalent of what this .Net class is doing for encrypting and decrypting.

Here is what I have experimented with in PHP naturally it will not give the same result as the .Net class as it is not using password iterations.

My PHP test:
<?php
function AESDecrypt($plain_txt){
	$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
	$key256 = '3De4gYipL2Fa4jnuIOPdb9CqWLnuoJcv';
	$iv =  'bf3TwIPaY2nXqe2k';

	mcrypt_generic_init($cipher, $key256, $iv);
	$encrypted = mcrypt_generic($cipher, $plain_txt);

	mcrypt_generic_init($cipher, $key256, $iv);
	$decrypted = mdecrypt_generic($cipher, $encrypted);
		
	echo "<br /><br />Encrypted: ".base64_encode($encrypted);
	echo "<br /><br />Decrypted: $decrypted";
}

AESDecrypt("testing, testing 123");

?>

Open in new window


For security reasons I have not given the actual Pass phrase and Initial vector but 32 character and 16 character place holders which should suffice. The output of the .Net class when encrypting looks something like this: BbhHf5vkOwj7PGOmbiDCkVBo3KcYr+gcK9swRaDmYwznj2VbNZYGGB+h7KN+yKuRZ8S+p0+p/H6PFHQNHD9KB6zsxYZlAE8xF2QiMRZbUH20lV1isfoBP/PmL39LuoOYw8EiSmXv9hZmMVc4QpcrxA==

Working .Net class code:

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 = "3De4gYipL2Fa4jnuIOPdb9CqWLnuoJcv";
        private static int Passworditerations = 8;
        private static string Initialvector = "bf3TwIPaY2nXqe2k";         

        #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

Jeremy LeysTechnical LeadAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Jeremy LeysTechnical LeadAuthor Commented:
0
Jeremy LeysTechnical LeadAuthor Commented:
Useful article on PHP manual talking about differences in .Net vs mycrypt (PHP)
http://php.net//manual/en/function.mcrypt-encrypt.php#47973
0
btanExec ConsultantCommented:
there is one example for interoperability stated e.g. encrypted using AES256 and which I needed to decrypt in PHP @ http://lukieb.blogspot.sg/2013/04/making-aes256-encryption-work-same-in.html

note the need to do a Base64 decode (base64_decode), thereafter extract (using substr) the IV (16 bytes of initialisation vector) and the following on ciphertext (next 16 bytes)
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

btanExec ConsultantCommented:
Probably on seeing it with details here is another explanation..similar to prev posting on the fields prior to mcrypt decrypt function
http://www.codingvision.net/security/c-php-compatible-encryption-aes256

others suggested...such as "PhpSeclib <-> BouncyCastle RSA" and as of "PHP equivalent to Rijndael AES encryption/decryption in .Net?" it is not really advocated but thought of sharing the quote below
PHP offers two extensions that can, in principle, do the job. OpenSSL suffers from not being documented beyond function prototypes, and Mcrypt suffers from being an absolute minefield if you don't happen to know exactly what you're doing. I wouldn't use either if I could possibly get away with it.

If you do attempt this, you will need to implement authentication yourself. You will need to implement padding yourself. If you screw up, you will get no indication even if the library knows perfectly well it's been asked to do something absurd, for the most part it will (silently!) guess at what you meant and continue on (patches for much of this are available, but not yet in mainline).
0
Jeremy LeysTechnical LeadAuthor Commented:
0
btanExec ConsultantCommented:
0
Jeremy LeysTechnical LeadAuthor Commented:
Taking a look at that now thanks, I will keep you posted :)
0
Jeremy LeysTechnical LeadAuthor Commented:
0
Jeremy LeysTechnical LeadAuthor Commented:
This is helpful btan:
http://lukieb.blogspot.sg/2013/04/making-aes256-encryption-work-same-in.html

Unfortunately, the .NET implementation uses PBKDF1 (PasswordDeriveBytes) with a SHA-1 algorithm and tries to return 32 bytes. I am looking for examples of how to perform PBKDF1 in PHP.
0
btanExec ConsultantCommented:
pretty tough into the details as the crypto required the consistency in use of the padding byte (depends on pkcs5 standard v1/1.5/2 as well) , block size, key size, iv etc. And making it tougher using different lang and implementation libraries which you are not savvy of its internal - always good to make sure the encrypt and decrypt work for the same lang first before the interoperability testing btw different lang.

for pbkdf1 in php some consideration (pkdf1 - should be using pkcs5v1.5 as padding)
http://forums.devnetwork.net/viewtopic.php?f=1&t=120070

I saw separately in forum on discussing the interoperability too..see the part on "PBKDF1" - you may also try google "how-do-i-convert-this-c-sharp-rijndael-encryption-to-php"
http://forums.devnetwork.net/viewtopic.php?f=1&t=120060
0
Jeremy LeysTechnical LeadAuthor Commented:
I have updated my question here:
http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28514171.html

I can now decrypt and I am trying to encrypt the same way as the .Net class
0
Jeremy LeysTechnical LeadAuthor Commented:
Thank you, I have found this to be extremely challenging and you have been a huge help. I ended up convincing my boss to move to pbkdf2.

Thank you so much time, this has been a steep learning curve me :)
0
btanExec ConsultantCommented:
noted thanks for sharing
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Encryption

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.