![]()
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 contents. 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