проблема с SSL_read
От: dag77er  
Дата: 13.05.10 13:13
Оценка:
пытаюсь написать клиент с использованием SSL. Проверка сертификатов клиента и сервера на этапе установления соединения по условиям задачи не требуется.
Используется C#, MSVS 2008, библиотеки OpenSSL 0.9.8.14.
Создал средствами .NET сокет. Стандартным образом подключил SSL, все вроде в порядке (SSL_connect вернул 1). Выполняю передачу данных — все в порядке, SSL_write возвращает >0, количество переданных байт похоже на правду, SSL_get_error (на вякий случай проверяю) возвращает 0. После этого пытаюсь сделать SSL_read и все зависает. Никак ничего не комментируя. Что может быть не так?

Фрагменты кода (в основе своей честностырено здесь):

// Описание класса OpenSslStream
//
namespace Addin
{
    public class OpenSslStream : Stream
    {
        protected IntPtr m_ssl = IntPtr.Zero;
        protected IntPtr m_ctx;
        protected Socket m_socket;
        static bool s_initialized = false;

        // некоторые в данном случае несущественные фрагменты пропущены

        protected static void InitializeOpenSSL()
        {
            if (!s_initialized)
            {
                API.SSL_load_error_strings();
                API.SSL_library_init();
                s_initialized = true;
            }
        }

        public OpenSslStream(Socket socket, SslProtocols protocol, bool isClient)
        {
            InitializeOpenSSL();

            if (socket == null)
                throw new ArgumentNullException("socket");

            m_socket = socket;
            m_isClient = isClient;

            IntPtr method = IntPtr.Zero;

            if ((protocol & SslProtocols.Default) == SslProtocols.Default)
                method = API.SSLv23_client_method();
            else if ((protocol & SslProtocols.Tls) == SslProtocols.Tls)
                method = API.TLSv1_client_method();
            else if ((protocol & SslProtocols.Ssl3) == SslProtocols.Ssl3)
                method = API.SSLv3_client_method();
            else if ((protocol & SslProtocols.Ssl2) == SslProtocols.Ssl2)
                method = API.SSLv2_client_method();
            else
                method = API.SSLv23_client_method();

            if (method == IntPtr.Zero)
            {
                throw new ArgumentException("failed to find a suitable protocol", "protocol");
            }

            /// Create a new OpenSSL context.
            m_ctx = API.SSL_CTX_new(method);
        }

        /// <summary>
        /// Destructor.
        /// </summary>
        ~OpenSslStream()
        {
            Cleanup();
        }

        void Cleanup()
        {
            if (m_ssl != IntPtr.Zero)
            {
                API.SSL_free(m_ssl);
                m_ssl = IntPtr.Zero;
            }
            if (m_ctx != IntPtr.Zero)
            {
                API.SSL_CTX_free(m_ctx);
                m_ctx = IntPtr.Zero;
            }
        }

        /// <summary>
        /// Attempts to authenticate with the server.
        /// </summary>
        public void Authenticate()
        {
            if (m_ssl != IntPtr.Zero)
                throw new InvalidOperationException("m_ssl already initialized");
            
            if (m_socket == null)
                throw new ObjectDisposedException("m_socket cannot be null");
            
            if (m_ctx == IntPtr.Zero)
                throw new ObjectDisposedException("m_ctx cannot be null");
            
            try
            {
                /// Get socket handle
                int fd = m_socket.Handle.ToInt32();

                if (fd == -1)
                    throw new ArgumentException("invalid socket handle");

                /// Create a new ssl structure.
                m_ssl = API.SSL_new(m_ctx);

                if (m_ssl == IntPtr.Zero)
                    throw new NotSupportedException("m_ssl == zero");

                int a = API.SSL_set_fd(m_ssl, fd);

                // Connect to the server.
                int result = API.SSL_connect(m_ssl) : API.SSL_accept(m_ssl);

                if (result != 1)
                {
                    throw new AuthenticationException();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("не получилось авторизоваться");
                Cleanup();
                throw ex;
            }
        }
 
        public string ReadFromSSL() // на этом методе все затыкается.
        {
            byte[] buffer = new byte[8192];
            string returnString = string.Empty;
            int error;

            while ((error = API.SSL_read(m_ssl, buffer, 8192)) > 0)
            {
                returnString += Encoding.UTF8.GetString(buffer); 
            }
            if (error < 0)
                throw new Exception("Read: Socket error.");
            return returnString;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            int good = 0;
            if (m_ssl == IntPtr.Zero)
                throw new ObjectDisposedException("m_ssl cannot be null");

            if (offset == 0)
            {
                good = API.SSL_write(m_ssl, buffer, count);
                MessageBox.Show("оффсет = 0"); // это я себе для наглядности
                MessageBox.Show("отправка = " + good);
                MessageBox.Show("ошибка отправки сообщения = " + API.SSL_get_error(m_ssl, good));
            }

            byte[] tmp = new byte[count];
            Array.Copy(buffer, offset, tmp, 0, count);
            good = API.SSL_write(m_ssl, tmp, count);
            MessageBox.Show("оффсет не = 0"); // и это я себе для наглядности
            MessageBox.Show("отправка = " + good);
            MessageBox.Show("ошибка отправки сообщения = " + API.SSL_get_error(m_ssl, good));
        }
    }

    #region API

    static class API
    { 
        const string libssl = "ssleay32";
        const string libeay = "libeay32";

        // несколько методов опущено 
        [DllImport(libssl)]
        extern public static int SSL_set_fd(IntPtr ssl, int fd);

        [DllImport(libssl)]
        extern public static int SSL_library_init();

        [DllImport(libssl)]
        extern public static IntPtr SSL_CTX_new(IntPtr method);

        [DllImport(libssl)]
        extern public static IntPtr SSL_new(IntPtr ctx);

        [DllImport(libssl)]
        extern public static int SSL_connect(IntPtr ssl);

        [DllImport(libssl)]
        extern public static int SSL_read(IntPtr ssl, byte[] buf, int num);

        [DllImport(libssl)]
        extern public static int SSL_write(IntPtr ssl, byte[] buf, int num);
    }

    #endregion
}


И потом все это используется так:

public void Connection(String server, Int32 port, ref String password)
{
            try
            {
                // Подключение к серверу
                client = new TcpClient(server, port);
                Socket sock = client.Client;
                Byte[] bytes = new Byte[8];
                
                // Это мне надо по условиям задачи и тут все пока работает
                sock.Receive(bytes);
                sock.Send(bytes);
                
                // собственно, подключение ssl
                ssl = new Addin.OpenSslStream(sock, SslProtocols.Ssl3, true);
                ssl.Authenticate(); // работает
                bytes = Encoding.UTF8.GetBytes(password);
                ssl.Write(bytes, 0, bytes.Length); // данные пишутся успешно
                password = ssl.ReadFromSSL(); // на этом месте все виснет.
            }
            catch
            {
                MessageBox.Show("нет соединения!");
            }
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.