I am trying to listen to the Foxycart XML Datafeed in C# and running into an issue which boils down to encryption.
In short, they send over their data as encoded and encrypted XML using RC4 encryption.
To test, they have some (user submitted) sample code to test this with C#. I tried using this sample RC4 decryption code provided by one of the users but it doesn't seem to work and their support staff thinks its down with the C# RC4 algorithm. Since they are not C# experts, i figured i would ask here. Here is the post on the FoxyCart forum
Anyway, here is the code that (tries to) simulate the response by encrypting an XML file and posting it to a URL (NOTE that DataFeedKey is a string that i have stored as a member variable):
public ActionResult TestDataFeed()
{
string transactionData = (new StreamReader(@"D:\SampleFeed.xml")).ReadToEnd();
string encryptedTransactionData = RC4.Encrypt(DataFeedKey, transactionData, false);
string encodedTransactionData = HttpUtility.UrlEncode(encryptedTransactionData, Encoding.GetEncoding(1252));
string postData = "FoxyData=" + encodedTransactionData;
var req = (HttpWebRequest)WebRequest.Create("http://localhost:3396/FoxyCart/RecieveDataFeed");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
var sw = new StreamWriter(req.GetRequestStream(), Encoding.ASCII);
sw.Write(postData);
sw.Close();
HttpWebResponse resp = null;
try
{
resp = (HttpWebResponse)req.GetResponse();
string r = new StreamReader(resp.GetResponseStream()).ReadToEnd();
}
catch (WebException ex)
{
string err = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();
}
return null;
}
and here is the callback method that receives the response.
[ValidateInput(false)]
public ActionResult RecieveDataFeed(FormCollection collection)
{
string unencodedFeed = HttpUtility.UrlDecode(collection["FoxyData"], Encoding.GetEncoding(1252));
string transaction = RC4.Decrypt(DataFeedKey, unencodedFeed, false);
return Content("foxy");
}
Instead of posting the whole RC4 class inline in this question, here is a link to the code of this RC4 class.
As i posted in the above link at the top of the question, the issue is when i check the variable transaction inside the
RecieveDataFeed()
method, i should have the regular XML back but instead i see this:
É?xø´ v´“Û·8êUŸí¥MïSÅJÖó5Cå7ã…ÄlÞ&þòG·¶ÝÙ3<ÍÖ¡«úüF¿¿ßìNµ>4¦Äu÷¼Â;£-w¤ƒûÊyL¹®½èíYö½’é(µJŒ~»»=3¼]F‡•=±Ùí]'鳫"øPç{Ù^yyéå–°ñ…5ðWF$zÉnÄ^_”Xë’ï%œ-5á
ÒÛ€jŠt`Â9œÇÞLU&¼~ç2îžúo/¢¶5,º*öOqÝ—‘.ó®šuf™å5G—õC®‰ÁéiÇúW®¦ÝÚ•Z±:„Q\p"p
ôÔiÛ!\D"ÉÂX3]ƒ°è€Œ«DQE‡kÝ@àö`gpöŽ÷nÛ={µÏßKQKüå(ö%¯¯Ü–9}¨¬°£7yo,«”ÜëCÍ/+…†ÕËî‘‹‰AÚmÇÙå©&©¡xÙkŒföX¯ÃX&×°S|kÜ6Ô°Üú\Ätóü-äUƆÈáÅ\ ’E8‚¤âÈ4Ž¾«ãΚ_Sï£y‰xJº•bm*jo›‰ÜW–[ô†ÆJÐà$½…9½šžˆ_ÙÜù/®öÁVhzŠ¥ú(ñ£²6ˆb6¢ëße¤oáIðZuK}ÆÙ]"T¼*åZêñß5K—½òQSåRN Çë'Å¡
ÕyiÈX •bØðIk¿WxwNàäx®‹?cv+X™¥E!gd4â¤nÔ‹¢½Ð”ªÊQ!‚.e8s
Gyª4¼ò,}Yœ‚¹”±E‡Jy}Sæ
ƒ¦ýK'Ð}~B¦E3!0°ú´A–5Þ³£9$–8äÏ©?
œ‡8GÂø
The code looks right:
but it doesn't seem to be working. Any suggestions on what might be wrong above?
I am a bit surprised by the code in the CR4 class. I can't see how it would work reliably.
The code uses windows-1252 encoding to encode characters into bytes, then it encrypts the bytes and tries to decode the bytes into characters. That won't work reliably, as you can only decode bytes that comes from encoding characters.
The method takes a string and returns a string, but it should take a byte array and return a byte array, similar to how all the encryption classes in the framework does it.
Here is a version that works like that:
public class RC4 {
public static byte[] Encrypt(byte[] pwd, byte[] data) {
int a, i, j, k, tmp;
int[] key, box;
byte[] cipher;
key = new int[256];
box = new int[256];
cipher = new byte[data.Length];
for (i = 0; i < 256; i++) {
key[i] = pwd[i % pwd.Length];
box[i] = i;
}
for (j = i = 0; i < 256; i++) {
j = (j + box[i] + key[i]) % 256;
tmp = box[i];
box[i] = box[j];
box[j] = tmp;
}
for (a = j = i = 0; i < data.Length; i++) {
a++;
a %= 256;
j += box[a];
j %= 256;
tmp = box[a];
box[a] = box[j];
box[j] = tmp;
k = box[((box[a] + box[j]) % 256)];
cipher[i] = (byte)(data[i] ^ k);
}
return cipher;
}
public static byte[] Decrypt(byte[] pwd, byte[] data) {
return Encrypt(pwd, data);
}
}
Example:
string data = "This is a test.";
byte[] key = { 1, 2, 3, 4, 5 };
// encrypt
byte[] enc = RC4.Encrypt(key, Encoding.UTF8.GetBytes(data));
// turn into base64 for convenient transport as form data
string base64 = Convert.ToBase64String(enc);
Console.WriteLine(base64);
// turn back into byte array
byte[] code = Convert.FromBase64String(base64);
// decrypt
string dec = Encoding.UTF8.GetString(RC4.Decrypt(key, code));
Console.WriteLine(dec);
Output:
5lEKdtBUswet4yYveWU2
This is a test.