Hina Kagiyama Hina Kagiyama - 1 year ago 587
C# Question

c# vs c++ ssl_connect

Hello i have a c++ application that uses openssl and winapi sockets, and the code looks like this:

sock = socket(AF_INET, SOCK_STREAM, 0);
if (connect(my_sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr))==0) {
WSAIoctl(my_sock, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),NULL, 0, &dwSize, NULL, NULL);
}
}
.....

SSL_load_error_strings();
SSL_library_init();


ctx = SSL_CTX_new(SSLv23_client_method());
if(!ctx)
{
return false;
}
ssl = SSL_new(ctx);
SSL_CTX_free(ctx);
if(!ssl)
{
return false;
}
SSL_set_fd(ssl, (int)sock); //making the socket use ssl mode
result = SSL_connect(ssl);


Im using the static ssl libraries with this c++ application, which i downloaded from here

Everything works fine, and ssl_connect returns 1. But what i need to do is re-write this code using c#. So i tried, and c# code looks like this:

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_read")]
public static extern int SSL_Read(IntPtr ssl, ref byte[] buf, int num);

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_write")]
public static extern int SSL_Write(IntPtr ssl, byte[] buf, int num);

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_set_fd")]
extern public static int SSL_set_fd(IntPtr ssl, int fd);

[DllImport("ssleay32.dll", CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSL_CTX_new")]
extern public static IntPtr SSL_CTX_new(IntPtr method);

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_new")]
extern public static IntPtr SSL_new(IntPtr ctx);

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_connect")]
extern public static int SSL_connect(IntPtr ssl);

[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSL_load_error_strings")]
extern public static void SSL_load_error_strings();

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_library_init")]
extern public static int SSL_library_init();

[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSLeay_add_all_algorithms")]
extern public static int SSLeay_add_all_algorithms();

[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSLv23_client_method")]
extern public static IntPtr SSLv23_client_method();

[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSLv3_client_method")]
extern public static IntPtr SSLv3_client_method();


[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSL_CTX_free")]
extern public static void SSL_CTX_free(IntPtr ctx);

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_free")]
extern public static void SSL_free(IntPtr ssl);

[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = "SSL_get_error")]
extern public static int SSL_get_error(IntPtr ssl, int ret);

[DllImport("ssleay32.dll",CallingConvention= CallingConvention.Cdecl, EntryPoint = " SSL_CTX_set_mode")]
extern public static long SSL_CTX_set_mode(IntPtr ctx, long mode);


[DllImport("libeay32", CallingConvention = CallingConvention.Cdecl, EntryPoint = "OPENSSL_add_all_algorithms_noconf")]
extern public static void OpenSSL_add_all_algorithms();

[DllImport("libeay32", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ERR_get_error")]
extern public static int ERR_get_error();

[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSL_CTX_ctrl")]
public extern static int SSL_CTX_ctrl(IntPtr ctx, int cmd, int arg, IntPtr parg);


[DllImport("ssleay32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SSLv23_method")]
public extern static IntPtr SSLv23_method();



public bool Ssl_Init()
{

SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_library_init();

IntPtr method = SSLv23_client_method();

IntPtr ctx = SSL_CTX_new(method);

if (ctx == IntPtr.Zero)
{
return false;
}

_ssl = SSL_new(ctx);
if (_ssl == IntPtr.Zero)
{
return false;
}

int val = SSL_set_fd(_ssl, this.socket.Handle.ToInt32()); //always returns 1
int result = SSL_connect(_ssl);
if(result!=1) return false;
SSL_CTX_free(ctx);
Ssl_Enabled.Set();
return true;
}


My c# socket is created like so:

socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

this.socket.Connect(host,port);

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1000);


I always get SSL_Connect = 0 in my c# code, with SSL_get_error() = 5 (error_syscall). So basically my question is, what's might be wrong with using .net socket with openssl? (because c++ application works perfectly with the same kind of code).

Update: I tried to use OPENSSL_add_all_algorithms_conf but it seems it doesn't change anything... I'm begging you for help!!

Answer Source

As far as I can tell, the System.Net.Sockets class doesn't have support for SSL. After some research, it looks like it's in System.Net.Security and System.Security.Authentication...

Here's a link to an MS example... http://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx

This example sends one message to the server and then disconnects. I wrote a similar (non-SSL) client that is Full Duplex (send and received async). You can probably adapt my example to communicate over SSL using some of the MS example.

Here it is...Hope it helps...let me know if you adapt it for SSL as I'll likely need to do the same.

using System;
using System.Text;
using System.Net.Sockets;
using System.Collections.Generic;

namespace MyNamespace.Utilities
{
        public class StateObject{
            public Socket workSocket = null;
            public const int BUFFER_SIZE = 1024;
            public byte[] buffer = new byte[BUFFER_SIZE];
            //public StringBuilder message = new StringBuilder();
        }

        public class FullDuplexSocket : IDisposable
        {
            public event NewMessageHandler OnMessageReceived;
            public delegate void NewMessageHandler(string Message);
            public event DisconnectHandler OnDisconnect;
            public delegate void DisconnectHandler(string Reason);

            private Socket _socket;
            private bool _useASCII = true;
            private string _remoteServerIp = "";
            private int _port = 0;
            private bool _allowRetry = true;

            /// <summary>
            /// Constructer of a full duplex client socket.   The consumer should immedately define 
            /// and event handler for the OnMessageReceived event after construction has completed.
            /// </summary>
            /// <param name="RemoteServerIp">The remote Ip address of the server.</param>
            /// <param name="Port">The port that will used to transfer/receive messages to/from the remote IP.</param>
            /// <param name="UseASCII">The character type to encode/decode messages.  Defaulted to use ASCII, but setting the value to false will encode/decode messages in UNICODE.</param>
            public FullDuplexSocket(string RemoteServerIp, int Port, bool UseASCII = true)
            {
                _useASCII = UseASCII;
                _remoteServerIp = RemoteServerIp;
                _port = Port;

                Initialize();
            }

            private void Initialize()
            {
                try //to create the socket and connect
                {
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _socket.Connect(_remoteServerIp, _port);
                }
                catch (Exception e)
                {
                    throw new Exception("Unable to connect to the remote Ip.", e);
                }

                try //to listen to the socket
                {
                    StateObject stateObject = new StateObject();
                    stateObject.workSocket = _socket;

                    _socket.BeginReceive
                        (
                            stateObject.buffer, //Buffer to load in our state object
                            0, //Start at the first position in the byte array
                            StateObject.BUFFER_SIZE, //only load up to the max per read
                            0, //Set socket flags here if necessary 
                            new AsyncCallback(ReadFromSocket), //Who to call when data arrives
                            stateObject //state object to use when data arrives
                        );
                    _allowRetry = true;
                }
                catch (Exception e)
                {
                    throw new Exception("Unable to start listening to the socket.", e);
                }
            }


            /// <summary>
            /// This will read the bytes from the socket, convert the bytes to a string and fire the OnMessageReceived event.
            /// If the socket is forcibly closed, the OnDisconnect event will be fired.   This happens when the other side of
            /// the socket connection on the remote Ip is no longer available.
            /// </summary>
            /// <param name="asyncResult"></param>
            public void ReadFromSocket(IAsyncResult asyncResult)
            {
                StateObject stateObject = (StateObject)asyncResult.AsyncState; //pull out the state object
                int bytesReceived = 0;

                try //to receive the message.
                {
                    bytesReceived = stateObject.workSocket.EndReceive(asyncResult); 
                }
                catch (Exception e)  //Exception will occur if connection was forcibly closed.
                {
                    RaiseOnDisconnect(e.Message);
                    return;
                }

                if (bytesReceived > 0)
                {
                    RaiseOnMessageReceived
                        (
                            _useASCII ?
                                Encoding.ASCII.GetString(stateObject.buffer, 0, bytesReceived) :
                                Encoding.Unicode.GetString(stateObject.buffer, 0, bytesReceived)
                        );

                    try  //The BeginRecieve can file due to network issues.   _allowRetry allows a single failure between successful connections.
                    {
                        if (_allowRetry)
                        {
                            stateObject.workSocket.BeginReceive
                                (
                                    stateObject.buffer, //Buffer to load in our state object
                                    0, //Start at the first position in the byte array
                                    StateObject.BUFFER_SIZE, //only load up to the max per read
                                    0, //Set socket flags here if necessary 
                                    new AsyncCallback(ReadFromSocket), //Who to call when data arrives
                                    stateObject //state object to use when data arrives
                                );
                        }
                    }
                    catch
                    {
                        _allowRetry = false;
                    }
                }
                else
                {
                    stateObject.workSocket.Close();
                    RaiseOnDisconnect("Socket closed normally.");
                    return;
                }
            }
            /// <summary>
            /// Broadcast a message to the IP/Port.  Consumer should handle any exceptions thrown by the socket.
            /// </summary>
            /// <param name="Message">The message to be sent will be encoded using the character set defined during construction.</param>
            public void Send(string Message)
            {
                //all messages are terminated with /r/n
                Message += Environment.NewLine;

                byte[] bytesToSend = _useASCII ?
                    Encoding.ASCII.GetBytes(Message) :
                    Encoding.Unicode.GetBytes(Message);

                int bytesSent = _socket.Send(bytesToSend);

            }

            /// <summary>
            /// Clean up the socket.
            /// </summary>
            void IDisposable.Dispose()
            {
                try
                {
                    _socket.Close();
                    RaiseOnDisconnect("Socket closed via Dispose method.");
                }
                catch { }
                try
                {
                    _socket.Dispose();
                }
                catch { }
            }


            /// <summary>
            /// This method will gracefully raise any delegated events if they exist.
            /// </summary>
            /// <param name="Message"></param>
            private void RaiseOnMessageReceived(string Message)
            {
                try //to raise delegates
                {
                    OnMessageReceived(Message);
                }
                catch { } //when none exist ignore the Object Reference Error
            }

            /// <summary>
            /// This method will gracefully raise any delegated events if they exist.
            /// </summary>
            /// <param name="Message"></param>
            private void RaiseOnDisconnect(string Message)
            {
                try //to raise delegates
                {
                    OnDisconnect(Message);
                }
                catch { } //when none exist ignore the Object Reference Error
            }

        }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download