Question

I'm trying to encrypt data on client side(C#) then transmit it to the server through POST and decode it at the server side(PHP).

For this test purpose I'm also attaching to the POST all values were used on the client side to match it for the server Values are:

  • Plain Text
  • Pass Phrase
  • IV
  • Generated By Client Encrypted Text

These parameters im re-using at the server side, it is mean i'm using the same plain text, the same pass phrase and the same IV however results doesn't match

Encrypted text at the client side doesn't match to the encrypted text from server side where both of them were generated from the same input parameters

Here is Console output where you can clearly see what is going on: https://dl.dropboxusercontent.com/u/15715229/ConsoleOutput.JPG

As You see server generate different hash with use of same "in" parameters...

What am I doing wrong?

here is my code:

C# Code:

static void Main(string[] args)
    {
        string url = "http://localhost/temp.php";
        WebClient web = new WebClient();

        string plainText = "This is sentence I want to encrypt";
        string passPhrase = "MyPassPhrase";
        string IV = DateTime.Now.ToLongTimeString() + "InVector";

        Console.WriteLine("");
        Console.WriteLine("----- Start Client -----");
        Console.WriteLine("Plain text = " + plainText);
        Console.WriteLine("PassPhrase = " + passPhrase);
        Console.WriteLine("IV = " + IV);

        string encryptedText = Encrypt(plainText, passPhrase, IV);
        Console.WriteLine("Encrypted Text = " + encryptedText);

        string decryptedText = Decrypt(encryptedText, passPhrase, IV);
        Console.WriteLine("Decrypted Text = " + decryptedText);
        Console.WriteLine("----- End Client -----");
        Console.WriteLine("");

        NameValueCollection postData = new NameValueCollection();
        postData.Add("plainText", plainText);
        postData.Add("encryptedText", encryptedText);
        postData.Add("passPhrase", passPhrase);
        postData.Add("IV", IV);

        string webData = Encoding.UTF8.GetString(web.UploadValues(url, "POST", postData));
        Console.WriteLine("----- Start Server Respond -----");
        Console.WriteLine(webData);
        Console.WriteLine("----- End Server Respond -----");
    }

    public static string Encrypt(string plainText, string passPhrase, string IV)
    {
        byte[] initVectorBytes = Encoding.UTF8.GetBytes(IV);
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        byte[] keyBytes = Encoding.UTF8.GetBytes(passPhrase);

        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;

        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);

        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
        cryptoStream.FlushFinalBlock();

        byte[] cipherTextBytes = memoryStream.ToArray();
            memoryStream.Close();
            cryptoStream.Close();

        return Convert.ToBase64String(cipherTextBytes);
    }

    public static string Decrypt(string cipherText, string passPhrase, string IV)
    {
        byte[] initVectorBytes = Encoding.UTF8.GetBytes(IV);
        byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
        byte[] keyBytes = Encoding.UTF8.GetBytes(passPhrase);

        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;

        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);

        MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
        CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

        byte[] plainTextBytes = new byte[cipherTextBytes.Length];
        int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
            memoryStream.Close();
            cryptoStream.Close();

        return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
    }

My PHP Code:

<?php

if(isset($_POST['plainText']))
{
    $plainText = $_POST['plainText'];
    $clientEncryptedText = $_POST['encryptedText'];
    $passPhrase = $_POST['passPhrase'];
    $iv = $_POST['IV'];

    echo "Plain text = ".$plainText."\n";
    echo "PassPhrase = ".$passPhrase."\n";
    echo "IV = ".$iv."\n";

    $encryptedText = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $passPhrase, $plainText, MCRYPT_MODE_CBC, $iv ));
    echo "Server Encrypted Text = ".$encryptedText."\n";
    echo "Client Encrypted Text = ".$clientEncryptedText."\n";

    $decryptedText = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $passPhrase, base64_decode($encryptedText), MCRYPT_MODE_CBC, $iv );
    echo "Server Decrypted Text = ".$decryptedText."\n";

    $decryptedText = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $passPhrase, base64_decode($clientEncryptedText), MCRYPT_MODE_CBC, $iv );
    echo "Decrypted text from Client = ".$decryptedText."\n";

}
else
{
    echo "POST is not set";
}

Can you please tell me what am i doing wrong and where? at the client (C#) or at the server (PHP)?

Regards Vadims Briksins

Was it helpful?

Solution 2

finally got it sorted. Whole day was fighting with it and now would love to share the code with you.

Code is 100% working - Tested and Verified!

Content of C# CryptoMaster.cs file (Client Side):

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EncryptionClient
{
    class CryptoMaster
    {
        private string encryptedText;

        public void StartEncryption()
        {
            Console.WriteLine("");
            Console.WriteLine("----- Client Start -----");
            string plainText = "Hello, this is a message we need to encrypt";
            Console.WriteLine("Plain Text = " + plainText);
            string passPhrase ="Pass Phrase Can be any length";
            string saltValue = DateTime.Now.ToLongTimeString(); //slat should be 8 bite len, in my case im using Time HH:MM:SS as it is dynamic value
            string hashAlgorithm = "SHA1";
            int passwordIterations = 1;
            string initVector = "InitVector Should be 32 bite len";
            int keySize = 256;

            encryptedText = Encrypt(plainText, passPhrase, saltValue, hashAlgorithm, passwordIterations, initVector, keySize);
            Console.WriteLine("Encrypted Text = " + encryptedText);

            string decryptedText = Decrypt(encryptedText, passPhrase, saltValue, hashAlgorithm, passwordIterations, initVector, keySize);
            Console.WriteLine("Decripted Text = " + decryptedText);
            Console.WriteLine("----- Client End -----");

            SendDataToWebServer(plainText, passPhrase, saltValue, hashAlgorithm, passwordIterations, initVector, keySize);
        }

        private void SendDataToWebServer(string plainText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
        {

            NameValueCollection POST = new NameValueCollection();
            //NOTE: I'm Including all this data to POST only for TESTING PURPOSE 
            //and to avoid manual entering of the same data at server side.
            //In real live example you have to keep sensative data hidden
            POST.Add("plainText", plainText);
            POST.Add("passPhrase", passPhrase);
            POST.Add("saltValue", saltValue);
            POST.Add("hashAlgorithm", hashAlgorithm);
            POST.Add("passwordIterations", passwordIterations+"");
            POST.Add("initVector", initVector);
            POST.Add("keySize", keySize+"");
            POST.Add("encryptedText", encryptedText);


            WebClient web = new WebClient();
            string URL = "http://localhost/Encryptor.php";
            Console.WriteLine("");
            string serverRespond = Encoding.UTF8.GetString(web.UploadValues(URL, "POST", POST));
            Console.WriteLine("----- Server Start -----");
            Console.WriteLine(serverRespond);
            Console.WriteLine("----- Server End -----");

        }

        public string Encrypt(string plainText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
        {

            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes, passwordIterations);

            byte[] keyBytes = password.GetBytes(keySize / 8);

            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.BlockSize = 256;
            symmetricKey.KeySize = 256;
            symmetricKey.Padding = PaddingMode.Zeros;
            symmetricKey.Mode = CipherMode.CBC;

            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);

            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

            cryptoStream.FlushFinalBlock();
            byte[] cipherTextBytes = memoryStream.ToArray();

            memoryStream.Close();
            cryptoStream.Close();

            string cipherText = Convert.ToBase64String(cipherTextBytes);

            return cipherText;
        }

        public static string Decrypt(string cipherText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
        {

            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

            Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes, passwordIterations);

            byte[] keyBytes = password.GetBytes(keySize / 8);

            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.BlockSize = 256;
            symmetricKey.KeySize = 256;
            symmetricKey.Padding = PaddingMode.Zeros;
            symmetricKey.Mode = CipherMode.CBC;

            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);

            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

            byte[] plainTextBytes = new byte[cipherTextBytes.Length];

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

            memoryStream.Close();
            cryptoStream.Close();

            string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);

            return plainText;
        }
    }
}

Content of PHP Encryptor.PHP file (Server Side):

<?php
error_reporting(0);


if (isset($_POST['plainText'])) {

    $plainText = $_POST['plainText'];
    $passPhrase = $_POST['passPhrase'];
    $saltValue = $_POST['saltValue'];
    $hashAlgorithm = $_POST['hashAlgorithm'];
    $passwordIterations = $_POST['passwordIterations'];
    $initVector = $_POST['initVector'];
    $keySize = $_POST['keySize'];
    $clientEncryptedText = $_POST['encryptedText'];

    $key = getKey($passPhrase,$saltValue, $passwordIterations, $keySize, $hashAlgorithm);

    echo "Plain Text = ".$plainText."\n";
    echo "Client Encrypted Text = ".$clientEncryptedText."\n";

    $encryptedText = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plainText, MCRYPT_MODE_CBC, $initVector));
    echo "Server Encrypted Text = ".$encryptedText."\n";

    $decryptedText = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($encryptedText), MCRYPT_MODE_CBC, $initVector), "\0");
    echo "Server Decrypted Text = ".$decryptedText."\n";

    $decryptedText = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($clientEncryptedText), MCRYPT_MODE_CBC, $initVector), "\0");
    echo "Client Decrypted Text = ".$decryptedText;

}

function getKey( $passPhrase, $saltValue, $passwordIterations, $keySize, $hashAlgorithm ) {

    $hl = strlen(hash($hashAlgorithm, null, true));
    $kb = ceil($keySize / $hl);
    $dk = '';

    for ( $block = 1; $block <= $kb; $block ++ ) {

        $ib = $b = hash_hmac($hashAlgorithm, $saltValue . pack('N', $block), $passPhrase, true);

        for ( $i = 1; $i < $passwordIterations; $i ++ )

            $ib ^= ($b = hash_hmac($hashAlgorithm, $b, $passPhrase, true));

        $dk .= $ib;
    }

    return substr($dk, 0, $keySize);
}

?>

Console Output can be viewed by this link

OTHER TIPS

Your Passphrase is not a key of the appropriate length. Same goes for the IV. Thus, some kind of padding, truncation or hashing will happen. PHP and C# likely do it differently. Also, you don't specify if AES-128 or AES-256 is to be used in C# - thus, you are likely using AES-256 in C#, while decrypting with AES-128. Also C# could, theoretically, also use different block sizes (it likely doesn't). Padding could also differ, which could cause issues later down the road.

Make sure your IV matches the block size used (should be 128 bit = 16 byte) and the passphrase/key matches whatever key size you chose.

If you will be using real passphrases in practice, you need to use something like PBKDF2 to derive keys from them.

You also may want to add integrity checking (e.g. using HMAC with a separate key).

Also, don't implement crypto yourself if you don't have to. Check if SSL/TLS could fix the problem for you, and then use it if possible. You can use hardcoded selfsigned certificates if you want to and it matches your requirements, but using an existing crypto protocol is usually a better idea than building your own.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top