In this article, I will talk about how I solved the problem of integration in test mode with services that work using algorithms defined by GOST R 34.10-2001 (outdated) and GOST R 34.10-2012 . I will give examples of some of the problems I encountered when solving a problem, give references to a ready-made solution and show several examples of their use.
For a start it is worth saying that for the production environment there are certified funds from the companies Cryptocom, Crypto-Pro, Signal-KOM and others. I also needed to implement the interaction in the test (developer) environment on Linux, buying licenses for this is not very convenient, but what's worse, there is no public documentation on this issue. On the request "https gost" not so many solutions. Among them, the use of OpenSSL + Crypto CSP is mentioned, I don’t remember the details, but I didn’t have this bundle with the support of GOST R 34.10-2012. Another result that was often encountered was the proposal to use OpenSSL 1.0, in which the GOST-engine is embedded inside, but this solution also did not contain support for GOST2012-GOST8912-GOST8912
.
The working solution turned out to be the OpenSSL 1.1.0g + build taken out in the separately connected dynamic GOST-engine . There is often a mention on the Internet that a certain Russian company has made efforts to develop it, but the repository itself does not contain information about the authors of the product. Taking this opportunity, I thank the authors for the engine in open source. This page says that before OpenSSL 1.1.0 the built-in GOST engine was used, now for GOST a third-party product from OpenSSL is used. Judging by the configuration, most likely, this is the same library.
Building a third-party product for those who rarely do this can be a non-trivial task. To build OpenSSL, GOST-engine and cURL I had to deal with a bunch of options and try several combinations of versions. If you notice strange things in the Dockerfile, then most likely it remains from such experiments.
I fixed all versions of the projects for the assembly, in order to eliminate the situation that something would stop working due to the update. For example, I collected the OpenSSL 1.1.0h + GOST-engine and the openssl ciphers
command did not contain GOST-algorithms, although the openssl engine
showed the GOST-engine in the list. Having specified the previous version of OpenSSL 1.1.0g, everything worked as expected. It was very strange, I repeated it again, then I tried to find out the reasons , but in the end I decided to stay at 1.1.0g.
Collecting the GOST-engine itself, I did not immediately notice the presence of the INSTALL.md
file in the master branch, because I collected openssl_1_1_0
from a branch from where the documentation was taken. That version was built with the custom build of the OpenSSL team
cmake -DCMAKE_C_FLAGS='-I/usr/local/ssl/include -L/usr/local/ssl/lib' ..
But the master branch stopped DOPENSSL_ROOT_DIR
like this, there were errors about the absence of DOPENSSL_ROOT_DIR
and the like. As a result, the solution was found and fixed.
There are a lot more documentation to build cURL with a custom build of OpenSSL, however, the documentation was outdated and I had to experiment a lot with options.
The result of the work posted here .
The image is launched in the Docker Hub .
I will not go into much detail about working with docker images, I’ll just give one command to start working inside the image:
docker run --rm -i -t rnix/openssl-gost bash
For a start, you can make sure that the algorithms GOST2012-GOST8912-GOST8912
and GOST2001-GOST89-GOST89
are in the list of supported:
openssl ciphers
If you know a host that uses HTTPS based on GOST algorithms, you can see its certificate and try to connect using the command:
openssl s_client -connect gost.example.com:443 -showcerts
Create a private key and certificate in accordance with GOST R 34.10-2012:
openssl req -x509 -newkey gost2012_256 -pkeyopt paramset:A -nodes -keyout key.pem -out cert.pem
Sign the file with previously created certificates:
openssl cms -sign -signer cert.pem -inkey key.pem -binary -in file.txt -nodetach -outform DER -nocerts -noattr -out signed.sgn
Extract the contents of the signed file with a certificate that was signed by itself:
openssl cms -verify -in signed.sgn -certfile cert.pem -CAfile cert.pem -inform der -out data.txt
With the signatures of the certificates themselves, everything is a little more complicated. So far I have not come across a service for which the certificate is issued by a trusted certificate authority. Usually, it is required to additionally indicate the certificate of the certificate authority when working with the certificates issued to them. This can be done globally for the system (in the image), or explicitly specified in each command.
The cURL program has not changed in use; it simply supports hosts with GOST certificates.
If a programming language allows you to execute programs installed in the system, then the task of using GOST algorithms is most easily accomplished by copying the binaries of the openssl and curl compiled at the end of the Dockerfile programming language using multi-stage build . For example:
FROM rnix/openssl-gost AS openssl-gost # Replace with any other image based on Debian x86_64 FROM debian:stretch-slim COPY --from=openssl-gost /usr/local/ssl /usr/local/ssl COPY --from=openssl-gost /usr/local/ssl/bin/openssl /usr/bin/openssl COPY --from=openssl-gost /usr/local/curl /usr/local/curl COPY --from=openssl-gost /usr/local/curl/bin/curl /usr/bin/curl COPY --from=openssl-gost /usr/local/bin/gostsum /usr/local/bin/gostsum COPY --from=openssl-gost /usr/local/bin/gost12sum /usr/local/bin/gost12sum
It is not even necessary to copy to /usr/bin
, this can be done in any directory, and then called from your program, passing the full path and all the arguments.
PHP, of course, allows you to make calls to system commands using, for example, exec
. But, looking at how PHP-FPM is going to be in Dockerfile, it seemed to me that you can easily build PHP with custom builds of OpenSSL and cURL. As it turned out, I was mistaken that it was easy. I still collected.
For some reason, I started with PHP-FPM 7.1. The idea was to use multi-stage build. To do this, replace the FROM
instruction in their Dockerfile with my FROM rnix/openssl-gost AS openssl-gost
, then write the copies of the collected openssl and curl before starting the php build itself, and finally, specify the path to these libraries in the --with-openssl-dir=/usr/local/ssl
and --with-curl=/usr/local/curl
replacing the original ones.
Surprises waited from everywhere. One of the most significant was that when building php 7.1, pkg-config is used, and the explicit installation of libcurl4-openssl-dev, libssl-dev was prescribed in the pkg-config version of the packages. As a result, not what was needed was collected. If you remove their installation, then /configure
php fell, citing the absence of openssl in pkg-config. I had to additionally copy from the custom openssl and curl assemblies their lib/pkgconfig /*
. After dozens of such surprises, the assembly began to pass. Then it turned out that the dependencies installed openssl, thus overwriting the previously copied binary of my custom build. I had to additionally copy it at the very end. But that's not all.
openssl_get_md_methods()
hashes algorithms appeared in the assembled php: GOST R 34.11-2012 with 256 bit hash
, GOST R 34.11-2012 with 512 bit hash
, GOST R 34.11-94
. This meant that php connected the GOST-engine. But the use of the curl extension in php for some reason said that it does not know such algorithms, connecting to the host via GOST-HTTPS. I spent several hours trying to find the reason for this. I watched the source, how the curl extension in php is arranged, how the curl itself works, how they communicate with openssl. I was looking for a place where supported algorithms can be defined or engines can connect. I searched on master branches, googled a lot, but did not find anything that would solve the problem right away. Then I remembered that I was not collecting the latest version of PHP.
I tried to build PHP-FPM 7.2. I had to make some changes to my script for adjusting the original Dockerfile PHP-FPM, but the build started to go through without a lot of surprises. The main news was that now the curl extension inside php was able to communicate with hosts in accordance with GOST algorithms, but there was one nuisance. Each php call wrote to stdout GOST engine already loaded
. Very unpleasant. I did not immediately understand who was doing this, but I found such a line in the GOST-engine source code. I still do not know in which part of the system the error occurred: php, php-curl, curl, openssl. But, apparently, in php 7.1 php-curl did not connect the engine at all, now in php 7.2 it began to connect it twice. Since everything worked correctly, only there was a conclusion, I decided to remove it by editing the source code in the Dockerfile instruction:
sed -i 's|printf("GOST engine already loaded\\n");|goto end;|' gost_eng.c
There, the line below already has a goto end;
, so I did not do anything serious. But, having rebuilt the whole zoo, everything began to work, as I wanted.
The image with PHP-FPM + OpenSSL + GOST + cURL is launched in Docker Hub .
The ability to work from programming languages is already a lot, but I also wanted two more possibilities:
Easy - this means docker image. It needs to be created. To do this, you need to build a nginx with a custom OpenSSL and GOST-engine in the Dockerfile. Having opened the nginx build documentation , I saw one option about ssl - --with-http_ssl_module
, which is just boolean. But nginx is a popular product, there are a lot of instructions for building with openssl, so I found another option --with-openssl=[DIR]
. As practice has shown, nginx wants openssl sources to be here, and the nginx scripts will take care of building themselves. This is not exactly what I would like (I would like to use a multi-stage build). I got acquainted with the help-output of the nginx collector, but I could not find anything that would help me there.
I had to duplicate the instructions for downloading the OpenSSL sources, unpacking, assembling the GOST-engine, including the GOST-engine in the configs. All this started to come together, but nginx still lacked support for GOST algorithms. I checked this with the indication in the config ssl_ciphers GOST2001-GOST89-GOST89:HIGH:MEDIUM;
. Running nginx -t
said that it does not know this algorithm.
As it turned out, the openssl compiled by nginx did not support dynamic engines, i.e. ./Configure
output no-dynamic-engine [forced]
. Here I had to carefully read the OpenSSL build documentation to find out what forced
put down. The reason was found in the openssl build arguments that nginx called, namely no-shared
. If this is specified, then there are no flags to enable support for loading engines. I had to edit the assembly instructions:
sed -i 's|--prefix=$ngx_prefix no-shared|--prefix=$ngx_prefix|' auto/lib/openssl/make
All this has gathered, but nginx started swearing that it could not find gost.so
along the path /usr/lib/x86_64-linux-gnu/
, which is rather strange, because the same assembled openssl is looking for and finds engines in a completely different place, exactly where it was going ./lib/engines-1.1
. Added instructions for copying compiled engines to /usr/lib/x86_64-linux-gnu/
, to please nginx. It worked.
Next to the main Dockerfile in the repository, I put a demo Dockerfile , which, during assembly, creates GOST certificates for itself and uses them, processing connections to https://gost.example.com . We'll have to work with DNS or docker network to try to connect to this demo from one container, but I described all this in the documentation .
The demo host uses the gost2001
keys, other options are gost2012_256
, gost2012_512
. And instead of GOST2001-GOST89-GOST89
- GOST2012-GOST8912-GOST8912
.
The image with nginx + GOST is launched in the Docker Hub: https://hub.docker.com/r/rnix/nginx-gost/
I have studied the problem of working with GOST-algorithms in Linux systems, provided a solution in the form of docker-images, all this is accompanied by documentation and examples. The solution is in the form of a repository on GitHub.
It should be said about the safety of using such a solution. The main thing is not to trust the images on the Docker Hub, even if it says Automated Build
. I can still collect an image with any edits of all used libraries and systems and push it into my Docker Hub under any tag. Therefore, I recommend to fork the repository on the githaba, pull it myself and assemble it yourself, checking the instructions in the Dockerfile for the fact that only official resources are used without suspicious modification during the build.
By assembling the image yourself, you can make sure that malicious edits do not get into the code, because the assembly occurs only from open source, which is available for viewing to everyone. However, this does not guarantee that there are no bugs and vulnerabilities in it. Using proprietary certified tools also does not guarantee the absence of errors and vulnerabilities, but besides their code is closed from you.
Source: https://habr.com/ru/post/353534/
All Articles