Встала задача обеспечить взаимодействие со службами РосМинздрава, а именно — с федеральным регистром медработников по протоколу SOAP. Со стороны клиента система на CentOS и работающими службами на PHP, со стороны сервера — SOAP-сервис с авторизацией по сертификатам с использованием ГОСТ алгоритмов. В наличии была флешка с закрытым ключом, сформированным удостоверяющим центром РосМинздрава и сертификат этого ключа.
После анализа ситуации по использованию ГОСТ алгоритмов шифрования в мире Linux выяснено что за последние годы здесь есть хорошее движение вперед, но все таки не совсем все хорошо. Итак, для того чтобы заставить расширение php-soap прозрачно понимать алгоритмы ГОСТ, а также использовать сертификаты и ключи выданные РосМинздравом нужно сделать следующее:
1. Обновить в дистрибутиве библиотеку OpenSSL до версии не ниже 1.0.1с и настроить поддержку ГОСТ.
2. Преобразовать выданный ключ и сертификат в формат, понятный OpenSSL. Проверить работу OpenSSL.
3. Подправить расширение OpenSSL в PHP и перекомпилировать сам PHP. Протестировать работу SOAP на PHP.
Итак, приступим. Данные дистрибутива, установленного php и библиотеки openssl:
# lsb_release -a LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch Distributor ID: CentOS Description: CentOS release 6.4 (Final) Release: 6.4 Codename: Final # yum list installed | grep php php.x86_64 5.3.3-22.el6 @base php-cli.x86_64 5.3.3-22.el6 @base php-common.x86_64 5.3.3-22.el6 @base php-dba.x86_64 5.3.3-22.el6 @base php-devel.x86_64 5.3.3-22.el6 @base php-imap.x86_64 5.3.3-22.el6 @base php-ldap.x86_64 5.3.3-22.el6 @base php-lessphp.noarch 0.3.9-1.el6 @epel php-mbstring.x86_64 5.3.3-22.el6 @base php-mcrypt.x86_64 5.3.3-1.el6 @epel php-odbc.x86_64 5.3.3-22.el6 @base php-pdo.x86_64 5.3.3-22.el6 @base php-pear.noarch 1:1.9.4-4.el6 @base php-pgsql.x86_64 5.3.3-22.el6 @base php-process.x86_64 5.3.3-22.el6 @base php-shout.x86_64 0.9.2-6.el6 @epel php-soap.x86_64 5.3.3-22.el6 @base php-xml.x86_64 5.3.3-22.el6 @base php-xmlrpc.x86_64 5.3.3-22.el6 @base # yum list installed | grep openssl openssl.x86_64 1.0.0-27.el6_4.2 @updates openssl-devel.x86_64 1.0.0-27.el6_4.2 @updates
Обновление библиотеки OpenSSL
Метод собирать «из исходников» и засорять систему — не наш метод. Поэтому лучший вариант — найти готовый пакет, и я его нашел в репозитории axivo:
# rpm -ivh --nosignature http://rpm.axivo.com/redhat/axivo-release-6-1.noarch.rpm # yum --enablerepo=axivo update openssl
Проверяем установленную версию OpenSSL:
# openssl version OpenSSL 1.0.1e 11 Feb 2013
Далее необходимо настроить библиотеку на использование алгоритмов ГОСТ, для этого редактируем файл настроек который лежит по адресу /etc/pki/tls/openssl.cnf
, добавив в самое начало файла строку:
openssl_conf = openssl_def
и в самый конец файла:
[openssl_def] engines=engine_section [engine_section] gost=gost_section [gost_section] engine_id=gost default_algorithms=ALL CRYPT_PARAMS=id-Gost28147-89-CryptoPro-A-ParamSet
После внесенных изменений проверяем, видит ли OpenSSL алгоритмы ГОСТ:
#openssl ciphers | tr ":" "\n" | grep GOST GOST2001-GOST89-GOST89 GOST94-GOST89-GOST89
Преобразуем выданный ключ и сертификат в формат, понятный OpenSSL
Для этого нам понадобится Windows машина с установленным CryptoPRO CSP 3.6. Я его слил с сайта разработчика (там дается демонстрационный режим режим на 3 месяца).
Первое что делаем — экспортируем ключевой контейнер в реестр средствами КриптоПро. Для этого открываем КриптоПро из панели управления Windows. Вставляем наш ключевой носитель в компьютер. Для обычной флешки убеждаемся, что у нас среди считывателей на вкладке «Оборудование» есть «Все съемные диски» и «Реестр». Если у вас не обычная флешка а что-то типа РуТокен либо Etoken либо другой носитель — необходимо наличие его драйверов и библиотеки для использования в КриптоПро (Его должно быть видно в разделе «Настроить Считыватели» на той же вкладке).
Далее переходим на вкладку «Сервис» и нажимаем «Копировать», нажимаем «Обзор» и выбираем среди списка свой ключевой контейнер. Если его там вдруг не находим — то возвращаемся к предыдущему параграфу и все проверяем. Далее вводим осмысленное имя нового ключевого контейнера, нажимаем «Готово» и в качестве носителя в появившемся окне выбираем «Реестр». Новый пароль на контейнер не ставим.
Теперь нам нужно установить сертификат в новый контейнер. На вкладке «Сервис» в КриптоПро нажимаем кнопку «Установить личный Сертификат», указываем файл с нашим сертификатом, нажимаем «Далее», выбираем только что созданный нами контейнер и не забываем галочку «Установить сертификат в контейнер».
Для того чтобы экспортировать данный сертификат в формате PKSC#12 вместе с закрытым ключом — нам понадобится сторонняя утилита. Которую можно либо купить здесь либо найти на просторах интернета по имени p12fromgostcsp. Запускаем данную утилиту, выбираем наш сертификат, пароль оставляем пустым и сохраняем его в файл mycert.p12.
Копируем полученный сертификат на linux-машину. Проверяем, что в сертификате у нас есть оба ключа — закрытый и открытый:
# openssl pkcs12 -in mycert.p12 -nodes
На запрос пароля просто нажимаем Enter. И смотрим наличие строк BEGIN CERTIFICATE и BEGIN PRIVATE KEY. Если строки есть то все в порядке. Преобразуем полученный сертификат в формат PEM:
# openssl pkcs12 -in mycert.p12 -out mycert.pem -nodes -clcerts
Если вам нужна не только авторизация по клиентскому сертификату, но и также проверка валидности самого сервера — вам понадобится корневой сертификат удостоверяющего центра. Его можно просто через Windows открыть и сохранить в формате DER в кодировке X.509 (Со вкладки «Состав» при просмотре сертификата), так как закрытого ключа он не содержит. Затем преобразовать его в PEM формат через OpenSSL.
# openssl x509 -inform DER -in cacert.cer -outform PEM -out cacert.pem
Где cacert.cer — имя исходного файла с корневым сертификатом. Проверить коннект с сервером с использованием сертификатов можно через OpenSSL командой:
# openssl s_client -connect service.rosminzdrav.ru:443 -CAfile cacert.pem -cert mycert.pem
В итоге, если все сделано верно, у вас должен получится вот такой вывод в конце:
New, TLSv1/SSLv3, Cipher is GOST2001-GOST89-GOST89 Server public key is 256 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : GOST2001-GOST89-GOST89 Session-ID: *** Session-ID-ctx: Master-Key: *** Key-Arg : None Krb5 Principal: None PSK identity: None PSK identity hint: None SRP username: None Start Time: 1375875984 Timeout : 300 (sec) Verify return code: 0 (ok)
Правка и перекомпиляция PHP
Основная проблема заключается в том, что для того чтобы OpenSSL использовала файл конфигурации по умолчанию (именно там у нас прописаны настройки для алгоритмов ГОСТ) перед ее использованием необходимо вызвать функцию OPENSSL_config(NULL). В расширении PHP OpenSSL этого не сделано, поэтому модуль PHP-SOAP при использовании SSL-соединения не видит алгоритмов ГОСТ. Кстати, тоже самое касается и других библиотек, например curl. Ее тоже нужно патчить если вы собираетесь с ней работать.
Итак приступим. Для того чтобы нам поправить OpenSSL необходимо перекомпилировать весь PHP, так как в CentOS расширение OpenSSL сделано не модулем.
Подготавливаем среду для сборки пакетов:
# yum install rpm-build redhat-rpm-config # mkdir /root/rpmbuild # cd /root/rpmbuild # mkdir BUILD RPMS SOURCES SPECS SRPMS # mkdir RPMS/{i386,i486,i586,i686,noarch,athlon}
Качаем исходники PHP и устанавливаем их:
# wget http://vault.centos.org/6.4/os/Source/SPackages/php-5.3.3-22.el6.src.rpm # rpm -ivh php-5.3.3-22.el6.src.rpm
Переходим в папку SOURCES и извлекаем php, делаем копию папки с иходниками:
# cd SOURCES # tar xvjf php-5.3.3.tar.bz2 # cp php-5.3.3 php-5.3.3p -R
Правим файл php-5.3.3p/ext/openssl/openssl.c
:
Находим строку SSL_library_init();
(у меня это строка № 985) и прописываем перед ней
OPENSSL_config(NULL);
.
Переходим в папку SOURCES и формируем патч:
# diff -uNr php-5.3.3/ php-5.3.3p/ > php-5.3.3-gostfix.patch
В результате получится файл со следующим содержимым:
diff -uNr php-5.3.3/ext/openssl/openssl.c php-5.3.3p/ext/openssl/openssl.c --- php-5.3.3/ext/openssl/openssl.c 2010-06-26 20:03:39.000000000 +0400 +++ php-5.3.3p/ext/openssl/openssl.c 2013-08-07 11:32:41.944581280 +0400 @@ -981,7 +981,7 @@ le_key = zend_register_list_destructors_ex(php_pkey_free, NULL, "OpenSSL key", module_number); le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL, "OpenSSL X.509", module_number); le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL X.509 CSR", module_number); - + OPENSSL_config(NULL); SSL_library_init(); OpenSSL_add_all_ciphers(); OpenSSL_add_all_digests();
Теперь нужно заставить сборщик использовать наш патч. Правила сборки описаны в файле SPEC/php.spec. Открываем его и после строки описания патчей (у меня это строка начинающаяся на Patch230) вставляем строку с описанием нового патча:
Patch231: php-5.3.3-gostfix.patch
Также прописываем вызов данного патча перед сборкой, для этого ищем строки начинающиеся на %patch и в конце них прописываем
%patch231 -p1
Сохраняем файл. Перед сборкой пакета ставим все зависимости для сборки:
# yum install bzip2-devel db4-devel gmp-devel httpd-devel pam-devel sqlite-devel pcre-devel libedit-devel libtool-ltdl-devel libc-client-devel cyrus-sasl-devel openldap-devel mysql-devel postgresql-devel libxml2-devel net-snmp-devel libxslt-devel libxml2-devel libXpm-devel libpng-devel freetype-devel libtidy-devel aspell-devel recode-devel libicu-devel enchant-devel net-snmp
Далее приступаем к сборке, из папки rpmbuld запускаем команду:
rpmbuild -ba SPECS/php.spec
Сборка занимает продолжительное время, и в случае если все прошло хорошо, в папке RPMS/{Ваша_архитектура} появятся готовые rpm_пакеты.
Для архитектуры x86_64:
# ls RPMS/x86_64/ php-5.3.3-22.el6.x86_64.rpm php-devel-5.3.3-22.el6.x86_64.rpm php-intl-5.3.3-22.el6.x86_64.rpm php-pgsql-5.3.3-22.el6.x86_64.rpm php-tidy-5.3.3-22.el6.x86_64.rpm php-bcmath-5.3.3-22.el6.x86_64.rpm php-embedded-5.3.3-22.el6.x86_64.rpm php-ldap-5.3.3-22.el6.x86_64.rpm php-process-5.3.3-22.el6.x86_64.rpm php-xml-5.3.3-22.el6.x86_64.rpm php-cli-5.3.3-22.el6.x86_64.rpm php-enchant-5.3.3-22.el6.x86_64.rpm php-mbstring-5.3.3-22.el6.x86_64.rpm php-pspell-5.3.3-22.el6.x86_64.rpm p hp-xmlrpc-5.3.3-22.el6.x86_64.rpm php-common-5.3.3-22.el6.x86_64.rpm php-fpm-5.3.3-22.el6.x86_64.rpm php-mysql-5.3.3-22.el6.x86_64.rpm php-recode-5.3.3-22.el6.x86_64.rpm php-zts-5.3.3-22.el6.x86_64.rpm php-dba-5.3.3-22.el6.x86_64.rpm php-gd-5.3.3-22.el6.x86_64.rpm php-odbc-5.3.3-22.el6.x86_64.rpm php-snmp-5.3.3-22.el6.x86_64.rpm php-debuginfo-5.3.3-22.el6.x86_64.rpm php-imap-5.3.3-22.el6.x86_64.rpm php-pdo-5.3.3-22.el6.x86_64.rpm php-soap-5.3.3-22.el6.x86_64.rpm
Теперь можем установить все это «добро» поверх уже установленного php с заменой:
rpm -Uvh --replacepkgs --replacefiles RPMS/x86_64/*
Далее проверяем работу php-soap. Для примера можно использовать такой код (пример для сервиса «Федеральный регистр медработников»):
<?php class GetEmployees { public $ogrn; } $cert="/mycert.pem"; //Сертификат $wsdl="https://service.rosminzdrav.ru/MedStaffIntegration/MedStaff.svc?wsdl"; //Адрес wdsl сервиса $loc = "https://service.rosminzdrav.ru/MedStaffIntegration/medstaff.svc/basic"; //Адрес точки доступа $sp = new SoapClient($wsdl,array( 'local_cert' => $cert, 'trace' => 1, 'exceptions' => 1, 'soap_version' => SOAP_1_1, 'location' =>$loc, )); $emp = new GetEmployees; $emp->ogrn = '1022303554570'; try{ $data = $sp->GetEmployees($emp); print_r($data); } catch (SoapFault $e) { echo "<h2>Exception Error!</h2>"; echo $sp->__getLastRequest(); echo get_class($e); echo $e->getMessage(); }
Если все прошло успешно никаких ошибок не возникнет и PHP-SOAP без проблем заработает.
Далее если необходимо можно также подправить библиотеку curl, сделать проверку сертификата сервера и вообще все операции которые допустимы для SSL-соединений вплоть до поднятия своего веб сервера с авторизацией по сертификатам с алгоритмами ГОСТ.
ссылка на оригинал статьи http://habrahabr.ru/post/189352/
Добавить комментарий