8

Encrypting and Decrypting Soap Data

 

This chapter dapples with Encrypting and Decrypting Soap Headers. It presupposes that you would possess some basic knowledge of the elements contained in an XML document. To put it precisely, it expects you to be cognizant of the process of accessing the contents of the nodes that constitute an XML document. This is for the reason that a SOAP payload is merely an XML document consisting of nodes, which in turn, also have nodes. These nodes are required to be accessed prior to modifying their contents.

 

The opening program in this chapter elucidates and establishes how you can gain access to a node in an XML file, in order to extract the relevant information from it. We have created a new directory named ddd in the root, and created the following two files in this directory:

 

a.cs

using System;

using System.IO;

using System.Xml;

public class zzz

{

public static void Main()

{

FileStream s = new FileStream("a.txt", FileMode.Open, FileAccess.Read);

XmlTextReader r = new XmlTextReader (s);

XmlDocument d = new XmlDocument();

d.Load(r);

XmlNamespaceManager m = new XmlNamespaceManager(d.NameTable);

m.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode n;

n = d.SelectSingleNode("//soap:Body", m);

System.Console.WriteLine(n.InnerText + " " + n.Name);

n = n.FirstChild;

System.Console.WriteLine(n.InnerText + " " + n.Name);

n = d.SelectSingleNode("//soap:Body", m);

n = n.FirstChild.FirstChild;

System.Console.WriteLine(n.InnerText + " " + n.Name);

}

}

 

a.txt

<?xml version="1.0" encoding="utf-8" ?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body>

<abcResponse xmlns="http://tempuri.org/">

<abcResult>vijay mukhi</abcResult>

</abcResponse>

</soap:Body>

</soap:Envelope>

 

Compile the .cs file using the csc compiler, and then, run the executable. The output generated is shown below.

 

Output

vijay mukhi soap:Body

vijay mukhi abcResponse

vijay mukhi abcResult

 

This program kicks-off and gets going by creating object 's', which is an instance of a FileStream class. This constructor accepts three parameters:

•     The name of the file - a.txt,

•     The mode to decide whether the File should be created or not - FileMode.Open

•     Whether the file is to be opened for reading or writing - FileAccess.Read.

 

The file a.txt is a clone of one of the SOAP responses received earlier by the client from the server.

 

Now, in order to access specific parts of the SOAP response packet, an object r of type XmlTextReader is created.  The XmlTextReader class requires either a Stream object or a class derived from the Stream class as a parameter. Hence, the constructor is supplied with the FileStream object 's'.

 

Once we have introduced a class that can read our XML file from disk, we require the services of another class named XmlDocument, which shall facilitate access to this XML file. The Load function in the XmlDocument object d is passed an XmlTextReader object. Now, when the object d is used, it allows access to each individual node in the file.

 

Every XML Document has a NameTable object, which stores the names of the elements and attributes, in the form of atomized strings. The data type of the NameTable is XmlNameTable.

 

The XmlNamespaceManager class deals with namespaces, which is perfectly suited to our requirement. The constructer is given a NameTable property as a parameter. The namespace prefixes are associated with the URIs using this Namespace manager. On a perusal of the a.txt file, you would notice that all the elements are prefaced by the namespace 'soap', which has a URI of http://schemas.xmlsoap.org/soap/envelope/. Therefore, this pair must be registered with the Namespace manager.

 

The XmlNode object is used to represent a node in the XML file. The SelectSingleNode function in the object is employed to return the first node for the specified parameter. In this case, the first node is returned as 'Body', with a namespace prefix of 'soap'. The second parameter to the function is the namespace manager 'm', which is conversant with the 'soap' namespace prefix. In order to verify the outcome of our actions so far, we display the InnerText and the Name properties of the node that we are currently pointing to.

 

The InnerText property provides the final content, which could include the child elements; while the Name property provides the full name, including the namespace prefix soap. Thus, the InnerText  is 'vijay mukhi', and the Name is 'soap:Body'.

 

Each node is blessed with children and grandchildren. The FirstChild property provides a handle to the first child, abcResponse. The InnerText property offers the same answer as before, i.e. 'vijay mukhi', since the node of abcResult contains this text. In order to access the grandchild abcResult, the FirstChild of the abcResponse node is employed. We have printed out its name for the purpose of verification.  Thus, we now have a mechanism to access the different nodes embodied in the XML file.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

public class zzz

{

public static void Main()

{

string s;

s = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n<soap:Body><abcResponse xmlns=\"http://tempuri.org/\">\n<abcResult>vijay mukhi</abcResult>\n </abcResponse></soap:Body></soap:Envelope>";

byte[] ms = Encoding.UTF8.GetBytes(s);

MemoryStream ms1 = new MemoryStream(ms);

XmlTextReader r = new XmlTextReader(ms1);

XmlDocument d = new XmlDocument();

d.Load(r);

XmlNamespaceManager m = new XmlNamespaceManager(d.NameTable);

m.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode n;

n = d.SelectSingleNode("//soap:Body", m);

n = n.FirstChild;

System.Console.WriteLine(n.InnerText + " " + n.Name);

}

}

 

Output

vijay mukhi abcResponse

 

This example is at a slight variance with the earlier one. Instead of opening a file containing the xml code, it now contains a String s, which represents the entire XML file.

 

The string has to be converted into an array of bytes. To achieve this, we utilise the static GetBytes function. The array of bytes received from the function, is then supplied to the MemoryStream constructor.

 

The MemoryStream class, derived from the Stream object, represents a stream in memory, and not on disk. Thus, we are entitled to supply the MemoryStream object to the XmlTextReader constructor. The rest of the program is identical to the earlier one.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

public class zzz

{

public static void Main()

{

string s;

s = "ABC";

byte[] ms = Encoding.UTF8.GetBytes(s);

MemoryStream ms1 = new MemoryStream(ms);

byte[] ms2 = ms1.ToArray();

StringBuilder s1 = new StringBuilder();

for(int i=0; i<ms2.Length; i++)

{

if(i==(ms2.Length-1))

s1.Append(ms2[i]);

else

s1.Append(ms2[i] + " ");

}

Console.WriteLine(s1);

}

}

 

Output

65 66 67

 

In the above program, a string variable 's' is initialized to ABC. Now, we intend to convert the string into an array of bytes. For this, we use the GetBytes function. The output is stored in the byte array called 'ms'. This byte array represents our string. It is thereafter, supplied to a MemoryStream object named 'ms1'.

 

After having converted the string into a stream, we embark on the reverse procedure, i.e. we use the ToArray function of the MemoryStream class, to convert a stream back into an array of bytes. Thereafter, the StringBuilder class is used to display this array.

 

The byte array has a size of 3, since the string has three characters. Now, using the 'for' loop, the individual strings are appended to the StringBuilder object s1, thereby, concatenating all of them. Since we desire to insert a space between the individual numbers, an extra space is subjoined while adding the number to the StringBuilder object. However, no space is added after the last character. Finally, the three numbers, along with the spaces, are displayed using the WriteLine function with s1 .

 

Having grappled with these essentials, we now progress onto the very heart of the chapter, i.e. Encryption and Decryption.

 

 

DES is an acronym for Data Encryption Standard. It is one of the most widely used standards for encryption. Other symmetric algorithms, such as RC2, also exist.

 

Symmetric algorithms use the same key or password for encryption and decryption; while the Asymmetric algorithms, such as private key and public key, use different passwords or keys for encryption and decryption. A password and key are synonyms, wherein, passwords are English-like and can easily be committed to the memory; however, keys cannot be memorized.

 

One of the most widely used techniques in the world of security is authentication of messages, where algorithms like MD5 or Message Digest 5 are implemented. On the basis of the message text, a MD5 hash number is generated for the document. This document is then sent across the wire, along with the number in an encrypted form. In the event of a single byte of the file being tampered with, the hash would change, thereby, apprising the receiver of the fact that the document has been modified in transit.

 

Given below is an example of DES, where bytes are encrypted and the new value is displayed.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

using System.Security.Cryptography;

public class zzz

{

public static void Main()

{

Byte[] k = {1, 2, 3, 4, 5, 6, 7 , 8};

Byte[] k1 = {4,5,6,7,8,9,10,11};

DESCryptoServiceProvider d = new DESCryptoServiceProvider();

MemoryStream ms1 = new MemoryStream();

CryptoStream s = new CryptoStream (ms1, d.CreateEncryptor( k, k1 ), CryptoStreamMode.Write);

byte[] b = Encoding.UTF8.GetBytes("ABCD");

s.Write(b, 0,b.Length);

s.FlushFinalBlock();

byte[] b1 = ms1.ToArray();

StringBuilder s1 = new StringBuilder();

for(int i=0; i<b1.Length; i++)

{

if(i==(b1.Length-1))

s1.Append(b1[i]);

else

s1.Append(b1[i] + " ");

}

Console.WriteLine(s1);

}

}

 

Output

96 216 128 107 226 104 164 58

 

We commence by creating an object of type DESCryptoServiceProvider, which is in the namespace of System.Security.Cryptography. This namespace has infinite classes for encoding and decoding data, hashing, generating random numbers, et al. The class DESCryptoServiceProvider is the only pathway granting access to the classes that deal with the DES standard.

 

Therefore, we first create an object of type DESCryptoServiceProvider, and then, set up a memory stream. To comprehend DES, we also need to create an object 's', of the class CryptoStream. The constructor of this class is supplied with three parameters.

 

The first parameter is a stream, which shall hold the results of the encryption. Since we are not too keen on saving this output to a file on disk, we have specified a memory stream named 'ms1'.

 

The second parameter is of type ICryptoTransform. The value specified here determines the cryptographic transform that is required to be carried out. One such value that can be specified is 'hashing'. The CreateEncryptor function in object 'd', accepts two parameters:

•     An 8-byte key or password in k

•     An 8-byte initialization in k1.

 

In real life, the password should be larger, and should normally contain some random digits. Thus, the output generated would be used to encrypt the string "ABCD".

 

The last parameter is an enum CryptoStreamMode that can take either of the two values, Read or Write. As we wish to write into the stream, the value given here is CryptoStreamMode.Write.

 

The string "ABCD" is then converted into an array of bytes. This is because, in the .Net world, an array of bytes is given preference over a string. As humans, we love to work with strings, but here, who pays heed to our preferences? The array is then written to the CryptoStream, using the Write function. There are three parameters to the Write function:

•     The byte array b.

•     The starting point.

•     The length of the byte array.

 

As always, the bytes are flushed.

 

The next task is to ascertain the contents of the MemoryStream object ms1, which was passed to the constructer of the CryptoStream class. ms1 now contains the string "ABCD" in its encrypted form.

 

To do so, the function ToArray of the Memory Stream is utilized to return the data that it contains, in the form of a series of bytes. The output displayed is the encrypted form of the string "ABCD". Since we wish to insert a space between each number that is displayed, we pursue an approach similar to the one that we had adopted earlier, in the case of the StringBuilder class. The encrypted form of the string "ABCD" is displayed, along with the spaces that we have inserted.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

using System.Security.Cryptography;

public class zzz

{

public static void Main()

{

Byte[] k = {1, 2, 3, 4, 5, 6, 7 , 8};

Byte[] k1 = {4,5,6,7,8,9,10,11};

DESCryptoServiceProvider des = new DESCryptoServiceProvider();

string s = "96 216 128 107 226 104 164 58";

char[] c = {' '};

string[] ss = s.Split(c);

byte[] b = new byte[ss.Length];

for(int i=0; i<b.Length; i++)

{

b[i] = Byte.Parse(ss[i]);

}

MemoryStream ms = new MemoryStream();

CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor ( k, k1 ), CryptoStreamMode.Write);

cs.Write(b, 0,b.Length);

cs.FlushFinalBlock();

byte [] b2 = ms.ToArray();

string s1 = Encoding.UTF8.GetString(b2);

Console.WriteLine(s1);

}

}

 

Output

ABCD

 

This program, in a sense, is a continuation of the earlier program. The encrypted data is now decrypted, using the DES standards.

 

The string 's' is initialized to the encrypted form of the string "ABCD", as displayed in the earlier program. Since the spaces are also part of the string, the first task is to get rid of these spaces. They were inserted in the earlier program, for visual clarity of the numbers. In order to remove these spaces, we have created a single byte array of chars, containing only one character, i.e. 'space'.

 

The 'split' function that is part of the String class, accepts one parameter, and splits a string on the occurrence of a specific character or characters, in the array of chars. In our case, the string is split into 8 strings, since there are a total of 7 spaces. These 8 numbers are accommodated in a new string array named 'ss'.

 

Finally, we need to copy these 8 strings into the array of bytes named 'b'. The Byte class has a static Parse function, which converts a string stored in an array location into a byte.

 

Thereafter, just like before, a memory stream object ms is created to contain the new value in the memory. The CryptoStream object 'cs' is created, but it now employs the CreateDecryptor function, since we want to decrypt the content’s. The last enum parameter is Write, because we intend to write the decrypted string to the memory stream.

 

The Write function in the CryptoStream class obtains the array of bytes from the byte array containing the encrypted data, and then, uses the DES standard to decrypt it.

 

As we are keen on witnessing the outcome of our actions, we convert the Memory Stream object ms into a byte array 'b2', and then, use the GetString function to convert this byte array into a string.

 

The output reveals the string "ABCD", thus proving beyond doubt, that the string "ABCD" was encrypted into some unintelligible mumbo-jumbo using DES, and thereafter, the mumbo-jumbo was decrypted back to the string "ABCD".

           

a.aspx

<HTML>

<HEAD>

<script language="c#" runat=server>

public void Page_Load()

{

zzz a = new zzz();

l.InnerHtml = a.abc();

}

</script>

</HEAD>

<BODY>

<b id="l" runat="server"/>

</HTML>

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

[vijay]

public string abc()

{

return "ABCD";

}

}

 

aaa.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Security.Cryptography;

[AttributeUsage(AttributeTargets.Method)]

public class vijay : SoapExtensionAttribute

{

public override Type ExtensionType

{

get

{

return typeof(mukhi);

}

}

public override int Priority

{

get

{

return 0;

}

set

{

}

}

}

public class mukhi : SoapExtension

{

Stream o;

Stream n;

Byte[] k = {1, 2, 3,4,5, 6, 7 , 8};

Byte[] k1 = {4,5,6,7,8,9,10,11};

public override object GetInitializer(LogicalMethodInfo m, SoapExtensionAttribute a)

{

return a;

}

public override object GetInitializer(Type t)

{

return typeof(vijay);

}

public override void Initialize(object i)

{

return;

}

public override void ProcessMessage(SoapMessage m)

{

if ( m.Stage == SoapMessageStage.BeforeDeserialize)

{

abc("BeforeDeserialize");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(n);

w.WriteLine(r.ReadToEnd());

w.Flush();

n.Position = 0;

}

if ( m.Stage == SoapMessageStage.AfterSerialize)

{

abc("AfterSerialize " + n.Position);

n.Position = 0;

XmlTextReader rr = new XmlTextReader(n);

XmlDocument d1 = new XmlDocument();

d1.Load(rr);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

abc(no.Name + " " + no.InnerText);

no = no.FirstChild.FirstChild;

abc(no.Name + " " + no.InnerText);

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

byte[] inp = Encoding.UTF8.GetBytes(no.InnerText);

MemoryStream ms1 = new MemoryStream();

CryptoStream cs = new CryptoStream(ms1, de.CreateEncryptor( k, k1 ), CryptoStreamMode.Write);

abc("Memory Stream Before" + ms1.Position);

cs.Write(inp, 0, inp.Length);

abc("Memory Stream Before" + ms1.Position);

cs.FlushFinalBlock();

abc("Memory Stream Before" + ms1.Position);

byte[] ou = ms1.ToArray();

string s1 = "";

abc(ou.Length.ToString());

for(int i = 0; i< ou.Length ; i++)

s1 = s1 + ou[i].ToString();

no.InnerText = s1;

abc(ou[0].ToString());

abc(s1);

MemoryStream ms = new MemoryStream();

d1.Save(ms);

abc("Position of ms " + ms.Position );

ms.Position = 0;

n = ms;

TextReader r1 = new StreamReader(n);

TextWriter w2 = new StreamWriter(o);

string s3 = r1.ReadToEnd();

w2.WriteLine(s3);

abc(s3);

w2.Flush();

}

}

public override Stream ChainStream( Stream stream )

{

o = stream;

n = new MemoryStream();

return n;

}

public void abc(string s)

{

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

}

 

SOAP response

<abcResponse xmlns="http://tempuri.org/">

<abcResult>9621612810722610416458</abcResult>

</abcResponse>

 

Browser Output

9621612810722610416458

 

a.txt

BeforeDeserialize

AfterSerialize 360

soap:Body ABCD

abcResult ABCD

Memory Stream Before0

Memory Stream Before0

Memory Stream Before8

8

96

9621612810722610416458

Position of ms 381

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abcResponse xmlns="http://tempuri.org/">

      <abcResult>9621612810722610416458</abcResult>

    </abcResponse>

  </soap:Body>

</soap:Envelope>

 

The above program is a protracted one, but all the same, it is absorbing, since it demonstrates the actions on the server-side, during encryption of a string value, which is to be dispatched to the client. It is our humble advice to you to ensue the steps outlined by us, while running this program. It is because, we are of the firm opinion that it is the safest and the most steadfast pathway, steering clear of any perplexity in understanding the concept.

 

The aspx file calls a function abc from the class zzz. The return value of this function is passed to the InnerHtml property of the bold element, which has an id of l. At this stage, we cannot run this aspx file in the browser, since no class named zzz exists in the bin folder. We will not create this zzz class; instead, we shall task the asmx file with this errand.

 

The asmx file has a function named abc, which returns the string ABCD. In addition to this, an attribute of 'vijay' is added to it. Since no code for 'vijay' is available at the moment, we get an error on loading the asmx. Hence, the WSDL file is not created.

 

Error

Compiler Error Message: CS0246: The type or namespace name 'vijay' could not be found (are you missing a using directive or an assembly reference?)

 

So, comment out the attribute 'vijay', and reload the asmx file.

 

[WebMethod]

//[vijay]

public string abc()

{

return "ABCD";

}

 

Save the wsdl contents in a file named aa.wsdl in the bin folder, and not in wwwroot folder. Add the port number 8080 to the localhost, and then, run the batch file named a.bat. This will create a dll named zzz.dll, which contains the code of the class zzz.

 

a.bat

cd \inetpub\wwwroot\bin

wsdl aa.wsdl

csc /t:library zzz.cs

 

Run the trace utility from the Soap Toolkit, if it is not already active, and then, in the browser window, load the aspx file as :

 

http://localhost/a.aspx

 

The browser window will display the string "ABCD". Further, the SOAP response packet will display the value of "ABCD", which is being sent across.

 

Now, instead of sending the string "ABCD" in its pristine form, we wish the server to encrypt it, and then, send it across to the browser.

 

To accomplish this, the attribute of 'vijay' is utilised. Remove the comments from the attribute 'vijay' in the a.asmx file, and thereafter, create the file aaa.cs in the bin folder. Now, compile the code in this file, and you will obtain a dll file.

 

\inetpub\wwwroot\bin>csc /t:library aaa.cs

 

Now, reload the asmx file to create the wsdl file. Change the port number to 8080 and run the batch file.

 

In aaa.cs, there exist a large number of 'using' statements. Although, quite a few of them are redundant, with no utility at all, the one positive aspect of a few extra statements strewn around is that, their presence is innocuous. The Attribute AttributeUsage restricts the use of the attribute 'vijay' only on methods.

 

Next, we create the actual attribute class 'vijay', derived from SoapExtensionAttribute. The code in this class is not substantial, except for the read-only property of ExtensionType, which returns the class 'mukhi'. The class 'vijay' handles the parameters that are passed from the attribute that gets forwarded to the 'mukhi' class.

 

Let us now proceed and press onto the main code in the class 'mukhi', wherein, our primary focus shall be on the ProcessMessage function. In this function too, our main object of interest is the AfterSerialize event. This is because, the SOAP packet has already been created. The main task that remains to be accomplished is, the encryption of the response that is being sent over.

 

But prior to that, the stream object passed to the ChainStream function as a parameter is stored in a public Stream variable named 'o'. The data that is written to MemoryStream object o, would be utilized by SOAP every now and then. Thereafter, the Memory Stream object 'n' is initialized and supplied as the return value of the function.

 

Although we have no business to transact with the BeforeDeserialize event, the presence of this event is mandatory, since the Stream object 'n' would remain empty in its absence. This would result in the loss of the SOAP request data.

 

In the BeforeDeserialize event, a TextReader object 'r' is created, using the Stream object 'o'. Moreover, a TextWriter object 'w' is also created, using the empty MemoryStream 'n'.

 

The entire SOAP request that reads from the TextReader 'r', is then written to the Writer. If the Stream 'w' in not flushed, nothing could possibly be written. As the SOAP infrastructure expects the file pointer to be positioned at the beginning, this basic housekeeping activity is also performed in the BeforeDeserialize event. The function abc can be used to corroborate our actions at each stage.

 

The main task rests in the AfterSerialize event, where the entire SOAP response that has been generated, is initially read. As the file pointer is at byte 360 ( revealed by the function abc), the pointer is repositioned at the beginning, by setting the Position property to 0.

 

Here, an XmlTextReader object 'rr' is created from the Stream object 'n', and then, an Xml Document object 'd1' is instantiated. Thereafter, the Load function in the document class is used, to read the SOAP response 'rr' into the XmlDocument object. The namespace of 'soap' is added to the Namespace manager, along with its url. The object no of type XmlNode is then made to represent the first node body in the SOAP response.

 

The function abc displays the name of the node body and its content, using the properties of Name and InnerText. Since, the abcresult node is the only node of interest to us at this point in time, the value within FirstChild in the FirstChild member is used. The function abc prints the values, and we stand vindicated, since our stance has been verified.

 

Next, we create an instance of the wrapper class DESCryptoServiceProvider. A wrapper or helper class shields us from the trivia or the nitty-gritty of the class. These repugnant details are carefully wrapped up by this wrapper class.

 

The InnerText property of XmlNode contains the string that we desire to encrypt. Since it is in a string form, it has to be converted into an array of bytes. The GetBytes function converts the string into bytes, and thereafter, stores them into an array named 'inp'.

 

Now, arises the requirement of a MemoryStream object, which is to be provided to the CryptoStream class. The CryptoStream class is the class that is used to encrypt the bytes. The CryptoStream constructor takes three parameters, which are as follows:

•     The first parameter is the MemoryStream object.

•     The second parameter is the encryption that needs to be performed. Therefore, the function comprises of an 8-bye key in 'k', and a 8-byte initialization in 'k1'.

•     The third parameter is the mode which represents either a read or a write.

 

Once the CryptoStream object cs is created, we use the Write function to write the array 'inp', which accommodates the contents of the tag abcResult, in the byte form. The second parameter to the Write function is the incipient point in the array. A value of zero indicates the beginning of the array. The last parameter is the length of the array, which needs to be written. The FlushFinalBlock function is like the Flush function, which actually does the writing.

 

Now, the MemoryStream ms1 contains the encrypted bytes. The output of the function abc provides ample evidence of the fact that, although the Write function may be writing, it is actually the Flush function that writes the bytes to the MemoryStream.

 

The ToArray function is used to convert the encrypted bytes in the Memory Stream object, into an array of bytes. Now, this 8-byte array has to be converted into an ASCII string. So, we use a loop construct, wherein each and every byte of the array is concatenated to the string s1. This string, which is the encrypted version of our SOAP response, is then written to disk.

 

The InnerText property of the XmlNode is also initialized to this string, resulting in the initialization of the XML document.

 

The XML document is then saved in the MemoryStream object named 'ms'. This new MemoryStream contains the same XML file that the SOAP infrastructure had created. However, there is a minor variation here, i.e. the contents of abcResult tag are encrypted.

 

The File pointer in the new MemoryStream, which is at Position 381 due to the saving, is now repositioned at the beginning of the file. Then, the MemoryStream 'n' is equated to 'ms', and a new TextReader object is created.

 

The TextWriter object uses the stream 'o', since it is this stream that should contain the final SOAP payload. The stream 'o' in this event is used for writing the output that is to be sent across. The stream 'n' is used to read the SOAP packet that is generated.

 

The string s3 is used to store the SOAP response, which is read from the reader. Then subsequently, it is written out to the Writer, or to the Stream 'o'.

 

Yet another way of achieving this is, to read the bytes from the memory stream, convert them into a string, and then, to write them with the help of the w2 object. The string s3 substantiates the fact that, the SOAP response that is sent across, comprises of contents that are encrypted.

 

a.aspx

<HTML>

<HEAD>

<script language="c#" runat=server>

public void Page_Load()

{

zzz w = new zzz();

l.InnerHtml = w.abc("hi");

}

</script>

</HEAD>

<BODY>

<b id="l" runat="server"/>

</HTML>

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

[vijay(Enc=EM.Response,Dec=DM.Request)]

public string abc( string a)

{

return "vijay " + a;

}

}

 

zzz.cs

using System.Diagnostics;

using System.Xml.Serialization;

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

[System.Web.Services.WebServiceBindingAttribute(Name="HelloWorldSoap", Namespace="http://tempuri.org/")]

public class zzz: System.Web.Services.Protocols.SoapHttpClientProtocol

{

public zzz()

{

this.Url = "http://localhost:8080/a.asmx";

}

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc")]

[vijay(Enc=EM.Request,Dec=DM.Response)]

public string abc(string a)

{

object[] results = this.Invoke("abc", new object[]{a});

return ((string)(results[0]));

}

}

 

aaa.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Security.Cryptography;

[AttributeUsage(AttributeTargets.Method)]

public class vijay : SoapExtensionAttribute

{

EM emode= EM.None;

DM dmode= DM.None;

public override Type ExtensionType

{

get

{

return typeof(mukhi);

}

}

public override int Priority

{

get

{

return 0;

}

set

{

}

}

public EM Enc

{

get

{

return emode;

}

set

{

emode= value;

}

}

public DM Dec

{

get

{

return dmode;

}

set

{

dmode= value;

}

}

}

public enum DM

{

None,

Response,

Request

}

public enum EM

{

None,

Response,

Request

}

public class mukhi : SoapExtension

{

Stream o;

Stream n;

DM dmode;

EM emode;

Byte[] k = {1, 2, 3, 4, 5, 6, 7, 8};

Byte[] k1 = {10,11 , 12, 13, 14, 15, 16, 17};

public mukhi()

{

abc("Constructor");

}

public override object GetInitializer(LogicalMethodInfo m, SoapExtensionAttribute a)

{

abc("GetInitializer " + m.ToString());

return a;

}

public override object GetInitializer(Type t)

{

abc("GetInitializer 1");

return typeof(mukhi);

}

public override void Initialize(object i)

{

vijay a = (vijay) i;

dmode = a.Dec;

emode = a.Enc;

abc("Initialize " + i.ToString() + " " + a.Dec.ToString() + " " + a.Enc.ToString());

return;

}

public override void ProcessMessage(SoapMessage m)

{

if ( m.Stage == SoapMessageStage.AfterSerialize)

{

abc("AfterSerialize " + emode.ToString() + " " + n.Position );

n.Position = 0;

if ((emode == EM.Request) || (emode == EM.Response))

{

abc("AfterSerialize if");

n.Position = 0;

XmlTextReader rr = new XmlTextReader(n);

XmlDocument d1 = new XmlDocument();

d1.Load(rr);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

no = no.FirstChild.FirstChild;

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

abc(no.InnerText);

byte[] inp = Encoding.UTF8.GetBytes(no.InnerText);

MemoryStream ms1 = new MemoryStream();

CryptoStream cs = new CryptoStream(ms1, de.CreateEncryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

byte[] ou = ms1.ToArray();

StringBuilder s = new StringBuilder();

for(int i=0; i<ou.Length; i++)

{

if(i==(ou.Length-1))

s.Append(ou[i]);

else

s.Append(ou[i] + " ");

}

no.InnerText = s.ToString();

MemoryStream ms = new MemoryStream();

d1.Save(ms);

ms.Position = 0;

n = ms;

}

TextReader r = new StreamReader(n);

TextWriter w = new StreamWriter(o);

string s5 = r.ReadToEnd();

abc(s5);

w.WriteLine(s5);

w.Flush();

}

if ( m.Stage ==  SoapMessageStage.BeforeDeserialize)

{

abc("BeforeDeserialize " + dmode.ToString());

MemoryStream ds = new MemoryStream();

if ((dmode == DM.Request) || (dmode == DM.Response))

{

abc("BeforeDeserialize if");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(ds);

string s6 = r.ReadToEnd();

w.WriteLine(s6);

abc(s6);

w.Flush();

abc("Position of ds " + ds.Position);

ds.Position = 0;

XmlTextReader r0 = new XmlTextReader(ds);

XmlDocument d1 = new XmlDocument();

d1.Load(r0);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

no = no.FirstChild.FirstChild;

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

string s3 = no.InnerText;

abc(s3);

char[] c = {' '};

string[] ss = s3.Split(c);

byte[] b = new byte[ss.Length];

abc(ss.Length.ToString());

for(int i=0; i<b.Length; i++)

{

b[i] = Byte.Parse(ss[i]);

}

byte[] inp = b;

MemoryStream ms1 = new MemoryStream();

CryptoStream cs = new CryptoStream(ms1,de.CreateDecryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

byte[] ou = ms1.ToArray();

string s1 = Encoding.UTF8.GetString(ou);

no.InnerText = s1;

abc(s1);

MemoryStream ms = new MemoryStream();

if ( dmode == DM.Request)

d1.Save("c:\\z.txt");

else

d1.Save("c:\\y.txt");

d1.Save(ms);

ms.Position = 0;

TextReader r1 = new StreamReader(ms);

TextWriter w1 = new StreamWriter(n);

w1.WriteLine(r1.ReadToEnd());

w1.Flush();

}

else

{

abc("BeforeDeserialize else");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(n);

w.WriteLine(r.ReadToEnd());

w.Flush();

}

n.Position = 0;

}

}

public override Stream ChainStream( Stream stream )

{

abc("ChainStream");

o = stream;

n = new MemoryStream();

return n;

}

public void abc(string s)

{

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

}

 

a.txt

Constructor

GetInitializer System.String abc(System.String)

Constructor

Initialize vijay Response Request

ChainStream

AfterSerialize Request 326

AfterSerialize if

hi

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abc xmlns="http://tempuri.org/">

      <a>214 134 72 131 106 41 82 55</a>

    </abc>

  </soap:Body>

</soap:Envelope>

Constructor

GetInitializer System.String abc(System.String)

Constructor

Initialize vijay Request Response

ChainStream

BeforeDeserialize Request

BeforeDeserialize if

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abc xmlns="http://tempuri.org/">

      <a>214 134 72 131 106 41 82 55</a>

    </abc>

  </soap:Body>

</soap:Envelope>

Position of ds 355

214 134 72 131 106 41 82 55

8

hi

ChainStream

AfterSerialize Response 364

AfterSerialize if

vijay hi

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abcResponse xmlns="http://tempuri.org/">

      <abcResult>225 92 58 159 62 55 111 215 225 255 97 209 232 130 31 99</abcResult>

    </abcResponse>

  </soap:Body>

</soap:Envelope>

ChainStream

BeforeDeserialize Response

BeforeDeserialize if

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abcResponse xmlns="http://tempuri.org/">

      <abcResult>225 92 58 159 62 55 111 215 225 255 97 209 232 130 31 99</abcResult>

    </abcResponse>

  </soap:Body>

</soap:Envelope>

 

Position of ds 416

225 92 58 159 62 55 111 215 225 255 97 209 232 130 31 99

16

vijay hi

 

z.txt

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abc xmlns="http://tempuri.org/">

      <a>hi</a>

    </abc>

  </soap:Body>

</soap:Envelope>

 

y.txt

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <soap:Body>

    <abcResponse xmlns="http://tempuri.org/">

      <abcResult>vijay hi</abcResult>

    </abcResponse>

  </soap:Body>

</soap:Envelope>

 

Output in Browser

Vijay hi

 

a.bat

del *.dll

csc /t:library aaa.cs

csc /t:library zzz.cs /r:aaa.dll

 

This program sheds light on the concepts of encryption and decryption at their best.

 

The client program sends a SOAP request, which holds the encrypted value of "hi", to the server. The server receives the encrypted parameter, decrypts it and transmits it further, to the SOAP system. The function in the extension dll receives the parameter as 'hi', and reciprocates by sending the return value of 'vijay hi', in an encrypted form. Then, the SOAP framework creates a SOAP packet, containing the return value in an encrypted form, and then, sends it to the client. The client receives this encrypted value, and decrypts it prior to flaunting it in the browser window. This roller coaster ride is an endeavor to unravel the mystery behind all the actions transpired above.

 

We set out by grappling with the aspx file, as the first step. The function abc in this file accepts a string parameter and then, returns a string. Subsequently, the browser displays this string. The SOAP request and response packets that flow to and fro, neither display the input string as "hi", nor do they disclose the response value of "vijay hi", which is received from the server. All that they display, is a series of numbers, floating from one end to the other.

 

SOAP request

<soap:Body>

<abc xmlns="http://tempuri.org/">

  <a>214 134 72 131 106 41 82 55</a>

</abc>

  </soap:Body>

 

SOAP response

<soap:Body>

<abcResponse xmlns="http://tempuri.org/">

<abcResult>225 92 58 159 62 55 111 215 225 255 97 209 232 130 31 99</abcResult>

</abcResponse>

</soap:Body>

 

The asmx file has a function named abc in the class zzz. This function accepts one string parameter named 'a', and it returns a string that contains the word 'vijay', concatenated to this string parameter.

 

It also has an attribute named 'vijay', which in turn, owns two parameters named Enc and Dec, which are short forms for the terms Encrypt and Decrypt, respectively. Thus, the class attribute 'vijay' now possesses two properties. The values of these properties determine what needs to be done. EM and DM are enums, both of which specify that the request or response is to be either encrypted or decrypted. The third value in the enum is 'none'.

 

In our case, the values specified here are indicative of the fact that, the Response is to be encrypted, and the Request is to be decrypted.

 

The asmx file is located on the server. Thus, it represents the processing that is to be done at the server end. It is the bounden duty of the server to decrypt the data that has been encrypted by the client, and to encrypt the Response data, which shall subsequently be decrypted by the client.

 

Until now, the asmx file was being used to create the client program, in that, it was utilized for running the wsdl utility on the wsdl file, and for creating the .cs file. But for the moment, we shall write the client program manually, since we have to place the attribute named 'vijay'.

 

Candidly speaking, we employed the webserver for creation of the client program, and subsequently, we eliminated the code that was unnecessary, and thus, redundant. Thereafter, the attribute named 'vijay' made its debut.

 

Akin to the server, this attribute also accepts two parameters, viz. Enc=EM.Request, Dec=DM.Response. These values signify that the Request is to be encrypted and the Response is to be decrypted. The code remains almost identical, except for the introduction of the attribute named 'vijay'. We have provided you with the bird's eye view, before diving any deeper into comprehending the code of the attribute named 'vijay'.

 

When the URL with the aspx file is written in the browser, a Get or Post is dispatched to the Web Server. Let us designate the name 'A' for the Web Server. Subsequent to the file being positioned on the web server 'A', the aspx program gets converted into C# code, and the function abc is called from the program named zzz.dll. Thereafter, this function sends a SOAP packet, containing data encrypted by the SOAP extension dll, to another server, which is named as 'B' merely on the whim. This server 'B' actually contains the code of the function abc.

 

On receiving the SOAP payload, the server B executes the asmx file. The SOAP extension dll decrypts the data and calls the function abc, while furnishing it with the original data value of 'hi'. The return value of the function i.e. 'vijay hi', is then encrypted, and dispatched back to the client dll named zzz.dll, which is located on the Web Server A. It is then decrypted by the code in the 'vijay' attribute extension dll, and the string 'vijay hi' is then returned back. Finally, the Web Server A creates an html file with the return value, and transports it to the browser. At times, this concept can be truly baffling!

 

The file aaa.cs is the extension dll, which contains the encryption and decryption code for both, the client and the server. Thus, it has to handle a situation wherein, only the Serialize events are triggered-off to encrypt the request from the client, and both the Serialize and Deserialize events are triggered-off for the response packets from the server.

 

The extension dll contains two enum classes called DM and EM, which symbolise Decryption and Encryption, respectively. These enum classes identify the values to be encrypted or decrypted, such as- the request, the response, or no value at all!

So, when we specify the enum value of EM.Response, the interpretation rendered by the program is that, the Response has to be encrypted.

 

The class 'vijay' has two properties named Enc and Dec. Only those programs that employ the attribute of 'vijay', are entitled to the use of Enc and Dec properties; and It is these properties that 'set' or 'get' the value that is stored in the variables 'emode' and 'dmode'. In the present context, these variables store the state of the properties Dec and Enc.

 

Next, the GetInitializer function returns the second parameter that is cached. Since you have already been enlightened about the motive behind this call, we choose to ignore it at this stage. The system calls this function twice, since there are two instances of the attribute 'vijay':

•     First, for the client in zzz.cs

•     And then, for the server in a.asmx.

 

The prototypes of both the functions have to be identical. Firstly, the constructor is called, followed by the GetInitializer function. Thereafter, the constructor is called yet again; but this time, it is for the client data. The same process is repeated for the server as well.

 

The Initialize function gets called each time. Here, the parameter i represents the 'vijay' object. The value of the a.Dec property is Response, whereas, the value of the a.Enc property is Request. The values unearthed by the function abc, vindicate our claim. It is because, in the client zzz.cs, the parameters of the attribute 'vijay' are specified as Enc=EM.Request and Dec=DM.Response.

 

Thus, the Initialize function is called for the packet received from the client. In this function, the values of the properties Dec and Enc, are saved in the variables dmode and emode, respectively. This concludes the process of transferring data from the attribute 'vijay', pertaining to the client.

 

In the ProcessMessage function, it is the AfterSerialize event which is called first, since the data originates from the client. The ChainStream function sets up the Streams, prior to this event being fired. The Position parameter of the Stream object n is set to 0, because when the packet is received, the stream pointer is at the end of the Stream, at position 326. This number corresponds to the size of the SOAP payload.

 

Now, emode contains the value of Request, since the packet has to be encrypted. Just to jog your memory a little, this is so because, in the parameter of the 'vijay' attribute in the client program named zzz.cs, the value of ENC is set to EM.Request.

 

Now, in order to populate the byte array, the code that was specified earlier, has to be entered. In order to pay our respects to the original programmer who wrote this class, the StringBuilder class is used to concatenate the bytes from the byte array. Here, we iterate through the byte array and utilise the Append function to add a byte to the StringBuilder class named 's'. We have added a space between the encrypted values, in order to brandish the output in a more aesthetic manner. These spaces will have to be stripped off, prior to decryption of the numbers.

 

The InnerText is then initialized to the new output data, and the memory stream is created. Once the loop terminates, the contents of the stream 'n' are written to the Stream 'o'. The file a.txt displays the SOAP payload that is being sent over. Presently, only the first parameter will be encrypted. The code catering to encryption of multiple parameters is not entered.

 

Once the SOAP packet reaches the server, the decryption routine takes over. This is because, in the asmx file, the parameter supplied to the attribute 'vijay' is Dec=DM.Request. The ChainStream function is called prior to the firing of the BeforeDeSerialize event.

 

In this event, an empty MemoryStream object is created initially. Then, the value of the dmode variable is ascertained, to determine whether it is Request or Response.

 

Since the value contained in dmode is 'Request', we enter the 'if' statement, which will decrypt the SOAP payload sent from the client to the server. The encrypted SOAP request payload from the client, which is saved in the Stream 'o', is then transferred to the memory stream 'ds'. Since the Position property in the memory stream ds has changed to 355, it is reset to 0.

 

Now, we require an XmlDocument to accommodate the encrypted data in the MemoryStream 'ds'. Therefore, we create an XmlTextReader object named 'r0', and an XmlDocument object named 'd1'. The Load function is pressed into service to copy the XML data from the memory stream 'ds', to the XmlDocument 'd1'. After having provided for the other details, we access the first grandchild from the body. The XmlNode named 'no' represents this child node.

 

Thereafter, a wrapper object of class DESCryptoServiceProvider, is instantiated. Further, the encrypted text, which is contained in the node 'no', is assigned to a string s3, using the InnerText property. Since the text contains spaces, the Split function of the string is provided with the delimiter of the 'space' character, and the output is stored in a string array named 'ss'. There are a total of 7 spaces in the string s3. Therefore, the size of the string array ss is 8. Once this is achieved, we create a byte array named b, of length 8, and subsequently, iterate through a 'for' loop.

 

The static function Parse in the Byte class, converts the string into a byte, and stores it in a different member of the array. The member is decided on the basis of the value of the variable i. The loop continues till all the string bytes have been transferred into the array. We could have used the Byte array named 'b' for this purpose, but we decided to create a new one named 'imp'. Ask us not why!

 

The next task is to decrypt this data. In order to accomplish this, an empty MemoryStream object named 'ms1' is created and assigned to the CryptoStream constructor, while the object cs is being created. The second parameter is the CreateDecryptor function, with the same key and IV. This leads to the creation of an object, which will decrypt the byte array and place the contents in the MemoryStream ms1. The last parameter is the enum value of 'Write'.

 

Finally, the Write function in the stream is called, with the byte arrays that contain the encrypted values. The end result is that, the memory stream ms1 shall now contain the decrypted value. But hold your horses! This is attainable only when the stream is flushed. For this, the FlushFinalBlock function is put to use. The decrypted text from the MemoryStream ms1 is then acquired, using the ToArray function, which returns a byte array containing the word 'hi'. The bytes are converted into a string, using the GetString function. Ultimately, the value of 'hi' is displayed.

 

The task still remains unfinished, since an XmlDocument has yet to be created, with this new value. The InnerText of the XmlNode is set to 'hi', and a MemoryStream object 'ms' is created. Then, using the Save function, the entire XmlDocument is written to this object.

 

Finally, we employ the freshly created 'ms' MemoryStream as the Reader, and the MemoryStream 'n' as the Writer. All that is written to this stream, shall be passed on to the asmx program. The 'else' block is never called, since it is used only when the dmode variable has a value of None 0 no decryption. Thus, you would realize that both encryption and decryption, adhere to analogous rules.

 

The server calls the asmx file, with the value of the parameter 'a' as 'hi'. The asmx file returns 'vijay hi', which is used by the server to create a SOAP payload. The SOAP payload reaches the extension dll, where the AfterSerialize event gets called, but with the value of emode as Response. The SOAP payload is now encrypted and sent across.

 

On receiving the encrypted SOAP response, the client uses the extension dll and triggers off the BeforeDeSerialize event. The code contained in this block, converts the encrypted bytes to the value 'vijay hi', since the value of dmode is DM.Response. The files z.txt and y.txt, exhibit the actual SOAP payload that has been received by the server and the client, subsequent to the decryption.  The above illustration is indeed an enormous mental workout, but is worth comprehending.

 

Once you have ruminated and digested the above program, incorporate the following two modifications in the aaa.cs program:

•   Replace the two instances of the DESCryptoServiceProvider class, with the RC2CryptoServiceProvider class.

 

Change

DESCryptoServiceProvider de = new DESCryptoServiceProvider();  

To

RC2CryptoServiceProvider de = new RC2CryptoServiceProvider ();

 

The residual code remains unchanged. The eventual outcome is that, the algorithm adopted for the encryption of data is RC2, and not the DES. This is suggestive of the fact that, shifting from one algorithm to another, is indeed effortless.

 

SOAP request

<soap:Body>

<abc xmlns="http://tempuri.org/">

  <a>214 40 209 188 96 8 97 42</a>

  </abc>

</soap:Body>

 

SOAP response

<soap:Body>

<abcResponse xmlns="http://tempuri.org/">

  <abcResult>164 69 219 2 3 8 58 43 175 147 53 64 112 176 15 181</abcResult>

  </abcResponse>

  </soap:Body>

 

The SOAP payload here is different, since the encrypted data is at variance with the earlier data, however, this does not seem to hassle the program at all. Now, let us make rapid strides to the very last program of the chapter.

 

a.aspx

<HTML>

<HEAD>

<script language="c#" runat=server>

public void Page_Load()

{

zzz w = new zzz("RC2");

l.InnerHtml = w.abc("hi");

zzz w1 = new zzz("DES");

l1.InnerHtml = w1.abc("Bye");

}

</script>

</HEAD>

<BODY>

<b id="l" runat="server"/>

<p>

<b id="l1" runat="server"/>

</HTML>

 

zzz.cs

using System.Diagnostics;

using System.Xml.Serialization;

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

[System.Web.Services.WebServiceBindingAttribute(Name="HelloWorldSoap", Namespace="http://tempuri.org/")]

public class zzz: System.Web.Services.Protocols.SoapHttpClientProtocol

{

public yyy b;

public zzz(string s)

{

b = new yyy();

b.type = s;

this.Url = "http://localhost:8080/a.asmx";

}

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc")]

[vijay(Enc=EM.Request,Dec=DM.Response)]

[System.Web.Services.Protocols.SoapHeaderAttribute("b")]

public string abc(string a)

{

object[] results = this.Invoke("abc", new object[]{a});

return ((string)(results[0]));

}

}

public class yyy : SoapHeader

{

public string type;

}

 

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz

{

[WebMethod]

[vijay(Enc=EM.Response,Dec=DM.Request)]

public string abc( string a)

{

return "vijay " + a;

}

}

 

aaa.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Security.Cryptography;

[AttributeUsage(AttributeTargets.Method)]

public class vijay : SoapExtensionAttribute

{

EM emode= EM.None;

DM dmode= DM.None;

public override Type ExtensionType

{

get

{

return typeof(mukhi);

}

}

public override int Priority

{

get

{

return 0;

}

set

{

}

}

public EM Enc

{

get

{

return emode;

}

set

{

emode= value;

}

}

public DM Dec

{

get

{

return dmode;

}

set

{

dmode= value;

}

}

}

public enum DM

{

None,

Response,

Request

}

public enum EM

{

None,

Response,

Request

}

public class mukhi : SoapExtension

{

Stream o;

Stream n;

DM dmode;

EM emode;

static string ctype = "RC2";

Byte[] k = {1, 2, 3, 4, 5, 6, 7, 8};

Byte[] k1 = {10,11 , 12, 13, 14, 15, 16, 17};

public mukhi()

{

}

public override object GetInitializer(LogicalMethodInfo m, SoapExtensionAttribute a)

{

return a;

}

public override object GetInitializer(Type t)

{

return typeof(mukhi);

}

public override void Initialize(object i)

{

vijay a = (vijay) i;

dmode = a.Dec;

emode = a.Enc;

abc("Initialize " + i.ToString() + " " + a.Dec.ToString() + " " + a.Enc.ToString());

return;

}

public override void ProcessMessage(SoapMessage m)

{

if ( m.Stage == SoapMessageStage.AfterSerialize)

{

abc("AfterSerialize " + emode.ToString() + " " + n.Position );

n.Position = 0;

if ((emode == EM.Request) || (emode == EM.Response))

{

abc("AfterSerialize if");

n.Position = 0;

XmlTextReader rr = new XmlTextReader(n);

XmlDocument d1 = new XmlDocument();

d1.Load(rr);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

no = no.FirstChild.FirstChild;

byte[] inp = Encoding.UTF8.GetBytes(no.InnerText);

MemoryStream ms1 = new MemoryStream();

abc("-------------------" + ctype);

byte[] ou = null;

if (emode == EM.Request)

{

XmlNode no1 = d1.SelectSingleNode("//soap:Header", nm);

no1 = no1.FirstChild.FirstChild;

abc("################" + no1.InnerText);

ctype = no1.InnerText;

}

if ( ctype == "DES")

{

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

CryptoStream cs = new CryptoStream(ms1, de.CreateEncryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

ou = ms1.ToArray();

}

else

{

RC2CryptoServiceProvider de = new RC2CryptoServiceProvider();

CryptoStream cs = new CryptoStream(ms1, de.CreateEncryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

ou = ms1.ToArray();

}

StringBuilder s = new StringBuilder();

for(int i=0; i<ou.Length; i++)

{

if(i==(ou.Length-1))

s.Append(ou[i]);

else

s.Append(ou[i] + " ");

}

no.InnerText = s.ToString();

MemoryStream ms = new MemoryStream();

d1.Save(ms);

ms.Position = 0;

n = ms;

}

TextReader r = new StreamReader(n);

TextWriter w = new StreamWriter(o);

string s5 = r.ReadToEnd();

w.WriteLine(s5);

w.Flush();

}

if ( m.Stage ==  SoapMessageStage.BeforeDeserialize)

{

abc("BeforeDeserialize " + dmode.ToString());

MemoryStream ds = new MemoryStream();

if ((dmode == DM.Request) || (dmode == DM.Response))

{

abc("BeforeDeserialize if");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(ds);

string s6 = r.ReadToEnd();

w.WriteLine(s6);

w.Flush();

ds.Position = 0;

XmlTextReader r0 = new XmlTextReader(ds);

XmlDocument d1 = new XmlDocument();

d1.Load(r0);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

no = no.FirstChild.FirstChild;

string s3 = no.InnerText;

char[] c = {' '};

string[] ss = s3.Split(c);

byte[] b = new byte[ss.Length];

for(int i=0; i<b.Length; i++)

{

b[i] = Byte.Parse(ss[i]);

}

byte[] inp = b;

MemoryStream ms1 = new MemoryStream();

byte[] ou = null;

abc(ctype);

if ( ctype == "DES")

{

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

CryptoStream cs = new CryptoStream(ms1,de.CreateDecryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

ou = ms1.ToArray();

}

else

{

RC2CryptoServiceProvider de = new RC2CryptoServiceProvider();

CryptoStream cs = new CryptoStream(ms1,de.CreateDecryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

ou = ms1.ToArray();

}

string s1 = Encoding.UTF8.GetString(ou);

no.InnerText = s1;

MemoryStream ms = new MemoryStream();

if ( dmode == DM.Request)

d1.Save("c:\\z.txt");

else

d1.Save("c:\\y.txt");

d1.Save(ms);

ms.Position = 0;

TextReader r1 = new StreamReader(ms);

TextWriter w1 = new StreamWriter(n);

w1.WriteLine(r1.ReadToEnd());

w1.Flush();

}

else

{

abc("BeforeDeserialize else");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(n);

w.WriteLine(r.ReadToEnd());

w.Flush();

}

n.Position = 0;

}

}

public override Stream ChainStream( Stream stream )

{

//abc("ChainStream");

o = stream;

n = new MemoryStream();

return n;

}

public void abc(string s)

{

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

}

 

Output

vijay hi

vijay Bye

a.txt

Initialize vijay Response Request

AfterSerialize Request 436

AfterSerialize if

-------------------RC2

################RC2

Initialize vijay Request Response

BeforeDeserialize Request

BeforeDeserialize if

RC2

AfterSerialize Response 364

AfterSerialize if

-------------------RC2

BeforeDeserialize Response

BeforeDeserialize if

RC2

Initialize vijay Response Request

AfterSerialize Request 437

AfterSerialize if

-------------------RC2

################DES

Initialize vijay Request Response

BeforeDeserialize Request

BeforeDeserialize if

DES

AfterSerialize Response 365

AfterSerialize if

-------------------DES

BeforeDeserialize Response

BeforeDeserialize if

DES

 

a.txt

Initialize vijay Response Request

AfterSerialize Request 436

AfterSerialize if

-------------------RC2

################RC2

Initialize vijay Request Response

BeforeDeserialize Request

BeforeDeserialize if

RC2

AfterSerialize Response 364

AfterSerialize if

-------------------RC2

BeforeDeserialize Response

BeforeDeserialize if

RC2

Initialize vijay Response Request

AfterSerialize Request 437

AfterSerialize if

-------------------RC2

################DES

Initialize vijay Request Response

BeforeDeserialize Request

BeforeDeserialize if

DES

AfterSerialize Response 365

AfterSerialize if

-------------------DES

BeforeDeserialize Response

BeforeDeserialize if

DES

 

a.bat

del c:\a.txt

del *.dll

csc /t:library aaa.cs

csc /t:library zzz.cs /r:aaa.dll

 

SOAP request1

<soap:Header>

- <yyy xmlns="http://tempuri.org/">

  <type>RC2</type>

  </yyy>

  </soap:Header>

- <soap:Body>

- <abc xmlns="http://tempuri.org/">

  <a>214 40 209 188 96 8 97 42</a>

  </abc>

  </soap:Body>

 

SOAP response1

<soap:Body>

- <abcResponse xmlns="http://tempuri.org/">

  <abcResult>164 69 219 2 3 8 58 43 175 147 53 64 112 176 15 181</abcResult>

  </abcResponse>

  </soap:Body>

 

SOAP request2

<soap:Header>

- <yyy xmlns="http://tempuri.org/">

  <type>DES</type>

  </yyy>

  </soap:Header>

- <soap:Body>

- <abc xmlns="http://tempuri.org/">

  <a>173 131 120 91 72 185 185 206</a>

  </abc>

  </soap:Body>

 

 

SOAP response2

<soap:Body>

- <abcResponse xmlns="http://tempuri.org/">

  <abcResult>106 62 105 90 124 131 63 128 6 230 32 130 85 3 149 119</abcResult>

  </abcResponse>

  </soap:Body>

 

In the aspx file, two objects named w and w1, of type zzz, are created. While the object is being created, the constructor is furnished with the specific encryption algorithm type, which the object supports. The object w utilizes the RC2 encryption algorithm, while the object w1 employs the DES algorithm.

 

Resultantly, the SOAP payload displays bytes encrypted with the RC2 algorithm in the first case, and with the DES algorithm in the second case. Thereafter, the function abc is called, using both the objects, but with a distinct parameter value.

 

While creating the objects, the a.aspx file calls code from the zzz.cs file. In the earlier program, we had made use of the attributes to supply values, whereas in the present case, this task has been undertaken by the constructor.

 

A new instance of the class yyy is created in the zzz object. The class yyy, which is derived from SoapHeader, has only one member called 'type'. This member, i.e. 'type' is initialized to the encryption method, which is supported by the object in the zzz constructor. This information is then sent across, in the form of a header value.

 

In one of the earlier chapters, we were enlightened about the fact that the headers can be sent across, using the attribute of SoapHeaderAttribute. We implement the same attribute here, and assign it the variable 'b', to stores the contents of the attribute.

 

The SOAP Request displays two messages, wherein, the first request header has the element type as RC2, and the second request header has the element type as DES. Once the header is created and sent across, the AfterSerialize event is called in the dll, which pertains to the 'vijay' attribute. Here, the value of emode is set to Request.

In the dll, contingent to the value displayed in the header, a specific encryption method is used. The ctype variable is declared static in nature, and assigned a value of "RC2" in the first round. This is for the reason that, a static variable is available, as long as the  application is alive. Therefore, even though there may be numerous instances of the 'mukhi' class in memory, there will be only one copy of the ctype variable. Had the variable been an instance variable, it would have lost its previous value, and would have attained a fresh value every time.

 

Once the header packet arrives at the server, the BeforeDeserialize event is triggered-off in the extension dll. This is because, the asmx file contains the attribute of 'vijay'. The process explicated in the previous program, is relevant in this context too, i.e. the data is decrypted before the function abc is called, and the return value is encrypted thereafter. This encrypted packet is then sent back to the client, to be decrypted.

 

It is pertinent to note that there exists no header packet, to notify the client as to which algorithm is to be used for decrypting the packet. This could cause complications during the transfer of data, using headers. The answer to this quagmire is, to use headers both ways. However, we have implemented a distinct method to deal with this exigency, i.e. we ascertain the value contained in the ctype variable. The details are given below.

 

When the packet arrives from the client, the value contained in the emode variable is Request. Thus, the 'if' statement becomes true. To access the headers tag in the SOAP packet, we utilize the SelectSingleNode function from the document class. The node named 'no1' now represents this header node. 

 

Thereafter, using the property FirstChild twice, the node 'no1' is initialized to represent the content, i.e. InnerText, of the type element. The value that is retrieved is RC2. To substantiate our claim, we have written it to the file a.txt, on disk. The ctype variable is initialized to the value contained in the header. Thereafter, the algorithm of RC2 is implemented, for encrypting the data.

 

 

Once the data has been encrypted, the packet is sent across. When the server receives the packet, the BeforeDeserialize event in the dll gets triggered-off. Herein lies the beauty of the static variable. The constructor could be called a million times, but the static variable retains its value as RC2. We could have easily examined the header again (as was done in the client), however, we prefer to use the value of the ctype variable, instead. The packet data is decrypted, using the valid algorithm.

 

Next, the AfterSerialize event is called from the server side. Since the value in the ctype variable has not been altered, there is no requirement for deciphering the value enclosed in the variable. Bear in mind that no header is present at the time, when the packet is being sent over. When the packet reaches the client, the BeforeDeserialize event is triggered. Since no header information has been sent from the server to client, there is no option, but to refer to the value in the ctype variable.

 

When the function abc is called from the second instance of the zzz object, the header element at the client end contains the value of DES. This alters the ctype variable to DES. Thus, the encryption algorithm that is employed here is DES. The whole process is then iterated with the algorithm of DES.

 

Thus, the header field is used only once in the client, basically to determine the value of the type field. We could even have used the headers in the server, and then, tasked the server for sending the header to the client. There are many ways to skin a cat, but we have the flexibility to choose what suits us the best.

 

Compression and Decompression

 

Very often, we encounter certain characters that make repeated appearances in a data sequence. This recurrence leads to dilation in the size of the data packet, thereby, adding to the web traffic. A thought that crosses the mind at this point is, why not pluck out this character and insert some indication instead, which signals that the specific character has been filtered out?

 

 

In a sense, this is the basis for the concept of compression of a data packet and its subsequent transmission. The receiver, who would be clued-up about this procedure, could then decompress the data, prior to processing it.

 

One variant of this approach is, the (Small Messaging System) SMS language, used in mobile communication.  Although it doesn't work exactly on the guidelines specified above, its utilization has gained popular acceptance by users of mobile phones. In SMS, specific characters are simply filtered out from the message.

 

Let us consider the case wherein we wish to filter out the 'space' character from the SOAP Payload. We have specifically chosen the 'space' character, since it is the most commonly recurring character in the entire packet. On eradicating the character from the parameters of the payload, there is a substantial decrease in the size of the payload, thus introducing the concept of compression.

 

At the other end, these spaces are simply inserted back into the positions from where they were removed earlier. To implement this, information relating to the number of spaces that have been removed, and their original positions, has to be retained, to facilitate subsequent restoration of the original data.

 

Here, we present to you a diminutive .cs program. It has nothing to do with SOAP or client server. It merely contains code that strips the spaces from the text, and subsequently, reinserts them into their original positions. You can work in any sub-directory, but we have chosen the ddd subdirectory.

 

a.cs

using System.Text;

public class zzz

{

public static void Main()

{

zzz a = new zzz();

string s = "A B C";

byte [] b,c;

byte [] d;

c = Encoding.ASCII.GetBytes(s);

for ( int kk=0; kk < c.Length ; kk++)

System.Console.Write(c[kk].ToString() + " ");

System.Console.WriteLine();

b = a.compress(c);

for ( int kk=0; kk < b.Length ; kk++)

System.Console.Write(b[kk].ToString() + " ");

System.Console.WriteLine();

d = a.decompress(b);

for ( int kk=0; kk < d.Length ; kk++)

System.Console.Write(d[kk].ToString() + " ");

System.Console.WriteLine();

System.Console.WriteLine(Encoding.ASCII.GetString(d));

}

public byte [] compress(byte [] d)

{

byte [] t = new byte[d.Length];

int i,j=0;

for ( i = 0; i < d.Length ; i++)

{

if ( d[i] == 32)

{

t[j] = d[i + 1] ;

t[j] += 128;

i++;

}

else

t[j] = d[i];

j++;

}

byte [] t1 = new byte[j];

for ( int k = 0 ; k < j ; k++)

t1[k] = t[k];

return t1;

}

public byte [] decompress(byte [] d)

{

byte [] t = new byte[d.Length*2];

int i,j=0;

for ( i = 0; i < d.Length ; i++)

{

if ( d[i] >= 128)

{

t[j] = 32;

j = j + 1;

t[j] = d[i];

t[j] -= 128;

}

else

t[j] = d[i];

j++;

}

byte [] t1 = new byte[j];

for ( int k = 0 ; k < j ; k++)

t1[k] = t[k];

return t1;

}

}

 

>csc a.cs

 

Output

65 32 66 32 67

65 194 195

65 32 66 32 67

A B C

 

The above program, which expounds the concept of compression and decompression, is applicable only to ASCII text. ASCII text is defined as characters that represent numbers from 0 to 128. The characters constituting the SOAP payload are all ASCII based. The program removes the spaces from the text, and subsequently, inserts them back into place.

 

Any number ranging from 0 to 255, occupies one byte, i.e. 8 bits. The number 200 takes up the same amount of space as the number 10.

 

We start with the string "A B C", containing spaces between its characters. Then we create three byte arrays named 'b', 'c' and 'd'.

 

The GetBytes function converts the string 's' into an array of bytes, which is stored in the array 'c'. This array is then displayed, using a 'for' loop. We see the following:

•     65 - the ASCII value of 'A'.

•     32 - the ASCII value of 'space'.

•     66 - the ASCII value of 'B'.

•     32 - the ASCII value of 'space'.

•     67 - the ASCII value of 'C'.

 

This byte array, which has a size of 5, is then passed to the compress function.

 

The compress function, as its name indicates, initiates a reduction in the number of bytes. First, it creates an array 't', which is of the same size as the array 'd'. The array 'd' is passed as a parameter to the function. It contains the original uncompressed string. The Length property of an array returns the size of an array.

 

Using a 'for' loop, each and every member of the byte array 'd' is checked, in order to determine whether it is a 'space' character or not. If it is not, the array member is copied to the freshly created array 't'. The variable 'i' is used as an index to the array 'd', and the variable 'j' is used as an index to the array 't'.

 

If we come across a space in the array, the 'if' statement gets activated. In the 'if' block, we merely move on to the next number in the byte array 'd'. Then, we store this number in the array 't', using 'j' as the index, but only after adding 128 to this number.

 

Thus, when we encounter a 'space' character after the character 'A' has been copied, the next value 66, which is the ASCII value of B, gets picked. The value of 128 is then added to the ASCII value of B, i.e. 66 + 128, resulting in the number 194. This number is then inserted into the array 't'.

 

Thus, the algorithm or rule is quite plain and simple, i.e. if you encounter a 'space' character, add 128 to the next number. This works without any complexity, since each of the numbers 66 and 194, occupy just a single byte. Thus, the spaces are compressed.

 

Then, we copy the array 't' into a new byte array named 't1', whose size is determined by the variable 'j'. This array t1 is then returned and displayed. Thus, whenever we chance upon a number greater than 128, it signifies the presence of a 'space' character, before this particular character.

 

Now, to decompress the data, in the decompress function, an array 't' is created, which is twice the size of the original array. This is to cater for the anticipated increase in the number of bytes. The same process is repeated again, except for the variations made to the 'if' statement. In the 'if' statement, the number is verified, to determine if it is larger than 128. If it is so, the following actions get executed:

•     A space is added to the array t.

•     The index variable is increased by 1.

•     The value 128 is subtracted from the number.

•     Finally the subtracted value is copied into the new array.

 

For instance, when the number 194 is sighted, a space is first inserted into the array. Then, the number 128 is subtracted from 194, to obtain the original number i.e. 66. Thereafter, the number 66 is stored in the new array. Thus, by applying this mechanism, spaces are reinserted into their original positions.

 

We have employed the most elementary method available. However, in real life, the method that is employed, allocates different bits to a character, based upon the frequency of occurrence of the character. Thus, if the character 'A' occurs most often, a smaller number of bits would be assigned to represent it, whereas, if the character 'Z' occurs the least number of times, a larger number of bits maybe allocated to represent it.

 

The next program is an extension of the encryption/decryption program. The client compresses the bytes prior to encrypting them. On the other side, the server decrypts the bytes and then decompresses them.

 

a.aspx

<HTML>

<HEAD>

<script language="c#" runat=server>

public void Page_Load()

{

zzz w = new zzz();

l.InnerHtml = w.abc("A B C");

}

</script>

</HEAD>

<BODY>

<b id="l" runat="server"/>

</HTML>

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

public class zzz

{

[WebMethod]

[vijay(Enc=EM.Response,Dec=DM.Request)]

public string abc( string a)

{

return "D E F" + a;

}

}

 

zzz.cs

using System.Diagnostics;

using System.Xml.Serialization;

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

[System.Web.Services.WebServiceBindingAttribute(Name="HelloWorldSoap", Namespace="http://tempuri.org/")]

public class zzz: System.Web.Services.Protocols.SoapHttpClientProtocol

{

public zzz()

{

this.Url = "http://localhost:8080/a.asmx";

}

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc")]

[vijay(Enc=EM.Request,Dec=DM.Response)]

public string abc(string a)

{

object[] results = this.Invoke("abc", new object[]{a});

return ((string)(results[0]));

}

}

 

aaa.cs

using System;

using System.IO;

using System.Xml;

using System.Text;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Security.Cryptography;

[AttributeUsage(AttributeTargets.Method)]

public class vijay : SoapExtensionAttribute

{

EM emode= EM.None;

DM dmode= DM.None;

public override Type ExtensionType

{

get

{

return typeof(mukhi);

}

}

public override int Priority

{

get

{

return 0;

}

set

{

}

}

public EM Enc

{

get

{

return emode;

}

set

{

emode= value;

}

}

public DM Dec

{

get

{

return dmode;

}

set

{

dmode= value;

}

}

}

public enum DM

{

None,

Response,

Request

}

public enum EM

{

None,

Response,

Request

}

public class mukhi : SoapExtension

{

Stream o;

Stream n;

DM dmode;

EM emode;

Byte[] k = {1, 2, 3, 4, 5, 6, 7, 8};

Byte[] k1 = {10,11 , 12, 13, 14, 15, 16, 17};

public mukhi()

{

abc("Constructor");

}

public override object GetInitializer(LogicalMethodInfo m, SoapExtensionAttribute a)

{

abc("GetInitializer " + m.ToString());

return a;

}

public override object GetInitializer(Type t)

{

abc("GetInitializer 1");

return typeof(mukhi);

}

public override void Initialize(object i)

{

vijay a = (vijay) i;

dmode = a.Dec;

emode = a.Enc;

abc("Initialize " + i.ToString() + " " + a.Dec.ToString() + " " + a.Enc.ToString());

return;

}

public override void ProcessMessage(SoapMessage m)

{

if ( m.Stage == SoapMessageStage.AfterSerialize)

{

abc("AfterSerialize " + emode.ToString() + " " + n.Position );

n.Position = 0;

if ((emode == EM.Request) || (emode == EM.Response))

{

abc("AfterSerialize if");

n.Position = 0;

XmlTextReader rr = new XmlTextReader(n);

XmlDocument d1 = new XmlDocument();

d1.Load(rr);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

no = no.FirstChild.FirstChild;

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

abc(no.InnerText);

byte[] inp1 = Encoding.UTF8.GetBytes(no.InnerText);

byte [] inp = compress(inp1);

MemoryStream ms1 = new MemoryStream();

CryptoStream cs = new CryptoStream(ms1, de.CreateEncryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

byte[] ou = ms1.ToArray();

StringBuilder s = new StringBuilder();

for(int i=0; i<ou.Length; i++)

{

if(i==(ou.Length-1))

s.Append(ou[i]);

else

s.Append(ou[i] + " ");

}

no.InnerText = s.ToString();

MemoryStream ms = new MemoryStream();

d1.Save(ms);

ms.Position = 0;

n = ms;

}

TextReader r = new StreamReader(n);

TextWriter w = new StreamWriter(o);

string s5 = r.ReadToEnd();

abc(s5);

w.WriteLine(s5);

w.Flush();

}

if ( m.Stage ==  SoapMessageStage.BeforeDeserialize)

{

abc("BeforeDeserialize " + dmode.ToString());

MemoryStream ds = new MemoryStream();

if ((dmode == DM.Request) || (dmode == DM.Response))

{

abc("BeforeDeserialize if");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(ds);

string s6 = r.ReadToEnd();

w.WriteLine(s6);

abc(s6);

w.Flush();

abc("Position of ds " + ds.Position);

ds.Position = 0;

XmlTextReader r0 = new XmlTextReader(ds);

XmlDocument d1 = new XmlDocument();

d1.Load(r0);

XmlNamespaceManager nm = new XmlNamespaceManager(d1.NameTable);

nm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

XmlNode no = d1.SelectSingleNode("//soap:Body", nm);

no = no.FirstChild.FirstChild;

DESCryptoServiceProvider de = new DESCryptoServiceProvider();

string s3 = no.InnerText;

abc(s3);

char[] c = {' '};

string[] ss = s3.Split(c);

byte[] b = new byte[ss.Length];

abc(ss.Length.ToString());

for(int i=0; i<b.Length; i++)

{

b[i] = Byte.Parse(ss[i]);

}

byte[] inp = b;

MemoryStream ms1 = new MemoryStream();

CryptoStream cs = new CryptoStream(ms1,de.CreateDecryptor( k, k1 ), CryptoStreamMode.Write);

cs.Write(inp, 0, inp.Length);

cs.FlushFinalBlock();

byte[] ou1 = ms1.ToArray();

byte[] ou = decompress(ou1);

string s1 = Encoding.UTF8.GetString(ou);

no.InnerText = s1;

abc(s1);

MemoryStream ms = new MemoryStream();

if ( dmode == DM.Request)

d1.Save("c:\\z.txt");

else

d1.Save("c:\\y.txt");

d1.Save(ms);

ms.Position = 0;

TextReader r1 = new StreamReader(ms);

TextWriter w1 = new StreamWriter(n);

w1.WriteLine(r1.ReadToEnd());

w1.Flush();

}

else

{

abc("BeforeDeserialize else");

TextReader r = new StreamReader(o);

TextWriter w = new StreamWriter(n);

w.WriteLine(r.ReadToEnd());

w.Flush();

}

n.Position = 0;

}

}

public override Stream ChainStream( Stream stream )

{

abc("ChainStream");

o = stream;

n = new MemoryStream();

return n;

}

public void abc(string s)

{

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

public byte [] compress(byte [] d)

{

byte [] t = new byte[d.Length];

int i,j=0;

for ( i = 0; i < d.Length ; i++)

{

if ( d[i] == 32)

{

t[j] = d[i + 1] ;

t[j] += 128;

i++;

}

else

t[j] = d[i];

j++;

}

byte [] t1 = new byte[j];

for ( int k = 0 ; k < j ; k++)

t1[k] = t[k];

return t1;

}

public byte [] decompress(byte [] d)

{

byte [] t = new byte[d.Length*2];

int i,j=0;

for ( i = 0; i < d.Length ; i++)

{

if ( d[i] >= 128)

{

t[j] = 32;

j = j + 1;

t[j] = d[i];

t[j] -= 128;

}

else

t[j] = d[i];

j++;

}

byte [] t1 = new byte[j];

for ( int k = 0 ; k < j ; k++)

t1[k] = t[k];

return t1;

}

}

 

a.txt

AfterSerialize Request 329

65 32 66 32 67

---------

65 194 195

BeforeDeserialize Request

BeforeDeserialize if

Position of ds 357

8 65 32 66 32 67

A B C

AfterSerialize Response 366

68 32 69 32 70 65 32 66 32 67

---------

68 197 198 65 194 195

BeforeDeserialize Response

BeforeDeserialize if

Position of ds 389

8 68 32 69 32 70 65 32 66 32 67

D E FA B C

 

SOAP request

abc xmlns="http://tempuri.org/">

<a>211 74 179 234 218 161 41 172</a>

 </abc>

SOAP Response

<abcResponse xmlns="http://tempuri.org/">

  <abcResult>30 192 219 33 206 140 243 252</abcResult>

</abcResponse>

 

Output in Browser

D E FA B C

 

a.bat

del c:\a.txt

del *.dll

csc /t:library aaa.cs

csc /t:library zzz.cs /r:aaa.dll

 

In the aspx file, the function abc is called with a string parameter. The only modification made to this file is that, the string now contains spaces. The a.asmx file and the zzz.cs file remain unchanged. However, the return value in the a.asmx file, now has the string "D E F" concatenated with the parameter value of 'a'.

 

There are some modifications made to the aaa.cs file. Before the bytes can be encrypted, the text embodied in the first node, needs to be compressed. So firstly, the bytes in the original text are displayed from the byte array inp1. Then, the compress function is called, which actually compresses the byte array, by uprooting all the spaces.

 

The new byte array is also displayed. The exercise of compression is carried out prior to the event of bytes being sent out to the other side, each time. Thereafter, the data packet is encrypted.

 

In the BeforeDeserialize event, the bytes are first decrypted.  After decryption, this byte array is handed over to the decompress function. The resultant bytes have been displayed at every stage, so that you can visually determine the result of every individual process.

 

An alternative to DES is the Triple DES Encryption, which is a great deal more sophisticated, but works along similar lines. The above method can also be modified, by employing an asymmetrical algorithm, such as, a public key and a private key.

 

A program can be downloaded off the RSA site, and executed to generate two keys, viz. a public key and a private key. A byte stream that is encrypted with a public key, can only be decrypted with the private key, and vice versa. The public key, as the name suggests, is meant for the public. Therefore, it can be unveiled to the whole world. A private key is meant for private use. Hence, as the saying goes, it should not be divulged even to your spouse!

 

The client keeps the private key close to its heart, and distributes the public key to the interested parties. Then, it encrypts the file that is to be sent across, using its private key.  On receiving the packet, the server decrypts it, using the public key of the client. In the reverse procedure, the server uses the public key of the client to encrypt the packet that is to be sent over to the client. It is only the client's sole prerogative to decrypt this packet, since it is in possession of the private key. So, it uses the private key to decrypt it. In this manner, the issue of security is taken care of.

 

Since the public key is made public, it could pose a problem in the sense that, it could fall into wrong hands. So, in order to plug this loophole, the client first encrypts the packet with its private key, followed by one more layer of encryption using the public key of the destination server. Now, only the designated can decrypt it, since it alone holds the private key to the packet. In the next step, the server would use the client's public key to decrypt the data.

 

Along the same lines, while sending the packet back to the client, the server first encrypts it using its private key, and then, encrypts it further, by using the client's public key. On similar lines, when the packet is received, the client first decrypts the data using its private key, followed by further decryption, using the server's public key.

 

This procedure ensures that only the genuine client and server have access to the SOAP payload, since the private keys are not to be shared with anyone. The above methodology is assigned the nomenclature of a 'digital envelope'.

 

Message Digests

 

a.cs

using System.Text;

using System.Security.Cryptography;

public class zzz

{

public static void Main()

{

zzz a = new zzz();

string s = "ABC";

byte [] c;

c = Encoding.ASCII.GetBytes(s);

MD5 m = new MD5CryptoServiceProvider ();

byte[] d  = m.ComputeHash(c);

for ( int i=0; i < d.Length ; i++)

System.Console.Write(d[i].ToString() + " ");

}

}

 

Output

144 47 189 210 177 223 12 79 112 180 165 210 53 37 233 50

 

After creating an object 'a' of type zzz, the string 's' is initialized to the string "ABC". The string is then converted into a byte array and stored in 'c', using the GetBytes function. Now, we use a new wrapper class called MD5CryptoServiceProvider, which is derived from the class MD5. From this class, a function ComputeHash is called, which accepts a byte array, as well as, returns a byte array. The return value is stored in 'd'.

 

The byte array, when displayed, reveals a series of unintelligible numbers. The output is depicted above. Now, change the string value to "AB". On doing so, there will be a drastic change in the output, which is also reflected below.

 

Output

184 111 198 176 81 246 61 115 222 38 45 76 52 227 160 169

 

Even a slight mutation in a single character, results in an altogether different set of values.

 

A Message Digest is a series of numbers that represents some data. The algorithm used above is called MD5. In this method, a unique number is generated to represent the document. Thus, throughout time and space, the string "AB" would be represented by the above Message Digest number.

 

The document is sent over by the client, subsequent to its Message Digest number being calculated. At the server end, on receipt of the message, the Message Digest Number is recalculated. In the eventuality of the numbers not matching, it is safe to conclude that, the document has been tampered with. We leave it as an exercise for you to implement a SOAP extension that uses MD5.

 

a.cs

using System.Text;

using System.Security.Cryptography;

public class zzz

{

public static void Main()

{

zzz a = new zzz();

string s = "AB";

byte [] c;

c = Encoding.ASCII.GetBytes(s);

byte [] k ;

k = new byte[8]{1,2,3,4,5,6,7,8};

HMACSHA1 h = new HMACSHA1(k);

byte [] d = h.ComputeHash(c);

for ( int i=0; i < d.Length ; i++)

System.Console.Write(d[i].ToString() + " ");

System.Console.WriteLine();

k = new byte[8]{1,2,3,4,5,6,7,9};

h = new HMACSHA1(k);

d = h.ComputeHash(c);

for ( int i=0; i < d.Length ; i++)

System.Console.Write (d[i].ToString() + " ");

}

}

 

Output

132 103 229 233 222 51 190 195 96 116 213 28 182 253 222 57 229 117 180 130

224 211 185 221 74 124 62 203 251 104 96 86 145 107 59 57 200 167 183 90

 

Though the Message Digests algorithm and the checksum methodology appear to be secure, they are not completely reliable.

 

Every TCP/IP packet has two checksums; one is for TCP, while the other is for IP. Just anyone can change a byte of the SOAP payload, and simultaneously, recalculate the TCP/IP checksums. This change could go undetected. So, checksums are not very dependable, as they are susceptible to manipulation. The same is true for the Message Digest computation.

 

It would be a sagacious move to encrypt the SOAP payload, and then, recalculate the new MD5 hash value. Over and above this, it would make sound sense to encrypt the MD5 hash also, before transmitting the packet across, or else, the hash loses its sanctity.

 

In the above program, we have a class called HMACSHA1, where HMAC is an acronym for "Hash based Message Authentication Code", and SHA1 represents the hashing algorithm. This class calculates a hash value and uses a key or password, which is passed to the constructor.

 

The hash value that gets generated at this stage, not only depends upon the input data, but also on the key. In the next round, we keep the input byte array the same, with the exception of the last byte of the key. It is changed to 9 from 8. As a result of this modification, the entire hash changes dramatically.

Thus, it is advisable to use the HMACSHA1 class, because when the hash value is sent across, only those with the password can compute or recompute the hash value.

 

Another problem with symmetrical encryption algorithms is that of 'key management'. How can we transfer the key to the other side, in a secure manner? One possibility is to implement the public key - private key logic. But then, the algorithm slows down the processing. Digital signatures are based on Message Digest algorithms and Public keys/Private keys.