How to Convert This C# Rijndael Encryption to PHP

How do I convert this C# Rijndael encryption to PHP?

I think that the PHP version may actually add 00h valued bytes to the key and IV. They both have an invalid size : 8 bytes for each. They need to be extended to 16 bytes for AES-128. In your C# code you use 32 bytes for the key, which will therefore use AES with a key size of 256 bits.

Futhermore, you don't specify the number of iterations in PasswordDeriveBytes, you should specify it as the class does not specify the default number of iterations - according to your comments, this would be 100, lets assume it is.

Oh, and you use the incorrect encryption method. MCRYPT_RIJNDAEL_256 specifies the Rijndael algorithm using a blocksize of 256 bits, not keys of 256 bits. Presumably, the bitsize of the keys is simply the number of bytes of the key times 8.

Could you replace your Encrypt function with this and try again?

function Encrypt($pass, $salt)
{
$derived = PBKDF1($pass, $salt, 100, 48);
$key = bin2hex(substr($derived, 0, 32));
$iv = bin2hex(substr($derived, 32, 16));
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $pass, MCRYPT_MODE_CBC, $iv);
}

Finally, please check if the generated IV and key match with the ones in PHP before performing encryption or decryption. Are you sure that that PHP PBKDF1 function is correct?

UPDATE:
Here is some more information on the M$ PBKDF1 routines in PasswordDeriveBytes (including Java code which you may try and convert):

ha, I see your point.

Interestingly, using .NET: the results are different when calling 48
or calling 32 followed by 16:

.NET GetBytes( 32 +16 ):
04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922655D8DD89AE1CAAC60A8041FCD7E8DA4

.NET GetBytes( 32 )
04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922
Followed by GetBytes( 16 ) 89946D3662B3196860A8041FCD7E8DA4

True Microsoft code, and they cannot change it because it could break applications in the field. Note that they also would return different results when calling it with 16 and then 8 bytes or directly by 24 bytes by design. You'd better upgrade to PBKDF2, and keep PBKDF1 limited to 20 bytes max, as defined in the standards.

Rijndael/AES decryption C# to PHP conversion

Your C# code doesn't really look secure, so if you can change it, see below for some tips.
Here is your given PHP code modified to look like it could be equivalent to the C# code given.

function decryptAES128CBC($content,$iv, $key) {

// AES is Rijndael-128
$rijndael = 'rijndael-128';

// key size is 128 bit = 16 bytes
$ks = 16;

// CBC mode, not OFB
$cp = mcrypt_module_open($rijndael, '', 'cbc', '');

// pad key and IV by zeros (this is not a good idea)
$key = str_pad($key, $ks, "\0");
$iv = str_pad($key, $iv, "\0");

// initialize the decryptor with key and IV
mcrypt_generic_init($cp, $key, $iv);

// the actual work
$decrypted = mdecrypt_generic($cp, $content);

// clean up
mcrypt_generic_deinit($cp);
mcrypt_module_close($cp);

// remove padding, see below
return unpad($decrypted);
}

The last unpad is there to remove the padding which was likely appended by the encryption function to enlarge the message size to a full number of blocks. The default padding used by RijndaelManaged is PKCS7-padding, which appends a number of bytes (between 1 to 16), each of which being equal to the number of bytes appended. In a real implementation, you would check after decryption that the padding is valid (i.e. that all these bytes have the same value), but for "quick and dirty" you can simply use something which checks the last byte and removes that many bytes. See the comments to mcrypt_decrypt for an example.

If you can change your C# code:

  • Note that it is usually not a good idea to use a fixed value (per key) as an initialization vector, and using the key itself as initialization vector is not good, either. Use a random initialization vector (sent together with the message), or see the next point.

  • Also, you normally don't want to use a (rather short) password directly as a key, but instead use a longer passphrase, and hash it with a salt (included with in the message) to derive the key. If you do this, you can also derive the initialization vector from the same two pieces of data (but in a way that they'll be different, using a key derivation function).
    To avoid brute-forcing your password from the encrypted file, use a slow hash function here (PBKDF-2 or bcrypt).

Rijndael Encryption/Decryption C# vs PHP

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

Rijndael 256 Encrypt/decrypt between c# and php?

If you want to use Rijndael256 in your C# application you have to set the BlockSize to 256.

RijndaelManaged rj = new RijndaelManaged();
rj.BlockSize = 256;

And then your iv has to be 256 bits long as well.

see SymmetricAlgorithm.BlockSize Property


Or the other way round: Currently your C# application uses Rijndael128 and so must your php script.

<?php
class Foo {
protected $mcrypt_cipher = MCRYPT_RIJNDAEL_128;
protected $mcrypt_mode = MCRYPT_MODE_CBC;

public function decrypt($key, $iv, $encrypted)
{
$iv_utf = mb_convert_encoding($iv, 'UTF-8');
return mcrypt_decrypt($this->mcrypt_cipher, $key, base64_decode($encrypted), $this->mcrypt_mode, $iv_utf);
}
}

$encrypted = "UmzUCnAzThH0nMkIuMisqg==";
$key = "qwertyuiopasdfghjklzxcvbnmqwerty";
$iv = "1234567890123456";

$foo = new Foo;
echo $foo->decrypt($key, $iv, $encrypted);

prints hello world



Related Topics



Leave a reply



Submit