Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Примитивный кросcплатформенный сервер с помощью ssl

Anna | 24.06.2014 | нет комментариев

Не так давным-давно передо мною встала задача: написать кроссплатформенный сервер для обработки запросов по протоколу ssl. До этого я писал сервера для обыкновенных, не шифрованных протоколов, но с ssl столкнулся впервой.
Беглый обзор интернета показал, что лучшим решением будет не велосипедостроение, а применение библиотеки OpenSSL.
В этой статье я не хочу рассматривать процесс установки OpenSSL на Linux и Windows, подмечу лишь, что для Windows процесс данный оказался нетривиальным. А рассказать я хочу о том, как мне удалось скомпилировать в Visual Studio пример простейшего сервера, входящий в состав исходников OpenSSL.
Неискушенному читателю может показаться: «что здесь особенного — сотворил план, включил в него готовый исходник, запустил»… Впрочем обо каждому по порядку.

Мне, как программисту проще рассказывать действия по шагам:

1. Создаем директорию для экспериментов:
Пускай это будет скажем С:\testssl

2. Скачиваем начальный код OpenSSL
Пускай данный код хранится нипример в директории C:\openssl-1.0.1c
3. Компилируем OpenSSL
Позже компиляции у вас будет директория с библиотечными и заголовочными файлами. Скопируйте эту директорию в нашу тестовую. В итоге должна получиться такая конструкция файлов и директорий:
С:\testssl
С:\testssl\openssl
С:\testssl\openssl\bin
С:\testssl\openssl\bin\openssl.exe
С:\testssl\openssl\include
С:\testssl\openssl\include\openssl
С:\testssl\openssl\include\openssl\aes.h
…..(здесь много заголовочных файлов)
С:\testssl\openssl\include\openssl\x509v3.h
С:\testssl\openssl\lib
С:\testssl\openssl\lib\libeay32.lib
С:\testssl\openssl\lib\ssleay32.lib
С:\testssl\openssl\ssl
С:\testssl\openssl\ssl\openssl

4. Скопируем необходимый пример из C:\openssl-1.0.1c\demos\ssl\serv.cpp в С:\testssl\serv.cpp

5. Для работы примера понадобится файл с секретным ключем. Данный файл дозволено тоже взять из исходников.
Скопируем его из C:\openssl-1.0.1c\certs\demo\ca-cert.pem в С:\testssl\ca-cert.pem

5. Создаем пустой консольный план в Visual Studio и добавляем в него файл serv.cpp.
6. В свойствах плана добавляем путь для заголовков: С:\testssl\openssl\include и путь для библиотек:С:\testssl\openssl\lib. А также сами библиотеки libeay32.lib, ssleay32.lib

7. Сейчас в коде исправляем

#define CERTF  HOME "foo-cert.pem"
#define KEYF  HOME  "foo-cert.pem"

на

#define CERTF  HOME "ca-cert.pem"
#define KEYF  HOME  "ca-cert.pem

8. Казалось бы вот и все, но код безусловно не скомпилируется. Дело в том, что в Windows и Linux необходимо включать различные типовые заголовочные файлы.

Необходимо поправить

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <openssl/rsa.h>       /* SSLeay stuff */
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

на

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>

#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#else
#include <io.h>
#include <Winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#endif

#include <openssl/rsa.h>       /* SSLeay stuff */
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

Как видно, для Windows необходимо включить файл «Winsock2.h», а так же на каждый случай библиотеку сокетов.

9. Но и это еще не все! Если сейчас попытаться скомпилировать план, то будут выданы ошибки:
error C2440: ‘=’: cannot convert from ‘const SSL_METHOD *’ to ‘SSL_METHOD *’
error C2664: ‘accept’: cannot convert parameter 3 from ‘size_t *’ to ‘int *’

Эти ошибки легко находятся и исправляются, но если вы хотите кросплотформенности, то исправлять нужно старательно:

Для первой ошибки нужно

  meth = SSLv23_server_method();

поправить на

#ifdef WIN32
  const SSL_METHOD *meth = SSLv23_server_method();
#else
  SSL_METHOD *meth = SSLv23_server_method();
#endif

Для 2-й ошибки нужно

  sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);

поправить на

#ifdef WIN32
	sd = accept (listen_sd, (struct sockaddr*) &sa_cli, (int *)&client_len);
#else
	sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
#endif  

10. Вот сейчас код скомпилируется и запустится, но выдаст ошибку в строке
«listen_sd = socket (AF_INET, SOCK_STREAM, 0); CHK_ERR(listen_sd, „socket“);»
Да. В Windows, Дабы трудиться с сокетами, нужно вначале вызвать функцию WSAStartup()!
Добавим ее в предисловие программы:

void main ()
{
  int err;
  int listen_sd;
  int sd;
  struct sockaddr_in sa_serv;
  struct sockaddr_in sa_cli;
  size_t client_len;
  SSL_CTX* ctx;
  SSL*     ssl;
  X509*    client_cert;
  char*    str;
  char     buf [4096];

#ifdef WIN32
	WSADATA wsaData;
	if ( WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) != 0 )
	{
		printf("Could not to find usable WinSock in WSAStartup\n");
		return;
	}
#endif

11. Запусаем программу, Windows умоляет разрешения Дабы приложение могло открыть порт. Разрешаем.
Приложение доходит до строки

sd = accept (listen_sd, (struct sockaddr*) &sa_cli, (int *)&client_len);

и подвисает.
Все верно, наш сервер ожидает пока к нему кто-нибудь подсоединится.
На этом этапе соединиться дозволено из командной строки: «telnet localhost 1111». Как только соединение произойдет, программа продолжит работу до строки

close (listen_sd);

На этой строке Visual Studio выдаст непонятную ошибку.
Но если разобраться то окажется, что в Windows сокеты закрывают иной функцией: «closesocket()».
Дабы избежать ошибок — меняем всюду close на closesocket, а для кросплатформенности добавляем код:

#ifndef WIN32
#define closesocket  close
#endif

12. Сейчас приложение запустилось и отработало до строки

err = SSL_accept (ssl);                        CHK_SSL(err);

Тут оно вновь повисло, видимо ждя от заказчика обмена сообщениями по протоколу ssl.
Как обмениваться этими сообщениями вручную телнетом, я лично не знаю. Но Дабы проверить работу программы до конца дозволено воспользоваться обыкновенным браузером!

13. Для проверки работоспособности нашего сервера запускаем его, а взамен командной строки запускаем браузер.
В адресной строке браузера набираем: https://localhost:1111
Браузер предупредит о небезопасном сертификате. Необходимо принять риск. Впрочем пока мы общаемся с браузером, сервер может вновь кончаться с оплошностью. Это типично, запустите его еще раз.

14. Сейчас, когда сервер запущен, а браузер помнит что мы этому серверу доверяем, наша программа наконец отработает до конца и примет от браузера букву «G» (предисловие запроса «GET»), по зашифрованному соединению!

15. На этом дозволено было бы завершить, но я решил для чистоты эксперимента скомпилировать получившийся файл на Linux. Оказалось, что и здесь не без сюрпризов.
На этапе компиляции мне выдалась оплошность:
serv.cpp:51: error: ‘::main’ must return ‘int’

Вот так вот. Оказывается пример из исходников OpenSSL не компилируется не только в Windows, но и в Linux.
Безусловно, я поправил «void main()» на «int main()» и всюду взамен «return» написал «return 0», но осадок остался.

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB