2013-11-15 4 views
4

Привет У меня есть C++ приложение, использующее OpenSSL и WinAPI сокетов, а код выглядит следующим образом:C# против C++ ssl_connect

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 используя статические библиотеки Ssl с этим с приложением ++, который я скачал с here

Все работает нормально, и ssl_connect возвращает 1. Но мне нужно сделать перезаписать этот код с помощью C#. Поэтому я попробовал, и C# код выглядит следующим образом:

[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; 
} 

Моя C# сокет так:

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

this.socket.Connect(host,port); 

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

Я всегда получаю SSL_Connect = 0 в моей C# код, с SSL_get_error() = 5 (error_syscall). Так что в основном мой вопрос: что может быть неправильно с использованием .net-сокета с openssl? (потому что приложение C++ отлично работает с одним и тем же кодом).

Обновление: Я попытался использовать OPENSSL_add_all_algorithms_conf, но, похоже, он ничего не меняет ... Я прошу вас о помощи !!

+0

Когда вы получаете error_syscall, что находится в errno? Вы получаете ошибку от базовой ОС. – Joe

ответ

1

Насколько я могу судить, класс System.Net.Sockets не поддерживает SSL. После некоторых исследований, это выглядит, как будто это в System.Net.Security и System.Security.Authentication ...

Вот ссылка на пример MS ... http://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx

Этот пример отправляет одно сообщение на сервер, а затем отключается. Я написал аналогичный (не SSL) клиент, который является Full Duplex (send and received async). Вероятно, вы можете адаптировать мой пример для связи через SSL с использованием некоторого примера MS.

Здесь это ... Надеюсь, что это поможет ... сообщите мне, если вы адаптируете его для SSL, поскольку мне, вероятно, придется сделать то же самое.

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 
      } 

     } 
} 
+0

Эй, спасибо за код, которому я действительно удался, чтобы он работал с openssl api, у меня был дополнительный прием из сокета, после того, как пакет, который должен был определить: alow/disallow ssl. Поэтому любой совет для других, у кого могут возникнуть проблемы с ssl, убедитесь, что у вас нет дополнительного recv после того, как вы получили пакет, который должен сказать вам, разрешить/или отклонить ssl. (если у вас есть этот тип пакета в протоколе, с которым вы работаете). Я на самом деле собираюсь переписать мою реализацию dllimport и использовать вашу, так что спасибо! :) –

Смежные вопросы