Note that there are some explanatory texts on larger screens.

plurals
  1. PODecrypt WebSocket client communication with C#
    primarykey
    data
    text
    <p>I am trying to make a demonstration for web socket communication for one of our new projects at work, and I've been trying to revise an old web socket server program I wrote quite a while ago.</p> <p>It does a proper handshake and can send data to the client properly (which is all it REALLY needs to do), but the client data that comes back is in the special web socket communication protocol, and I am not that good with working with binary or hex or encryption algorithms.</p> <p>I know from research that the text I'm receiving back contains a frame around it, and that it is sha1 encrypted, but my problem is that I have no idea how to read or remove this frame or unencrypt it.</p> <p>Here is the full code of the web server so far:</p> <pre><code>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.IO; using System.Web; using System.Collections.Specialized; using System.Text.RegularExpressions; using System.Threading; using System.Security.Cryptography; namespace WebSocks { public class WebSockServer { /// &lt;summary&gt; /// Port number to listen on /// &lt;/summary&gt; private const int PortNumber = 8181; /// &lt;summary&gt; /// Socket which awaits connections /// &lt;/summary&gt; private Socket ListenerSocket; /// &lt;summary&gt; /// Thread in which we await for incomming connections. /// &lt;/summary&gt; private System.Threading.Thread _serverThread; public delegate void ClientConnectedHandler (Socket Sock); public delegate void ReceivedDataHandler(Socket Sock, string Message); public event ClientConnectedHandler ClientConnected; public event ReceivedDataHandler ReceivedData; static WebSockServer() { } /// &lt;summary&gt; /// Starts thread with listening socket. /// &lt;/summary&gt; public void Start() { System.Threading.ThreadStart ts = new System.Threading.ThreadStart(Listen); _serverThread = new System.Threading.Thread(ts); _serverThread.Start(); } /// &lt;summary&gt; /// Stops listening for connections. /// &lt;/summary&gt; public void End() { _serverThread.Abort(); ListenerSocket.Dispose(); } public void Listen() { //Start listening ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); EndPoint ep = new IPEndPoint(IPAddress.Parse("0.0.0.0"), PortNumber); ListenerSocket.Bind(ep); ListenerSocket.Listen(5); while (true) { //New client Socket client = ListenerSocket.Accept(); //Receiving clientHandshake string clientHandshake = String.Empty; byte[] buffer = null; int readBytes = 0; do { buffer = new byte[client.Available]; readBytes = client.Receive(buffer); clientHandshake += Encoding.UTF8.GetString(buffer); } while (client.Available &gt; 0); //Last eight bytes are body of requets (we should include it in response) byte[] secKey3 = buffer.Skip(readBytes - 8).Take(8).ToArray(); //Variables we can extract from clientHandshake string clientOrigin = String.Empty; string secKey1 = String.Empty; string secKey2 = String.Empty; string WebSocketVersion = String.Empty; int WSV = 0; string WebSocketKey = String.Empty; //Extracting values from headers (key:value) string[] clientHandshakeLines = Regex.Split(clientHandshake, Environment.NewLine); foreach (string hline in clientHandshakeLines) { int valueStartIndex = hline.IndexOf(':') + 2; if (valueStartIndex &gt; 0) { if (hline.StartsWith("Origin")) { clientOrigin = hline.Substring(valueStartIndex, hline.Length - valueStartIndex); } else if (hline.StartsWith("Sec-WebSocket-Key2")) { secKey2 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex); } else if (hline.StartsWith("Sec-WebSocket-Key1")) { secKey1 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex); } if (hline.StartsWith("Sec-WebSocket-Version")) { WebSocketVersion = hline.Replace("Sec-WebSocket-Version: ", ""); WSV = Convert.ToInt32(WebSocketVersion); } if (hline.StartsWith("Sec-WebSocket-Key")) { WebSocketKey = hline.Replace("Sec-WebSocket-Key: ", ""); } } } if (!String.IsNullOrEmpty(WebSocketVersion)) //WebSocketVersion 8 and up handshake check { //New WebSocketVersion number, included after Version 8 StringBuilder mResponse = new StringBuilder(); mResponse.AppendLine("HTTP/1.1 101 Switching Protocols"); mResponse.AppendLine("Upgrade: WebSocket"); mResponse.AppendLine("Connection: Upgrade"); mResponse.AppendLine(String.Format("Sec-WebSocket-Accept: {0}", ComputeWebSocketHandshakeSecurityHash09(WebSocketKey)) + Environment.NewLine); byte[] HSText = Encoding.UTF8.GetBytes(mResponse.ToString()); client.Send(HSText, 0, HSText.Length, 0); } else { //This part is common for all websockets editions (v. 75 &amp; v.76) client.Send(Encoding.UTF8.GetBytes("HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine)); client.Send(Encoding.UTF8.GetBytes("Upgrade: WebSocket" + Environment.NewLine)); client.Send(Encoding.UTF8.GetBytes("Connection: Upgrade" + Environment.NewLine)); if (String.IsNullOrEmpty(secKey1) &amp;&amp; String.IsNullOrEmpty(secKey2)) //75 or less handshake check { client.Send(Encoding.UTF8.GetBytes(String.Format("WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine)); client.Send(Encoding.UTF8.GetBytes("WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine)); client.Send(Encoding.UTF8.GetBytes(Environment.NewLine)); } else //76 handshake check { //Keys present, this means 76 version is used. Writing Sec-* headers client.Send(Encoding.UTF8.GetBytes(String.Format("Sec-WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine)); client.Send(Encoding.UTF8.GetBytes("Sec-WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine)); client.Send(Encoding.UTF8.GetBytes(Environment.NewLine)); //Calculating response body byte[] secret = CalculateSecurityBody(secKey1, secKey2, secKey3); client.Send(secret); } } if (ClientConnected != null) { ClientConnected(client); } Thread t = new Thread(new ParameterizedThreadStart(WaitForMessages)); t.Start(client); } } private static void SendMessage(string Msg, Socket client, int WebSockVersion) { if (WebSockVersion &gt;= 8) { bool IsFinal = true; int OpCode = 1; int? Mask = null; byte[] payload = Encoding.UTF8.GetBytes(Msg); int PayloadLength = payload.Length; byte[] buffer = new byte[64]; // for working out the header int offset = 0; buffer[offset++] = (byte)((IsFinal ? 128 : 0) | ((int)OpCode &amp; 15)); if (PayloadLength &gt; ushort.MaxValue) { // write as a 64-bit length buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127); buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = (byte)(PayloadLength &gt;&gt; 24); buffer[offset++] = (byte)(PayloadLength &gt;&gt; 16); buffer[offset++] = (byte)(PayloadLength &gt;&gt; 8); buffer[offset++] = (byte)(PayloadLength); } else if (PayloadLength &gt; 125) { // write as a 16-bit length buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126); buffer[offset++] = (byte)(PayloadLength &gt;&gt; 8); buffer[offset++] = (byte)(PayloadLength); } else { // write in the header buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength); } if (Mask.HasValue) { int mask = Mask.Value; buffer[offset++] = (byte)(mask &gt;&gt; 24); buffer[offset++] = (byte)(mask &gt;&gt; 16); buffer[offset++] = (byte)(mask &gt;&gt; 8); buffer[offset++] = (byte)(mask); } // you might want to manually combine these into 1 packet client.Send(buffer, 0, offset, SocketFlags.None); client.Send(payload, 0, payload.Length, SocketFlags.None); } else { client.Send(new byte[] { 0x00 }); client.Send(Encoding.UTF8.GetBytes(Msg)); client.Send(new byte[] { 0xFF }); } } private void WaitForMessages(object client) { Socket sock = (Socket)client; byte[] buffer = new byte[1024]; while (true) { sock.Receive(buffer); ReceivedData(sock, Encoding.UTF8.GetString(buffer)); } } public byte[] CalculateSecurityBody(string secKey1, string secKey2, byte[] secKey3) { //Remove all symbols that are not numbers string k1 = Regex.Replace(secKey1, "[^0-9]", String.Empty); string k2 = Regex.Replace(secKey2, "[^0-9]", String.Empty); //Convert received string to 64 bit integer. Int64 intK1 = Int64.Parse(k1); Int64 intK2 = Int64.Parse(k2); //Dividing on number of spaces int k1Spaces = secKey1.Count(c =&gt; c == ' '); int k2Spaces = secKey2.Count(c =&gt; c == ' '); int k1FinalNum = (int)(intK1 / k1Spaces); int k2FinalNum = (int)(intK2 / k2Spaces); //Getting byte parts byte[] b1 = BitConverter.GetBytes(k1FinalNum).Reverse().ToArray(); byte[] b2 = BitConverter.GetBytes(k2FinalNum).Reverse().ToArray(); //byte[] b3 = Encoding.UTF8.GetBytes(secKey3); byte[] b3 = secKey3; //Concatenating everything into 1 byte array for hashing. List&lt;byte&gt; bChallenge = new List&lt;byte&gt;(); bChallenge.AddRange(b1); bChallenge.AddRange(b2); bChallenge.AddRange(b3); //Hash and return byte[] hash = MD5.Create().ComputeHash(bChallenge.ToArray()); return hash; } public String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey) { const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; String secWebSocketAccept = String.Empty; // 1. Combine the request Sec-WebSocket-Key with magic key. String ret = secWebSocketKey + MagicKEY; // 2. Compute the SHA1 hash SHA1 sha = new SHA1CryptoServiceProvider(); byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret)); // 3. Base64 encode the hash secWebSocketAccept = Convert.ToBase64String(sha1Hash); return secWebSocketAccept; } } } </code></pre> <p>I know its very crude, it doesn't clean up after itself, and it doesn't separate the different connections properly and hold them in a list or anything, this is purely for a demonstration project.</p> <p>So the main section I need help with is the WaitForMessages function:</p> <pre><code> private void WaitForMessages(object client) { Socket sock = (Socket)client; byte[] buffer = new byte[1024]; while (true) { sock.Receive(buffer); //Remove Frame and decrypt here ReceivedData(sock, Encoding.UTF8.GetString(buffer)); } } </code></pre> <p>I really just want to drop some code in here that will work, if you point me to a "demonstration that is kind of like your code and you should be able to figure it out from this" I'm really not going to understand it, I can almost guarantee you. I'm an app developer, not an API developer, I just don't want to wait until Microsoft finally gets around to writing an API into .NET 6.0 or something for this feature.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload