Solved

Encrypting in .Net decrypting in php

Posted on 2014-09-07
13
1,570 Views
Last Modified: 2014-09-10
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

0
Comment
Question by:jwleys
  • 8
  • 5
13 Comments
 

Author Comment

by:jwleys
ID: 40309192
0
 

Author Comment

by:jwleys
ID: 40309196
Useful article on PHP manual talking about differences in .Net vs mycrypt (PHP)
http://php.net//manual/en/function.mcrypt-encrypt.php#47973
0
 
LVL 61

Accepted Solution

by:
btan earned 500 total points
ID: 40309224
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
 
LVL 61

Expert Comment

by:btan
ID: 40309229
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
 

Author Comment

by:jwleys
ID: 40311231
0
 
LVL 61

Expert Comment

by:btan
ID: 40311241
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:jwleys
ID: 40311255
Taking a look at that now thanks, I will keep you posted :)
0
 

Author Comment

by:jwleys
ID: 40313491
0
 

Author Comment

by:jwleys
ID: 40313584
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
 
LVL 61

Expert Comment

by:btan
ID: 40313663
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
 

Author Comment

by:jwleys
ID: 40316103
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
 

Author Closing Comment

by:jwleys
ID: 40316214
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
 
LVL 61

Expert Comment

by:btan
ID: 40316258
noted thanks for sharing
0

Featured Post

Easy Project Management (No User Manual Required)

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

This paper addresses the security of Sennheiser DECT Contact Center and Office (CC&O) headsets. It describes the DECT security chain comprised of “Pairing”, “Per Call Authentication” and “Encryption”, which are all part of the standard DECT protocol.
This article discusses how to create an extensible mechanism for linked drop downs.
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to dynamically set the form action using jQuery.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now