Abstract
cCryptoTcpTokenizer
builds upon .NET framework, extends what�s offered by System.Security.Cryptography
and provides a mechanism that facilitates secured, asynchronous communication between two IP end points. cCryptoTcpTokenizer
supports:
- Message encryption/decryption: Messages are encrypted using Rijndael algorithm. Secret key and initialization vector used in payload encryption are, in turn, encrypted using recipient�s public key. Encrypted secret key and initialization vector are sent to recipient along with the encrypted message.
cCryptoTcpTokenizer
manages key exchanges internally and the whole process is transparent to client.
- Message integrity and signing.
- Parsing/tokenizing of incoming network stream.
- Events to support asynchronous messaging:
- EvIncomingMessage Incoming message detected.
- EvTargetPublicKeyRetrieved Recipient�s public key retrieved.
- EvConnectBack Connect back to target established.
Contents
- Application Notes
- Architecture
- Scalability and performance
- Further extension in subsequent releases
- References
- Appendix
Application Notes
cCryptoTcpTokenizer package info
- executable: clCryptoTcpTokenizer.dll
- solution: clCryptoTcpTokenizer (clCryptoTcpTokenizer.sln)
- Solution Directory: cCryptoTcpTokenizer_package\clCryptoTcpTokenizer\
- source: CryptoTcpTokenizer.cs
- demo source: CryptoTcpTokenizerDemo.cs
- demo executable: CryptoTcpTokenizerDemo.exe (C# console application)
- VERSION: 1.0.1355.23652 (BETA)
Referenced assemblies
Class |
DLL |
Supports |
CTcpTokenizer
Source: TcpTokenizer.cs
Namespace: nsTcpTokenizer |
TcpTokenizer.dll |
Supports sending/receiving/tokenizing messages TO/FROM network stream. |
CRSAWrap
Source: RSAWrap.cs
Namespace: nsRSAWrap |
clRSAWrap.dll |
Multi-block RSA encryption/decryption subroutines. |
Table 1 Referenced Assemblies
Other packages included:
a. cTcpTokenizer
- executable: TcpTokenizer.dll
- solution: TcpTokenizer (TcpTokenizer.sln)
- Solution Directory: cCryptoTcpTokenizer_package\ TcpTokenizer\
- source: TcpTokenizer.cs
- demo source: TcpTokenizerDemo.cs
- demo executable: TcpTokenizerDemo.exe
- performance test: cCryptoTcpTokenizerPerformanceTest.cs (Project: cCryptoTcpTokenizerPerformanceTest)
b. clRSAWrap
- solution: clRSAWrap (RSACryptoServiceProvider Demo.sln)
- Solution Directory: cCryptoTcpTokenizer_package\ RSACryptoServiceProvider Demo\
- Info: This solution is a collection of projects demonstrating basics of .NET System.Cryptography namespace: Hash, digital signature, RSA/asymmetric encryption. Included is a simple wrapper around RSACrptoServiceProvider: class cRSAWrap. The class provides two static methods to simplify multi-block RSA encryption/decryption.
Demonstration (1.2) (Source: CryptoTcpTokenizerDemo.cs)
The following code fragment below demonstrates the how to connect two IP endpoints using class cCryptoTcpTokenizer
. If you�re more comfortable with UML sequence diagrams then code, skip ahead to the Walkthrough section and read that section first.
using System;
using System.Text;
using System.Threading;
using System.Collections;
using nsCryptoTcpTokenizer;
namespace CryptoTcpTokenizerDemo
{
public class cCryptoTcpTokenizerDemo
{
public bool bContinue;
public cCryptoTcpTokenizer tkMessenger1;
public cCryptoTcpTokenizer tkMessenger2;
public ManualResetEvent evMsg1SendComplete;
public ManualResetEvent evMsg2SendComplete;
public ManualResetEvent evMsg1PKRetrieved;
public ManualResetEvent evMsg2PKRetrieved;
public ManualResetEvent evConnectBackTo2;
public cCryptoTcpTokenizerDemo()
{
bContinue=true;
evMsg2SendComplete = new ManualResetEvent(false);
evMsg1PKRetrieved = new ManualResetEvent(false);
evMsg2PKRetrieved = new ManualResetEvent(false);
evConnectBackTo2 = new ManualResetEvent(false);
evMsg1SendComplete = new ManualResetEvent(false);
}
void OnConnectBackTo2()
{
evConnectBackTo2.Set();
}
void OnIncomingMsg1()
{
object oMsg = tkMessenger1.qIncomingMessages.Dequeue();
string sMsg = oMsg.ToString();
Console.WriteLine("tkMessenger1: {0}", sMsg);
}
void OnIncomingMsg2()
{
object oMsg = tkMessenger2.qIncomingMessages.Dequeue();
string sMsg = oMsg.ToString();
Console.WriteLine("tkMessenger2: {0}", sMsg);
}
void OnMsg1PKRetrieved()
{
Console.WriteLine("tkMessenger1 just retrieved public" +
" key from target (tkMessenger2).");
evMsg1PKRetrieved.Set();
}
void OnMsg2PKRetrieved()
{
Console.WriteLine("tkMessenger2 just retrieved " +
"public key from target (tkMessenger1).");
evMsg2PKRetrieved.Set();
}
public void Messenger1()
{
int index=0;
try
{
tkMessenger1 = new cCryptoTcpTokenizer();
tkMessenger1.listenPort = 11000;
tkMessenger1.targetIP="localhost";
tkMessenger1.targetPort=12000;
cCryptoTcpTokenizer.dgIncomingMessage dgIncoming1 =
new cCryptoTcpTokenizer.dgIncomingMessage(OnIncomingMsg1);
tkMessenger1.evIncomingMessage += dgIncoming1;
cCryptoTcpTokenizer.dgTargetPublicKeyRetrieved dgMsg1PKRetrieved =
new cCryptoTcpTokenizer.dgTargetPublicKeyRetrieved(
OnMsg1PKRetrieved);
tkMessenger1.evTargetPublicKeyRetrieved += dgMsg1PKRetrieved;
cCryptoTcpTokenizer.dgConnectBack dgConnectBackTo2 =
new cCryptoTcpTokenizer.dgConnectBack(OnConnectBackTo2);
tkMessenger1.evConnectBack += dgConnectBackTo2;
tkMessenger1.StartListening();
evConnectBackTo2.WaitOne();
tkMessenger1.AsynRequestTargetPublicKey();
evMsg2SendComplete.WaitOne();
evMsg1PKRetrieved.WaitOne();
Console.WriteLine("tkMessenger1: sending 10" +
" messages to tkMessenger2.");
tkMessenger2.bSendEncrypted=true;
for(index=0; index<10; index++)
{
tkMessenger1.SendMessage("Message index[" +
index.ToString() + "] (encrypted)" );
}
evMsg1SendComplete.Set();
while(bContinue==true)
{
}
tkMessenger1.StopListening();
Console.WriteLine("Messenger1 thread returning.");
}
catch(Exception err)
{
Console.WriteLine(err.Message);
}
return;
}
public void Messenger2()
{
int index=0;
try
{
tkMessenger2 = new cCryptoTcpTokenizer();
tkMessenger2.listenPort = 12000;
tkMessenger2.targetIP="localhost";
tkMessenger2.targetPort=11000;
cCryptoTcpTokenizer.dgIncomingMessage dgIncoming2 =
new cCryptoTcpTokenizer.dgIncomingMessage(OnIncomingMsg2);
tkMessenger2.evIncomingMessage += dgIncoming2;
cCryptoTcpTokenizer.dgTargetPublicKeyRetrieved
dgMsg2PKRetrieved =
new cCryptoTcpTokenizer.dgTargetPublicKeyRetrieved(
OnMsg2PKRetrieved);
tkMessenger2.evTargetPublicKeyRetrieved += dgMsg2PKRetrieved;
tkMessenger2.StartListening();
tkMessenger2.ConnectTarget();
tkMessenger2.AsynRequestTargetPublicKey();
evMsg2PKRetrieved.WaitOne();
tkMessenger2.bSendEncrypted=false;
Console.WriteLine("tkMessenger2: sending " +
"10 messages to tkMessenger1.");
for(index=0; index<5; index++)
{
tkMessenger2.SendMessage("Message index[" +
index.ToString() + "] (plaintext)" );
}
index=0;
tkMessenger2.bSendEncrypted=true;
for(index=0; index<5; index++)
{
tkMessenger2.SendMessage("Message index[" +
index.ToString() + "] (encrypted)" );
}
evMsg2SendComplete.Set();
evMsg1SendComplete.WaitOne();
while(bContinue==true)
{
}
tkMessenger2.StopListening();
Console.WriteLine("Messenger2 thread returning.");
}
catch(Exception err)
{
Console.WriteLine(err.Message);
}
return;
}
}
class cMain
{
[STAThread]
static void Main(string[] args)
{
cCryptoTcpTokenizerDemo demo = new cCryptoTcpTokenizerDemo();
Thread tTom = new Thread( new ThreadStart(demo.Messenger1));
tTom.Name="Tom";
Thread tJerry = new Thread( new ThreadStart(demo.Messenger2));
tJerry.Name="Jerry";
tTom.Start();
Thread.Sleep(100);
tJerry.Start();
Thread.Sleep(180000);
demo.bContinue=false;
return;
}
}
}
Listing 1
Walkthrough (1.3)
The sample in Section 1.2 consists of three blocks:
- Events handlers for events supported by
cCryptoTcpTokenizer
.
- Two thread functions: Messenger1( ) and Messenger2( )
Main(..)
� The demo is a C# console application. So, start tracing the demo from here.
public class cCryptoTcpTokenizerDemo
{
public bool bContinue;
public cCryptoTcpTokenizer tkMessenger1;
public cCryptoTcpTokenizer tkMessenger2;
public ManualResetEvent evMsg1SendComplete;
public ManualResetEvent evMsg2SendComplete;
public ManualResetEvent evMsg1PKRetrieved;
public ManualResetEvent evMsg2PKRetrieved;
public ManualResetEvent evConnectBackTo2;
public cCryptoTcpTokenizerDemo()
{
... code ...
}
void OnConnectBackTo2()
{
... code ...
}
void OnIncomingMsg1()
{
... code ...
}
void OnIncomingMsg2()
{
... code ...
}
void OnMsg1PKRetrieved()
{
... code ...
}
void OnMsg2PKRetrieved()
{
... code ...
}
public void Messenger1()
{
... code ...
}
public void Messenger2()
{
... code ...
}
}
class cMain
{
[STAThread]
static void Main(string[] args)
{
}
}
}
Listing 2
Main(...)
First, two threads are launched. Thread functions Messenger1( )
and Messenger2( )
instantiate two instances of cCryptoTcpTokenizer
(tkMessenger1
and tkMessenger2
), configures them to point at each other and associates message handlers with events exposed by cCryptoTcpTokenizer
.
What happens after this is a linear sequence of events:
- tkMessenger2 (Messenger2�s thread) connects to tkMessenger1
- tkMessenger1 responds by connecting back to tkMessenger2
When this happens, cCryptoTcpTokenizer.evConnectBack
event is raised. tkMessenger1 is now ready to issue request for tkMessenger2�s public key (You can�t issue a key request to target until a connection has been established).
- tkMessenger2 dispatches a request for tkMessenger1�s public key.
- tkMessenger1 dispatches a request for tkMessenger2�s public key.
- tkMessenger2 waits until tkMessenger1�s key is retrieved.
When that happens, event cCryptoTcpTokenizer.dgTargetPublicKeyRetrieved
is raised and corresponding handler OnMsg2PKRetrieved
is invoked.
- tkMessenger1 waits until tkMessenger2�s key is retrieved.
- tkMessenger2 sends messages to tkMessenger1.
- tkMessenger1 sends messages to tkMessenger2.
- Threads Messenger1 and Messenger2 are kept alive for as long as
bContinue==true
: while(bContinue==true)
{
}
During this time, tkMessenger1 and tkMessenger2 continue parsing from �incoming� network stream connecting the two instances of cCryptoTcpTokenizer. Each instance of cCryptoTcpTokenizer
encapsulates two instances of TcpTokenizer:
cCryptoTcpTokenizer.m_sender
Responsible for sending data to target.
cCryptoTcpTokenizer.m_receiver
Responsible for receiving data from target.
Keep in mind that:
m_receiver
of tkMessenger1 is connected to m_sender of tkMessenger2.
m_sender
of tkMessenger1 is connected to m_receiver of tkMessenger2.
Until StopListening( )
is invoked, tkMessenger1 will continue to retrieve data, if available, from network stream connecting tkMessenger1.m_receiver
and tkMessenger2.m_sender
, and to load its incoming message queue - tkMessenger1.qIncomingMessages
(type: �System.Queue
�) with new messages. Every time a new message arrives, cCrytoTcpTokenizer.evIncomingMessage
event is raised. Handler (OnIncomingMsg1
) for the event is invoked. The incoming message is retrieved from tkMessenger1�s message queue (tkMessenger1.qIncomingMessages
) and is then displayed to screen.
Main(..)
toggles bContinue
to false
after 180 seconds elapsed. StopListening( )
is then invoked. tkMessenger1/tkMessenger2 disconnects from the network stream and stops parsing/reading from the network stream.
Fig 1. Communication between two instances of cCryptoTcpTokenizer
(CryptoTcpTokenizerDemo.cs):
Architecture
- Configuration and Connections
- Event Subscriptions
- Key Exchanges
- Sending and Receiving Messages
Configuration and Connections
First an instance of cCryptoTcpTokenizer
must configure the port on which it listens on:
- Set
cCryptoTcpTokenizer.listenport
.
- Call
cCryptoTcpTokenizer.StartListening( )
to starts listening in for incoming connection requests, key requests and messages.
Connects to target:
- Set
cCryptoTcpTokenizer.targetPort
and cCryptoTcpTokenizer.targetIP
- Call
cCryptoTcpTokenizer.ConnectTarget( )
When one instance initiates connection to a remote instance, the remote instance responds by:
- Accepting the connection
- Internally calls
ConnectBackToClient( )
, which in turn calls ConnectTarget( ).
Event evConnectBack
is raised upon return of the method.
Event Subscriptions
Three events are available for client subscription:
Event |
When |
evIncomingMessage |
Fired when incoming message has been processed. To retrieve the incoming message, pops off �message� from the �message queue� cCryptoTcpTokenizer.qIncomingMessages after this event is fired. �Message Queue� QIncomingMessages is of type �System.Queue �. �Messages� in the queue are of type �System.Queue �. Individual �tokens� in these �messages� are of type �System.object �. |
evTargetPublicKeyRetrieved |
Fired when target�s public key has been retrieved. Don�t try sending message encrypted before this event is raised. Once target�s public key is retrievd, message can be sent encrypted as follows:
- Toggle
bSendEncrypted to true.
- Call
Send( ) |
EvConnectBack |
Fired when one instance connects back to a remote instance. |
Table 2 cCryptoTcpTokenizer events
To subscribe to cCryptoTcpTokenizer
events:
cCryptoTcpTokenizer.dgIncomingMessage dgIncoming1 =
new cCryptoTcpTokenizer.dgIncomingMessage(OnIncomingMsg1);
tkMessenger1.evIncomingMessage += dgIncoming1;
Key Exchanges
Key requests are dispatched to client very much the same way regular messages are sent. Here�s a description of the key exchange process:
Requesting for target�s public key:
- Make sure connection is established between this instance of
cCryptoTcpTokenizer
(Server A) and the remote instance (Server B). Check bIsConnected
.
- Server A calls
AsynRequestTargetPublicKey
( ).
Internally, the method loads m_sender.bufferQueue
with key request and send it to remote instance (Server B): string sREQ = "REQ_RSA_PUBKEY";
m_sender.bufferQueue.Enqueue(sREQ);
m_sender.Send();
- Remote instance (Server B) receives this message. Internally, m_receiver of remote instance (Server B) raises an
cTcpTokenizer
event �evbufferLoaded
� which triggers ProcessIncomingMsg( ).
The method examines m_receiver.bufferQueueCollection, which now contains the key request �REQ_RSA_PUBKEY
�. The message is identified as a key request, and is therefore routed to DispatchRsaPK( ).
DispatchRsaPK( )
is responsible for sending its public key to the target very much the same way it receives the key request. Here�s how:m_sender.bufferQueue.Enqueue("REQ_RSA_PUBKEY_RESULT");
m_sender.bufferQueue.Enqueue(m_rsaSelfPublicKey);
m_sender.bufferQueue.Enqueue(btHash);
m_sender.Send();
- The response is received by Server A. As with all other kinds of message, �
REQ_RSA_PUBKEY_RESULT
� messages are first retrieved by ProcessIncomingMsg( ).
The message type is identified, and execution is then routed to SetRecipientRsaPK( ).
SetRecipientRsaPK( )
then stores target�s (Server B) public key in protected field: m_rsaRecipientPublicKey. m_bIsRecipientPKRetrieved
is toggled to true internally.
- Server A is now ready to send message to Server B in encrypted mode. Toggle
cCryptoTcpTokenizer.bSendEncrypted
to true before calling Send( )
method. For server B to send encrypted message, server B must issue key requests to server A.
Sending and Receiving Messages
The following section describes the internal process of sending and receiving messages with cCryptoTcpTokenizer
.
A single �message� (type: �System.Queue
�) is composed of multiple �tokens� (type: �System.Object
�). The first token is �message label�. Subsequent tokens depends on which whether Server A is sending encrypted message or plaintext.
Memory map of a single encrypted �message� resembles the following:
Fig 2 Memory map: encrypted messages
SendMessageEncrypted( )
first encrypts the plaintext message using symmetric algorithm RijndaelManaged - symmetric key algorithms are a thousand times faster than asymmetric algorithms. The secret key and initialization vector are encrypted using recipient�s public key. Hash of the encrypted message is encrypted with recipient�s public key � and therefore, can only be decrypted by recipient�s public key.
Here�s how tokens are queued and dispatched inside SendMessageEncrypted( )
m_sender.bufferQueue.Enqueue("ENCRYPTED_PAYLOAD");
m_sender.bufferQueue.Enqueue(btEncryptedRMKey);
m_sender.bufferQueue.Enqueue(btEncryptedRMIV);
m_sender.bufferQueue.Enqueue(btEncryptedHash);
m_sender.bufferQueue.Enqueue(btEncryptedMessage);
m_sender.Send();
Memory map of a single plaintext message resembles the following:
Fig 3 Memory map: plaintext messages
- Server B receives the message.
m_receiver
raises event �evbufferLoaded�, which triggers cCryptoTcpTokenizer.ProcessIncomingMsg( ).
cCryptoTcpTokenizer.ProcessIncomingMsg( )
first categorizes the message by examining message label. A message can be of the following message types:
Message Type |
Description |
Handler |
REQ_RSA_PUBKEY |
Request for public key |
DispatchRsaPK() |
REQ_RSA_PUBKEY_RESULT |
Response to a request for public key |
SetRecipientRsaPK() |
ENCRYPTED_PAYLOAD |
Message (Encrypted) |
ProcessEncryptedPayload() |
PLAINTEXT_PAYLOAD |
Message (plaintext) |
ProcessPlaintext() |
Table 3 Message Types
ProcessIncomingMsg
routes execution to appropriate handlers depending on �Message Label�.
ProcessEncryptedPayload
and ProcessPlaintext -
ProcessPlaintext
simply deposits the message in cCryptoTcpTokenizer.qIncomingMessages
for later retrieval. The method will then fire event evIncomingMessage to alert/notify any subscriber.
ProcessEncryptedPayload
first decrypts the following tokens using its own private key, RSA algorithm and cRSAWrap
:
- secret key (RijndaelManaged)
- initialization vectors (RijndaelManaged)
- hash on encrypted message (SHA1)
Message integrity is confirmed by comparing the hash received and the hash computed on the received encrypted message. The payload can then be decrypted using RijndaelManaged algorithm and the secret key/initialization vectors decrypted earlier.
Fig 4. The cCryptoTcpTokenizer
class
Scalability and performance
Thread safety (3.1)
cCryptoTcpTokenizer.qIncomingMessages
is Thread-Safe.
Performance test (3.2)
- Test method: Send a number of messages and measure the difference between the time when the first message is sent and the time the last message is received. Both IP end points reside on the same physical machine.
- Setup: Toshiba Satellite 2400 1694MHz 256MB
- Source code: cCryptoTcpTokenizerPerformanceTest.cs
- Project directory: \cCryptoTcpTokenizer_package \clCryptoTcpTokenizer\ cCryptoTcpTokenizerPerformanceTest\
Plaintext messages:
Table 4 Time elapsed between first message (plaintext) sent and last message received (unit: sec)
Encrypted messages:
Table 5 Time elapsed between first message (encrypted) sent and last message received (unit: sec)
Chart 1
Chart 2
Chart 3
Time elapsed between first message sent and last message received (unit: sec; Total 10 messages sent.)
Message Size |
Plaintext |
Encrypted |
Performance
Ratio |
10 |
3.81 |
91 |
23.8 |
50 |
3.92 |
103 |
26.2 |
500 |
3.95 |
95 |
24.0 |
1 kB |
3.98 |
98 |
24.6 |
10 kB |
2.5 |
97 |
38.8 |
100 kB |
32 |
123 |
3.84 |
1 MB |
353 |
465 |
1.32 |
Table 6 Performance Comparison: Plaintext Vs Encrypted Messages.
Table 7
Observations:
- For smaller messages (size < 1 kB), processing time for encrypted messages is roughly 24 times slower than that for plaintext messages. The difference in processing time between encrypted messages and plaintext is much less pronounced for larger messages. (Table 6)
- It will take approximately 100 sec to encrypt and wire 10 JPEG�s (100kB each) to a specified target. The same process takes approximately 32 sec to complete if the images are not encrypted. (Table 4, Table 5, Table 7)
- For smaller messages (< 100 kB), message size has little impact on processing time. For larger messages, processing time and message size exhibits a linear correlation. (Chart 1, Chart 2, Chart 3).
Further extension in subsequent releases
CRSAWrap
:
cRSAWrap.EncryptBuffer( )
and cRSAWrap.DecryptBuffer( ):
RSACryptoServiceProvider
should be instantiated on a per connection/session basis.
CTcpTokenizer
(SECURITY WARNING):
cCryptoTcpTokenizer
�messages� are encapsulated in cTcpTokenizer
�messages�. But cTcpTokenizer
messages are susceptible to DOS attacks because no mechanism is in place to guard against incorrect/corrupted block sizes specified BLOCK 1 and BLOCK 2 (Appendix 2 Fig 5 Memory map: cTcpTokenizer
messages). For example, �data buffer size� in BLOCK 2 specifies size of �data buffers� in BLOCK 3. Attacker can overwrites �data buffer size� token in BLOCK 2 to a number larger/smaller than the actual size of the corresponding buffer in BLOCK 3. This would cause problem in subsequent parsing and shut down the listening thread. One way to do this is to encrypt hash of cTcpTokenizer
message before sending it.
cCryptoTcpTokenizer
:
- Current implementation does not support multicasting/broadcasting.
- No mechanism in place to acknowledge receipt of message.
- Message signing should be made available to plaintext messaging. For encrypted messages, message signing should be available as an optional parameter.
- Additional overload for
Send
method:
Send(string sMessage)
Send(byte[] btMessage)
Quality issues
- More tests are required to fully evaluate performance characteristics of
cCryptoTcpTokenizer
.
- Security review pending.
Appendix:
- Common Cryptographic Exceptions
- cTcpTokenizer
- cRSAWrap
- Disclaimer
Appendix 1: Common Cryptographic Exceptions
MSDN provides pretty good documentation on System.Security.Cryptography
namespace. The documentation can be located under subject: �Cryptographic Tasks�, �Cryptography Overview�. There�re a few issues that are not adequately elaborated:
- Common cryptographic exception: Bad Key, Bad Data, Bad Length
- Multi-block encryption in RSA encryption/decryption (Please refer to Appendix 3 cRSAWrap)
In this section, we�ll provide a brief discussion of common cryptographic exceptions.
- Solution Name: RSACryptoServiceProvider Demo
- Solution Directory: \cCryptoTcpTokenizer_package\RSACryptoServiceProvider Demo\
- Project Name: SimplestRSADemo
- Source code: SimplestRSADemo.cs
Demonstration:
using System;
using System.Text;
using System.Security.Cryptography;
namespace tryDecrypt
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
int keySize=0;
int blockSize=0;
byte[] buffer;
byte[] encryptedbuffer;
byte[] decryptedbuffer;
string decryptedstring;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters completeParams =
rsa.ExportParameters(true);
RSAParameters publicParams =
rsa.ExportParameters(false);
string plainText = "00000000011111111111111122222222222222222" +
"3333333333333333333000000000000000000111111111111111" +
"222222222222222223333333";
Console.WriteLine("plainText to be encrypted: {0}", plainText);
Console.WriteLine("plainText.Length: {0}",
Convert.ToString(plainText.Length));
RSACryptoServiceProvider rsaSender = new RSACryptoServiceProvider();
rsaSender.ImportParameters(publicParams);
keySize = rsaSender.KeySize/8;
blockSize = keySize -11;
Console.WriteLine("keySize = {0}; blockSize = {1}",
Convert.ToString(keySize),
Convert.ToString(blockSize));
buffer = Encoding.ASCII.GetBytes(plainText);
encryptedbuffer = rsaSender.Encrypt(buffer, false);
Console.WriteLine("Encrypted stuff: {0}",
Encoding.ASCII.GetString(encryptedbuffer) );
rsaSender.Clear();
rsaSender= null;
RSACryptoServiceProvider rsaReceiver =
new RSACryptoServiceProvider();
rsaReceiver.ImportParameters(completeParams);
decryptedbuffer = rsaReceiver.Decrypt(encryptedbuffer, false);
decryptedstring = Encoding.ASCII.GetString(decryptedbuffer);
Console.WriteLine("Decrypted stuff: {0}", decryptedstring);
Console.WriteLine("Lenght of decrypted stuff: {0}",
Convert.ToString(decryptedstring.Length));
Console.ReadLine();
}
}
}
Appendix 2: cTcpTokenizer
Design objectives:
- Facilitate parsing/tokenizing of data retrieved from
NetworkStream
.
- Simplify the process of sending messages composed of multiple tokens to target IP address.
- Thread-Safe operation.
- Solution Name: TcpTokenizer
- Solution Directory: \cCryptoTcpTokenizer_package\TcpTokenizer\
- Project Name: TcpTokenizer
- Source code: TcpTokenizer.cs
- Demo Project Name: TcpTokenizerDemo
- Demo Source code: TcpTokenizerDemo.cs
Demonstration
using System;
using System.Text;
using System.Threading;
using System.Collections;
using nsTcpTokenizer;
namespace TcpTokenizerDemo
{
public class ClassDemo
{
public cTcpTokenizer sender;
public cTcpTokenizer receiver;
public bool bContinue;
public ClassDemo()
{
bContinue=true;
}
public void StartSending()
{
Console.WriteLine("StartSending invoked.");
try
{
string [] payload1 = new string []
{"AAA", "BBBB", "CCCCC", "DDDDDD", "EEEEEEE"};
string [] payload2 = new string []
{"T2kSN", "yfh@", ".:sX2", "AA*3@$", "@@B"};
string sPayload3 = "AABBCCDD";
byte[] btPayload3 = Encoding.ASCII.GetBytes(sPayload3);
sender = new cTcpTokenizer();
sender.bMode = true;
sender.targetIP = "localhost";
sender.targetPort = 11000;
sender.ConnectTarget();
for(int i=0; i<5; i++)
{
sender.bufferQueue.Enqueue(payload2[i]);
}
for(int i=0; i<3; i++)
{
sender.bufferQueue.Enqueue(payload1[i]);
}
sender.Send();
for(int i=0; i<3; i++)
{
sender.bufferQueue.Enqueue(payload1[i]);
}
sender.Send();
sender.DisconnectTarget();
sender.targetIP = "localhost";
sender.targetPort = 11000;
sender.ConnectTarget();
for(int i=0; i<5; i++)
{
sender.bufferQueue.Enqueue(payload2[i]);
}
sender.Send();
sender.bufferQueue.Enqueue(btPayload3);
sender.Send();
sender.DisconnectTarget();
}
catch(Exception err)
{
Console.WriteLine("Sender side: error detected.");
Console.WriteLine(err.ToString());
}
finally
{
Console.WriteLine("StartSending return.");
}
return;
}
public void OnIncomingConnectionAccepted()
{
Console.WriteLine("Incoming connection accepted.");
}
public void OnBufferLoaded()
{
byte[] btToken;
string sToken;
int index=0;
Console.WriteLine("Incoming message:");
Queue qMsg = (Queue) receiver.bufferQueueCollection.Dequeue();
index=0;
foreach(object oToken in qMsg)
{
if(oToken.GetType().ToString().Equals("System.String"))
{
Console.WriteLine("Token[{0}]: {1}", index.ToString(),
oToken.ToString());
}
else if(oToken.GetType().ToString().Equals("System.Byte[]"))
{
btToken = (byte[]) oToken;
sToken = Encoding.ASCII.GetString(btToken);
Console.WriteLine("Token[{0}]: {1}", index.ToString(),
sToken);
}
index++;
}
Console.WriteLine();
return;
}
public void StartListening()
{
Console.WriteLine("StartListening invoked.");
try
{
receiver = new cTcpTokenizer();
receiver.bMode = false;
receiver.listenPort=11000;
receiver.polltime = 500;
cTcpTokenizer.dgIncomingConnectionAccepted d1 = new
cTcpTokenizer.dgIncomingConnectionAccepted(
OnIncomingConnectionAccepted);
receiver.evIncomingConnectionAccepted += d1;
cTcpTokenizer.dgbufferLoaded d3 =
new cTcpTokenizer.dgbufferLoaded(OnBufferLoaded);
receiver.evbufferLoaded += d3;
receiver.StartListening();
Thread.Sleep(3000);
receiver.DisconnectCurrentClient();
while(bContinue==true)
{
}
receiver.StopListening();
}
catch(Exception err)
{
Console.WriteLine("Receiver side: error detected.");
}
finally
{
Console.WriteLine("StartListening return.");
}
return;
}
}
public class ClassMain
{
[STAThread]
static void Main(string[] args)
{
ClassDemo demo = new ClassDemo();
Thread receiverThread = new Thread(
new ThreadStart(demo.StartListening));
Thread senderThread =
new Thread( new ThreadStart(demo.StartSending));
receiverThread.Start();
Thread.Sleep(100);
senderThread.Start();
Thread.Sleep(150000);
demo.bContinue=false;
}
}
}
Fig 5. Memory map: cTcpTokenizer
messages.
Fig 6. cTcpTokenizer
class
Appendix 3: cRSAWrap
- Design objectives: Simplify multi-block encryption/decryption (RSA) � It�s basically a wrapper around RSACryptoServiceProvider.
- Solution Name: RSACryptoServiceProvider Demo
- Solution Directory: \cCryptoTcpTokenizer_package\RSACryptoServiceProvider Demo\
- Project Name: SimplestRSADemo
- Source code: SimplestRSADemo.cs
- Required namespace: System; System.Text; System.Security.Cryptography; nsRSAWrap;
Demonstration
string secret = "At a beach resort best known for turquoise surf "
+ "and drunken U.S. college students, trade ministers huddled" +
"in conference rooms of five-star hotels in preparation " +
"for the meeting, which begins Wednesday. Away from the " +
"hotel zone, thousands of anti-globalization activists from " +
"around the world set up camp, renting hammocks and swatting " +
"mosquitoes, and vowed to derail the meetings with protests " +
"and marches, as they did in Seattle in 1999. Agriculture will " +
"likely be at the top of the Cancun agenda. Removing barriers " +
"to trade in agriculture is controversial, with developing " +
"nations demanding that rich countries like Japan, the United" +
"States and European nations end subsidies and tariffs " +
"designed to keep unprofitable farms afloat.";
byte [] btSecret;
byte [] btEncryptedSecret;
byte [] btDecryptedSecret;
ASCIIEncoding AE = new ASCIIEncoding();
RSACryptoServiceProvider rsaKeyGen = new RSACryptoServiceProvider();
string rsaCompleteKeyString =
rsaKeyGen.ToXmlString(true);
string rsaPublicKeyString =
rsaKeyGen.ToXmlString(false);
btSecret = Encoding.ASCII.GetBytes(secret);
btEncryptedSecret = cRSAWrap.EncryptBuffer(
rsaPublicKeyString, btSecret);
btDecryptedSecret = cRSAWrap.DecryptBuffer(
rsaCompleteKeyString, btEncryptedSecret);
A Note On Multi-Block encryption/decryption:
1. Key size Vs Block Size
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
... more code ...
keySize = rsaSender.KeySize/8;
blockSize = keySize -11;
2. RSACryptoServiceProvider.Encrypt
btEncryptedToken = rsaSender.Encrypt(btPlaintextToken, false);
btEncryptedToken size: keySize;
btPlaintextToken size: blockSize;
CAUTION: Cryptographic exception �Bad Data� will be thrown if btPlaintextToken�s buffer size is set incorrectly.
3. RSACryptoServiceProvider.Decrypt
btPlaintextToken = rsaReceiver.Decrypt(btEncryptedToken, false);
btEncryptedToken size: keySize;
btPlaintextToken size: blockSize;
CAUTION: Cryptographic exception �Bad Data� will be thrown if btEncryptedToken�s buffer size is set incorrectly.
Appendix 4: Disclaimer
THIS SOFTWARE IS PROVIDED BY AUTHORS/CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS/CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.