MTSC  1.0.5
Build TCP servers out of modules with handlers for communication, exception and logging.
EncryptionHandler.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Net.Sockets;
5 using System.Security.Cryptography;
6 using System.Text;
7 using System.Xml;
8 
9 namespace MTSC.Client.Handlers
10 {
11  public class EncryptionHandler : IHandler
12  {
13  private enum ConnectionState
14  {
15  Initial,
16  RequestingPublicKey,
17  NegotiatingSymKey,
18  Encrypted
19  }
20  #region Fields
21  private ConnectionState connectionState = ConnectionState.Initial;
22  private byte[] aesKey;
23  #endregion
24  #region Constructors
25  public EncryptionHandler()
30  {
31 
32  }
33  #endregion
34  #region Public Methods
35 
36  #endregion
37  #region Private Methods
38  private string GetUniqueKey(int size)
39  {
40  return Convert.ToBase64String(GetUniqueByteKey(size));
41  }
42 
43  private byte[] GetUniqueByteKey(int size)
44  {
45  byte[] data = new byte[size];
46  using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
47  {
48  crypto.GetBytes(data);
49  }
50  return data;
51  }
52 
53  private byte[] EncryptBytes(byte[] bytesToBeEncrypted)
54  {
55  byte[] encryptedBytes = null;
56 
57  // Set your salt here, change it to meet your flavor:
58  // The salt bytes must be at least 8 bytes.
59  byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
60 
61  using (MemoryStream ms = new MemoryStream())
62  {
63  using (RijndaelManaged AES = new RijndaelManaged())
64  {
65  AES.KeySize = 256;
66  AES.BlockSize = 128;
67  AES.Mode = CipherMode.CBC;
68  var key = new Rfc2898DeriveBytes(aesKey, saltBytes, 1000);
69  AES.Key = key.GetBytes(AES.KeySize / 8);
70  AES.IV = key.GetBytes(AES.BlockSize / 8);
71 
72  using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
73  {
74  cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
75  cs.Close();
76  }
77  encryptedBytes = ms.ToArray();
78  }
79  }
80 
81  return encryptedBytes;
82  }
83 
84  private byte[] DecryptBytes(byte[] bytesToBeDecrypted)
85  {
86  byte[] decryptedBytes = null;
87 
88  // Set your salt here, change it to meet your flavor:
89  // The salt bytes must be at least 8 bytes.
90  byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
91 
92  using (MemoryStream ms = new MemoryStream())
93  {
94  using (RijndaelManaged AES = new RijndaelManaged())
95  {
96  AES.KeySize = 256;
97  AES.BlockSize = 128;
98 
99  AES.Mode = CipherMode.CBC;
100 
101  var key = new Rfc2898DeriveBytes(aesKey, saltBytes, 1000);
102  AES.Key = key.GetBytes(AES.KeySize / 8);
103  AES.IV = key.GetBytes(AES.BlockSize / 8);
104 
105 
106 
107  using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
108  {
109  cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
110  cs.Close();
111  }
112  decryptedBytes = ms.ToArray();
113  }
114  }
115 
116  return decryptedBytes;
117  }
118 
119  private string EncryptText(string input)
120  {
121  // Get the bytes of the string
122  byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
123 
124  byte[] bytesEncrypted = EncryptBytes(bytesToBeEncrypted);
125 
126  string result = Encoding.UTF8.GetString(bytesEncrypted);
127 
128  return result;
129  }
130 
131  private string DecryptText(string input)
132  {
133  // Get the bytes of the string
134  byte[] bytesToBeDecrypted = Encoding.UTF8.GetBytes(input);
135 
136  byte[] bytesDecrypted = DecryptBytes(bytesToBeDecrypted);
137 
138  string result = Encoding.UTF8.GetString(bytesDecrypted);
139 
140  return result;
141  }
142  #endregion
143  #region Interface Implementation
150  {
151  this.connectionState = ConnectionState.Initial;
152  return true;
153  }
158  void IHandler.Disconnected(Client client)
159  {
160 
161  }
168  bool IHandler.HandleSendMessage(Client client, ref Message message)
169  {
170  if (connectionState == ConnectionState.Encrypted)
171  {
172  byte[] decryptedBytes = message.MessageBytes;
173  byte[] encryptedBytes = EncryptBytes(decryptedBytes);
174  message = CommunicationPrimitives.BuildMessage(encryptedBytes);
175  return true;
176  }
177  return false;
178  }
185  bool IHandler.PreHandleReceivedMessage(Client client, ref Message message)
186  {
187  if (connectionState == ConnectionState.Encrypted)
188  {
189  byte[] encryptedBytes = message.MessageBytes;
190  byte[] decryptedBytes = DecryptBytes(encryptedBytes);
191  message = CommunicationPrimitives.BuildMessage(decryptedBytes);
192  return false;
193  }
194  return false;
195  }
202  bool IHandler.HandleReceivedMessage(Client client, Message message)
203  {
204  if (connectionState == ConnectionState.RequestingPublicKey)
205  {
206  string ascii = ASCIIEncoding.ASCII.GetString(message.MessageBytes);
207  if (ascii.Contains(CommunicationPrimitives.SendPublicKey))
208  {
209  byte[] publicKeyBytes = new byte[message.MessageLength - CommunicationPrimitives.SendPublicKey.Length - 1];
210  Array.Copy(message.MessageBytes, CommunicationPrimitives.SendPublicKey.Length + 1, publicKeyBytes, 0, publicKeyBytes.Length);
211  string publicKey = ASCIIEncoding.ASCII.GetString(publicKeyBytes);
212  RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
213  RSAParameters parameters = new RSAParameters();
214  XmlDocument xmlDoc = new XmlDocument();
215  xmlDoc.LoadXml(publicKey);
216  if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
217  {
218  foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
219  {
220  switch (node.Name)
221  {
222  case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
223  case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
224  case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
225  case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
226  case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
227  case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
228  case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
229  case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
230  }
231  }
232  }
233  rsa.ImportParameters(parameters);
234  byte[] symkeyBytes = GetUniqueByteKey(32);
235  byte[] encryptedSymKey = rsa.Encrypt(symkeyBytes, false);
236  aesKey = symkeyBytes;
237  byte[] messageHeader = ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.SendEncryptionKey + ":");
238  byte[] messageBytes = new byte[encryptedSymKey.Length + messageHeader.Length];
239  Array.Copy(messageHeader, 0, messageBytes, 0, messageHeader.Length);
240  Array.Copy(encryptedSymKey, 0, messageBytes, messageHeader.Length, encryptedSymKey.Length);
241  client.QueueMessage(messageBytes);
242  connectionState = ConnectionState.NegotiatingSymKey;
243  return true;
244  }
245  }
246  else if (connectionState == ConnectionState.NegotiatingSymKey)
247  {
248  string ascii = ASCIIEncoding.ASCII.GetString(DecryptBytes(message.MessageBytes));
249  if (ascii == CommunicationPrimitives.AcceptEncryptionKey)
250  {
251  connectionState = ConnectionState.Encrypted;
252  return true;
253  }
254  else
255  {
256  connectionState = ConnectionState.Initial;
257  return false;
258  }
259  }
260  return false;
261  }
267  void IHandler.Tick(Client client)
268  {
269  if (connectionState == ConnectionState.Initial)
270  {
271 
272  client.QueueMessage(ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.RequestPublicKey));
273  this.connectionState = ConnectionState.RequestingPublicKey;
274  }
275  }
276  #endregion
277  }
278 }
EncryptionHandler()
Creates an instance of EncryptionHandler.
bool HandleSendMessage(Client client, ref Message message)
Called when a message is being sent to the server.
bool PreHandleReceivedMessage(Client client, ref Message message)
Called before the message is handled. Use this method to modify the message if necesarry.
bool HandleReceivedMessage(Client client, Message message)
Called when a message has been received.
void Tick(Client client)
Called every cycle. This method should perform regular actions on the connection.
bool InitializeConnection(Client client)
Called when the connection is being initialized.
void Disconnected(Client client)
Called when the client is disconnected.
void QueueMessage(byte[] message)
Add a message to the message queue.
Definition: Client.cs:59
Handler interface for client communication.
Definition: IHandler.cs:11
Base class for TCP Client.
Definition: Client.cs:19