Tuesday 17 November 2009

PHP C# WCF Rijndael

I have been working with .NET and webservices for some time, but a colleague of mine has now moved over to using WCF instead of basic webservices.

After many, many problems dealing with simple soap message exchanges, we finally got our WCF service to talk to PHP (using NuSOAP) by setting the encoding to UTF8 and the binding to type basicHttpBinding.

My next problem was why when I was encoding data using Rijndael, was the encoded data returned from WCF not being decoded by mcrypt in PHP correctly.

Eventually, after some digging around, I found a really good article at http://benvanmol.blogspot.com/2009/10/secure-communication-between-net-and.html that summises the following points:

• You need to enable padding in the .net rijndael class. This ensures any blocks are padded with 0’s to fill them. This is done with Padding.Zeros
• Specifically set Cipher Block Chaining in the .Net class.
• The Initialisation Vector (IV) needs to be 16 bytes.
• You need to specifically set the encryption to the MD5 Hash algorithm, and change this to return the 32 character hash.
• Setup base64 encoding either end.

So, from this article by Ben van Mol I was able to produce a class from his code as follows:


public class PhpRijndael
{
System.Security.Cryptography.Rijndael r = null;

public void InitializePhpRijndael(string iv, string key,int blockSize)
{
byte[] keyBytes = Encoding.ASCII.GetBytes(EncodeTo64(key));
byte[] hash = MD5.Create().ComputeHash(keyBytes);
string ret = "";
foreach (byte a in hash)
{
ret += a.ToString("x2");
}
string iv64 = EncodeTo64(iv);
byte[] ivBytes = Convert.FromBase64String(iv64);

r = System.Security.Cryptography.Rijndael.Create();

r.Padding = PaddingMode.Zeros;
r.BlockSize = blockSize;
r.Key = Encoding.ASCII.GetBytes(ret);
r.IV = ivBytes;

}

public string Decrypt(string str)
{
byte[] encryptedBytes = Convert.FromBase64String(str);
byte[] decryptedBytes = transformBytes(
r.CreateDecryptor(), encryptedBytes);

string plaintext = Encoding.ASCII.GetString(decryptedBytes);
int idx = plaintext.IndexOf("\0");
if (idx > -1)
plaintext = plaintext.Substring(0, idx);

return plaintext;
}

public string Encrypt(string plaintext)
{
byte[] plainBytes = Encoding.ASCII.GetBytes(plaintext);
byte[] encryptedBytes = transformBytes(
r.CreateEncryptor(), plainBytes);

return Convert.ToBase64String(encryptedBytes);
}

private byte[] transformBytes(ICryptoTransform transform,
byte[] plainBytes)
{
MemoryStream memStream = new MemoryStream();
CryptoStream cryptStream =
new CryptoStream(memStream, transform,
CryptoStreamMode.Write);
cryptStream.Write(plainBytes, 0, plainBytes.Length);
cryptStream.Close();
byte[] encryptedBytes = memStream.ToArray();
memStream.Close();

return encryptedBytes;
}

private string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);

return returnValue;

}
}


In WCF I expose this in my endpoint as:

private string passPhrase = "secret";
private string initVector = "ujkrtadxcfrzpj1Bs5fpM18doZQDGYS4";
private int keySize = 256;

public string EncryptData(string value)
{

PhpRijndael r = new PhpRijndael();
r.InitializePhpRijndael(initVector, passPhrase, keySize);
string ret = "";
ret = r.Encrypt(value);
return ret;
}
public string ReturnTestData()
{
return EncryptData("Test Encrypted Data");
}

Note: Both of these are expose on the interface as OperationContracts inside the ServiceContract.

Now, in the PHP world, we need to create a page to call this, so inside a normal html form, we do the following:

if( isset($_POST['Submit']) )
{
//Show me all errors please
error_reporting(E_ALL);
ini_set('display_errors', '1');
//load nusoap
require_once('../lib/nusoap.php');
//client config to CZ
$client = new nusoap_client('http://example/Service?wsdl',true);
$client->setEndpoint("http://example/Service/EndpointName");
$client->setUseCurl(0);
//set utf-8, or else it breaks.
$client->soap_defencoding='UTF-8';
$client->setDebugLevel( 1 );
//any errors?
$err = $client->getError();
if ($err) {
echo '

Constructor error

' . $err . '
';
echo '

Debug

' . htmlspecialchars($client->getDebug(), ENT_QUOTES) . '
';
exit();
}
// This is a paramter list that can be used later, but not used at the moment
$params = array(
'value' => "666"
);
//Call dummy method for some test info
$result = $client->call('ReturnTestData',$params);
//is there a fault?
if ($client->fault) {
echo '

Fault

'; print_r($result); echo '
';
} else {
$err = $client->getError();
if ($err) {
echo '

Error

' . $err . '
';
} else {
echo '

Result

'; print_r($result); echo '
';
}
}

//decode the base sixty four encoded data returned
$bsf = base64_decode(trim($result["ReturnTestDataResult"]));

//setup auth
$key = 'secret';
$iv='ujkrtadxcfrzpj1Bs5fpM18doZQDGYS4';

//a function to configure Rijndael from the afforementioned article
function init_rijndael ($key,$iv) {
$rj = mcrypt_module_open('rijndael-256', '', 'cbc', '');
if ($rj !== FALSE)
{
$expected_key_size = mcrypt_enc_get_key_size($rj);

$key = substr(md5(base64_encode($key)), 0, $expected_key_size);

mcrypt_generic_init($rj, $key, $iv);
}
return $rj;
}
//setup Rijndael
$rj = init_rijndael($key,$iv);

//decrypt result
$decryptedResult = mdecrypt_generic($rj, $bsf);
//print out.
echo '

Result

'; echo($decryptedResult); echo '
';
}


And hey presto - finally! a working php to WCF communication in Rijndael!

Friday 6 November 2009

203.117.91.73 sdra64.exe

My eset firewall started complaining last night about connections going outbound to the IP address 203.117.91.73

After a call to ESET, I found out that that I had a trojan virus called sdra64.exe on my machine!

How do you get rid of it?

The guy from ESET firstly check for the virus using a program called RootAlyzer. This confirmed the infection. Next he downloaded procexp.exe from Microsoft to see what dependancies sdra64.exe had. Using another program called Process Unloader, he then closed the handles to sdra64.exe, and all files located in c:\windows\system32\lowsec. Once this was done, he created blank executable from notepad, and overwrote sdra64.exe, then deleted the lowsec folder.

Finally, in the registry under windowsnt\winlogon, he removed the reference to sdra64.exe in the userinit key.

A nasty little beast of a virus, and apparently acquired randomly from visiting a website!