пытаюсь написать клиент с использованием 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("нет соединения!");
}
}