📜 ⬆️ ⬇️

Interaction of php-soap on linux with certificate authorization using GOST algorithms

I came across cryptography earlier, I had to deploy a certificate authority at CryptoPro in due time, so I had some general ideas about what private and public keys and certificates were, but there wasn’t much about how it all worked in Linux.
The task was to ensure interaction with the RosMinzdrav services, namely, with the federal register of health workers using the SOAP protocol. On the client side, the system on CentOS and running services on PHP, on the server side, is a SOAP service with certificate authorization using GOST algorithms. In the presence was a flash drive with a private key, formed by the certifying center RosMinzdzdrava and certificate of this key.
After analyzing the situation on the use of GOST encryption algorithms in the Linux world, it was found out that in recent years there has been a good move forward, but all is not quite all right. So, in order to make the php-soap extension transparently understand the GOST algorithms, as well as use the certificates and keys issued by the RosMinzdrav, you need to do the following:

1. Update the OpenSSL library in the distribution package to version not lower than 1.0.1c and configure the support for GOST.
2. Convert the issued key and certificate into a format that OpenSSL understands. Test OpenSSL operation.
3. Correct the OpenSSL extension in PHP and recompile PHP itself. Test SOAP for PHP.

So let's get started. Data distribution, installed php and openssl library:
')
# 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 

Update OpenSSL Library

The method of collecting “from source” and clogging up the system is not our method. Therefore, the best option is to find a complete package, and I found it in the axivo repository:
 # rpm -ivh --nosignature http://rpm.axivo.com/redhat/axivo-release-6-1.noarch.rpm # yum --enablerepo=axivo update openssl 

Check the installed version of OpenSSL:
 # openssl version OpenSSL 1.0.1e 11 Feb 2013 

Next, you need to configure the library to use GOST algorithms, for this we edit the settings file which is located at /etc/pki/tls/openssl.cnf , adding the line to the very beginning of the file:
 openssl_conf = openssl_def 

and at the very end of the file:
 [openssl_def] engines=engine_section [engine_section] gost=gost_section [gost_section] engine_id=gost #!!     dynamic_path=/usr/lib64/openssl/engines/libgost.so default_algorithms=ALL CRYPT_PARAMS=id-Gost28147-89-CryptoPro-A-ParamSet 

After the changes are made, we check if OpenSSL sees the GOST algorithms:
 #openssl ciphers | tr ":" "\n" | grep GOST GOST2001-GOST89-GOST89 GOST94-GOST89-GOST89 

Convert the issued key and certificate into a format that OpenSSL understands

To do this, we need a Windows machine with the installed CryptoPRO CSP 3.6. I merged it from the developer’s site (there is a demo mode for 3 months).
The first thing we are doing is exporting the key container to the registry using CryptoPro. To do this, open CryptoPro from the Windows Control Panel. We insert our key carrier into the computer. For a regular flash drive, we make sure that we have “All removable drives” and “Registry” among the readers in the “Hardware” tab. If you do not have a regular USB flash drive, but something like RuToken or Etoken or another medium, you must have its drivers and library for use in CryptoPro (It should be visible in the section “Configure Readers” on the same tab).

Next, go to the “Service” tab and click “Copy”, click “Browse” and select your key container from the list. If we don’t find him there, we return to the previous paragraph and check everything. Next, enter the meaningful name of the new key container, click "Finish" and select "Registry" as the carrier in the appeared window. We do not set a new password for the container.
Now we need to install the certificate in a new container. On the “Service” tab in CryptoPro, click the “Set personal certificate” button, specify the file with our certificate, click “Next”, select the container we just created and do not forget the checkbox “Install certificate in container”.
In order to export this certificate in PKSC # 12 format with a private key, we will need a third-party utility. You can either buy it here or find it on the Internet called p12fromgostcsp. Run this utility, select our certificate, leave the password empty and save it in the file mycert.p12.
We copy the received certificate on the linux-machine. We check that in the certificate we have both keys - closed and open:
 # openssl pkcs12 -in mycert.p12 -nodes 

At the password prompt, just press Enter. And we look for the presence of the lines BEGIN CERTIFICATE and BEGIN PRIVATE KEY. If the line is there, then everything is fine. We convert the received certificate into the PEM format:
 # openssl pkcs12 -in mycert.p12 -out mycert.pem -nodes -clcerts 

If you need not only authorization by a client certificate, but also verification of the validity of the server itself, you will need a root certificate from the certification authority. You can simply open it through Windows and save it in DER format in X.509 encoding (from the “Composition” tab when viewing the certificate), since it does not contain the private key. Then convert it to PEM format via OpenSSL.
 # openssl x509 -inform DER -in cacert.cer -outform PEM -out cacert.pem 

Where cacert.cer is the name of the source file with the root certificate. You can check the connection with the server using certificates using the OpenSSL command:
 # openssl s_client -connect service.rosminzdrav.ru:443 -CAfile cacert.pem -cert mycert.pem 

In the end, if everything is done correctly, you should get this conclusion at the end:
 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) 

Editing and recompiling PHP

The main problem is that in order for OpenSSL to use the default configuration file (this is where we set up the settings for the GOST algorithms), before using it, you must call the OPENSSL_config (NULL) function. This is not done in the PHP OpenSSL extension, so the PHP-SOAP module does not see the GOST algorithms when using an SSL connection. By the way, the same applies to other libraries, for example curl. She also needs to be patched if you are going to work with her.
So let's get started. In order for us to fix OpenSSL, it is necessary to recompile all PHP, since in CentOS the OpenSSL extension is not made by module.
Prepare the environment for building packages:
 # 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} 

We download PHP source and install them:
 # 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 

Go to the folder SOURCES and extract php, make a copy of the folder with ihodnikami:
 # cd SOURCES # tar xvjf php-5.3.3.tar.bz2 # cp php-5.3.3 php-5.3.3p -R 

Edit the file php-5.3.3p/ext/openssl/openssl.c :
Find the string SSL_library_init(); (I have this line number 985) and prescribe it
OPENSSL_config(NULL); .
Go to the folder SOURCES and form the patch:
 # diff -uNr php-5.3.3/ php-5.3.3p/ > php-5.3.3-gostfix.patch 

The result is a file with the following contents:
 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(); 

Now we need to force the collector to use our patch. The build rules are described in the SPEC / php.spec file. Open it and after the patch description line (I have a line beginning with Patch230) insert the line with the description of the new patch:
 Patch231: php-5.3.3-gostfix.patch 

We also prescribe a call to this patch before building, for this we search for lines starting at% patch and at the end of them we prescribe
 %patch231 -p1 

Save the file. Before assembling the package we put all the dependencies for the assembly:
 # 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 

Next, proceed to the assembly, from the rpmbuld folder, run the command:
 rpmbuild -ba SPECS/php.spec 

The build takes a long time, and if everything went well, ready-made rpm packages will appear in the RPMS / {Your_architecture} folder.
For x86_64 architecture:
 # 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 

Now we can install all this “good” over the already installed php with the replacement:
 rpm -Uvh --replacepkgs --replacefiles RPMS/x86_64/* 

Next, check the operation of php-soap. For example, you can use the following code (example for the Federal Register of Medical Workers service):
 <?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(); } 

If everything went well, no errors will occur and PHP-SOAP will work without problems.
Then, if necessary, you can also correct the curl library, check the server certificate and generally all operations that are valid for SSL connections, up to raising your web server with certificate authorization using GOST algorithms.

Source: https://habr.com/ru/post/189352/


All Articles