Содержание


Безопасное программирование с OpenSSL API, Часть 3

Обеспечение надежного обслуживания

OpenSSL предоставляет необходимые возможности

Comments

Серия контента:

Этот контент является частью # из серии # статей: Безопасное программирование с OpenSSL API, Часть 3

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Безопасное программирование с OpenSSL API, Часть 3

Следите за выходом новых статей этой серии.

Первые две части цикла обсуждают создание обращенного к клиенту приложения, используя OpenSSL. Часть 1 затрагивает создание базового безопасного пользователя, тогда как Часть 2более подробно рассказывает о цифровых сертификатах. После нескольких позитивных откликов, было очевидно, что последующие обсуждения должны коснуться серверов.

Серверы обеспечивают работу сети и интернета с доступом к таким источникам, как файлы и различные устройства. Иногда бывает необходимо обеспечить эти услуги в обход безопасных каналов. OpenSSL позволяет записывать услуги, используя как безопасный, так и открытый каналы.

Создание базового серверного приложения при использовании OpenSSL по характеру практически идентично созданию базового клиентского приложения. Отличий относительно немного. Первое, очевидно, это то, что сервер будет настроен на принятие входящих соединений, а не на создание исходящих. И, как вы, должно быть, помните из разговора о цифровых сертификатах в Части 2, сервер также должен обеспечить сертификат безопасности при установлении связи.

Соблюдая выжидательную позицию

Чаще всего сервер отдыхает в ожидании входящих соединений. В конце концов, он для этого и предназначен. Сетевые серверы ждут браузеры для запроса страниц, FTP серверы – пользователей для запроса файлов, диалоговые серверы – входящих пользовательских соединений. Они просто ждут.

Существует минимальное отличие между безопасным пользовательским и серверным обменом информацией, за исключением того, что сервер – это оборотная сторона в условиях соединения. Все остальное идентично.

Именно это делает создание безопасного серверного приложения с помощью OpenSSL сущим пустяком, при условии, что вы знаете, как при помощи OpenSSL создавать пользовательское приложение. (Если вы еще не имели с этим дела, то, пожалуйста, прочтите Часть 1 цикла Обзор API, чтобы научиться настраивать библиотеку OpenSSL).

Две формы идентификации, будьте добры

Или, скорее, две части распознавания.

Сервер отвечает за обеспечение сертификата безопасности, который будет использоваться при соединении. Полный сертификат состоит из двух частей: открытый ключ и секретный ключ. Открытый ключ – это то, что отсылается пользователю, тогда как секретный таковым и остается.

Точно так же, как надежный сертификат должен быть предоставлен библиотеке клиентского приложения, так и серверные ключи должны предоставляться библиотеке серверного приложения. Существует несколько функций, обеспечивающих это:

Листинг 1. Загрузка серверного сертификата
SSL_CTX_use_certificate(SSL_CTX *, X509 *)
SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d);
SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);

Переменная ASN1 этой функции загружает ASN1-раскодированный цифровой сертификат из определенного участка памяти в SSL контекст. Первая функция загружает X.509 сертификат, заключенный в данной структуре памяти, тогда как вторая, _file, загружает PEM-раскодированный цифровой сертификат из файла. Параметр type позволяет загрузить DER-раскодированный сертификат.

Чтобы загрузить секретный ключ, используйте следующие функции:

Листинг 2. Загрузка секретного ключа
SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey);
SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d, long len);
SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len);
SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type);

Необходимое взаимодействие

Любой секретный ключ лучше ранить зашифрованным. Хотя проблема заключается в том, что функции, которые загружают сертификаты, не запрашивают пароль для закодированных сертификатов. Вместо этого OpenSSL обеспечивает механизм отзыва для получения пароля.

Формат отзыва таков:

Листинг 3. Формат отзыва
int password_callback(char *buf, int size, int rwflag, void *userdata);

Для нас, последний параметр, userdata, не обязательный. Буфер распределяется до вызова этой функции, так что вы не контролируете размер буфера.

Параметр rwflag- это флажок считывания/записи. Он используется для того, чтобы вы могли программно определить, используется ли пароль для шифровки (rwflag = 1) или расшифровки (rwflag = 0). информации. Если отзыв используется для запроса пароля для расшифровки информации, предпочтительнее запрашивать пароль дважды, на всякий случай, чтобы исключить опечатки.

Пароль запрашивается один раз в случае, когда сертификат загружается, для того, чтобы он мог быть расшифрован и сохранен. То, каким образом получать пароль от пользователя, полностью зависит то вас.

Как только вы создали функцию отзыва пароля, установите ее в SSL контекст, используя SSL_CTX_set_default_passwd_cb, как показано ниже:

Листинг 4. Установка функции отзыва
/* ctx – указатель к предварительно созданному SSL контексту, а cb - указатель
 * на созданную функцию отзыва.
 */

SSL_CTX_set_default_passwd_cb(ctx, cb);

Введение ключа в запуск

Теперь, когда функция отзыва создана для напоминания о пароле, могут быть использованы функции собственно импорта сертификатов. Сертификаты могут быть импортированы как из существующей структуры памяти, так и из файла.

Для того чтобы следовать общепринятой практике обращения с цифровыми сертификатами, например как в Apache HTTP Server Project, я продемонстрирую, как загружать сертификат из файла. Если вы уже ознакомились с Частью 1 данного цикла, то вы знаете, что загрузка сертификата походит на загрузку доверительного хранилища, которая была продемонстрирована в статье.

Начнем с открытого сертификата, отсылаемого клиенту.

Листинг 5. Загрузка открытого сертификата
/**
 * ctx - SSL контекст, созданный ранее
 */

if(SSL_CTX_use_certificate_file(ctx, "/path/to/certificate.pem", SSL_FILETYPE_PEM) < 1)
{
    /* Управление неудавшейся загрузкой*/
}

После загрузки открытого сертификата, необходимо загрузить секретный сертификат. Эта деталь необходима во время соединения, так как клиент будет отсылать на сервер информацию, зашифрованную для открытого сертификата. Эта информация может быть расшифрована только при использовании секретного ключа. Снова, чтобы быть последовательным, загружаем этот ключ из файла.

Листинг 6. Загрузка секретного ключа
if(SSL_CTX_use_PrivateKey_file(ctx, "/path/to/private.key", SSL_FILETYPE_PEM) < 1)
{
    /* Управление неудавшейся загрузкой*/
}

Завершение настройки

После настройки контекста (см. SSL контекст выше) и загрузки ключей, пришло время завершить настройку созданием BIO объекта. ИзЧасти 1 вы можете вспомнить, как мы устанавливали SSL и non-SSL соединения, используя библиотеку OpenSSL. Чтобы не противоречить указанной статье, то же самое будет проделано здесь.

Листинг 7. BIO указатели
BIO *bio, *abio, *out;

Три BIO объекта? Почему нам нужны именно три? Поверьте, все три имеют свое назначение. (Память, доверие и безопасность – звенья одной цепи.)

Первый, bio, это основной BIO объект, который будет создан из SSL контекста. Второй объект, abio,это акцепт BIO, тот, который будет принимать входящие соединения. Третий, out, сервер будет передавать клиенту.

Листинг 8. Настройка основного BIO объекта
bio = BIO_new_ssl(ctx, 0);
if(bio == NULL)
{
    /* Управление сбоем*/
}

/* Здесь, ssl  - это SSL* (см. Часть 1) */

BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

Здесь настройка BIO объекта немного отличается от аналогичной для клиентского соединения. Вы можете вспомнить из Части 1, что клиентское соединение настраивается при использовании BIO_new_ssl_connect.

Здесь же настройка происходит при использовании BIO_new_ssl с двумя параметрами: указателем на SSL_CTX объект и флажком. Флажок указывает OpenSSL на то, какой BIO объект создать: 0 для сервера, 1 для клиента. Как только данный код пытается настроить клиентское соединение, флажок устанавливается на 0.

Листинг 9. Настройка акцепта BIO
abio = BIO_new_accept("4422");
BIO_set_accept_bios(abio, bio);

Там же, где BIO_do_connect создает BIO для клиентских соединений, BIO_new_acceptсоздает BIO для серверных соединений. Просто необходим один параметр, порт для передачи закодированный в строке.

Так как предполагается, что это передача для безопасных соединений, нам необходимо присоединить безопасный BIO к акцепту BIO. Вот где второй запрос функции, BIO_set_accept_bios, вступает в игру. Он присоединяет предварительно созданный SSL BIO к акцепту BIO.

Данный запрос функции также освобождает от необходимости освобождать SSL BIO. Он будет автоматически освобожден, когда акцепт BIO ликвидируется.

А теперь - ждать

Сервер – своего рода рыболовное судно, которое находится в ожидании "клева" клиента. Серверы ждут входящих соединений.

Если вы когда-либо имели дело с Winsock или BSD сокетами, вы наверняка сталкивались с функцией accept. В OpenSSL таким аналогом является BIO_do_accept, за исключением того, что accept нужно вызвать только один раз, чтобы перевести его в состояние ожидания, тогда как BIO_do_accept необходимо вызывать дважды.

Листинг 10. Перевод сервера в состояние ожидания
/* Первый запрос для настройки приема входящих соединений... */

if(BIO_do_accept(abio) <= 0)
{
    /* Управление сбоем */
}

/* Второй запрос для режима ожидания */

if(BIO_do_accept(abio) <= 0)
{
    /* Управление сбоем */
}

/* Любой следующий запрос установит режим ожидания автоматически */

Первый вызов BIO_do_accept настраивает BIO на прием входящего соединения. Следующий запрос необходим собственно для того, чтобы перевести его в состояние ожидания. Дальше вы можете просто ждать.

Ответ на входящий запрос

BIO_do_accept возвращает 1, когда принимает входящее соединение. Но вы не можете просто передать акцепт BIO. Вместо этого OpenSSL создает другой BIO, который должен быть вытолкнут из акцепта BIO c помощью BIO_pop.

Листинг 11. Выталкивание соединения для разговора
out = BIO_pop(abio);

if(BIO_do_handshake(out) <= 0)
{
    /* Управление сбоем */
}

После выталкивания водящего соединения из акцепта BIO, соединение должно быть осуществлено при помощи запроса BIO_do_handshake. Если предыдущие настройки были успешны, соединение также должно пройти без проблем.

Собственно общаться с клиентом сервер должен через различные функции считывания и записи, доступные библиотеке BIO. Об этом речь шла в Части 1.

Обеспечение надежного обслуживания

В целом создание безопасного серверного приложения с OpenSSL не составит сложности, как только вы освоили основы его функционирования. С этого момента вы можете расширять данные образцы кодов, для создания полного серверного приложения SSL. Однако будьте готовы к тому, что образцы кодов, данные здесь и в разделе Загрузка ниже, наименее оптимальны и могут быть использованы только в целях эксперимента. Перед созданием полного серверного приложения SSL обязательно ознакомьтесь с последними рекомендациями по безопасности.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=191375
ArticleTitle=Безопасное программирование с OpenSSL API, Часть 3: Обеспечение надежного обслуживания
publish-date=01222007