шифрование ГОСТ на php и электронные больничные ФСС
Возникла задача - добавить в нашу МИС интеграцию с сервисом ФСС по обработке электронных больничных - здесь описание.
МИСка наша крутится на сервере с линуксом. Реализовать взаимодействие с сервисом оказалось в итоге не просто.
Опытом своим хочу поделиться
Возникла задача - добавить в нашу МИС интеграцию с сервисом ФСС по обработке электронных больничных - здесь описание.МИСка наша крутится на сервере с линуксом. Реализовать взаимодействие с сервисом оказалось в итоге не просто.
Опытом своим хочу поделиться
МИСка наша крутится на сервере с линуксом. Реализовать взаимодействие с сервисом оказалось в итоге не просто.
Опытом своим хочу поделиться
Статья пока незакончена, в процессе....
У меня сервер на CENTOS достаточно старенький конечно - ну что поделать, так исторически сложилось©, работать то надо.
1 шаг. Установка Крипто-Про
Скачиваем и устанавливаем Крипто-Про
У меня лицензия на 4-ю версию, поэтому берем ее - https://www.cryptopro.ru/products/csp/downloads#latest_csp40r4_linux
Выбор пал на КриптоПро CSP 4.0 для Linux (x64, rpm) https://www.cryptopro.ru/sites/default/files/private/csp/40/9963/linux-amd64.tgz
Также нам понадобится КриптоПро ЭЦП SDK 2.0 (нам нужна поддержка ГОСТ-2012 - в более ранних версиях ее нет) https://www.cryptopro.ru/products/cades/downloads
выбор опять же пал на - Linux 64 бита https://www.cryptopro.ru/sites/default/files/products/cades/current_release_2_0/cades_linux_amd64.tar.gz
Также на всякий случай лучше запастись stunnel-msspi https://www.cryptopro.ru/products/other/stunnel-msspi, нам он понадобился в следующем проекте с МДЛП о котором я расскажу вот здесь (типа ссылка тут будет)
Закидываем скаченные архивы на сервер, распаковываем - там куча rpm-ок
[root@bars srv]# tar -xf linux-amd64.tgz [root@bars srv]# cd linux-amd64 [root@bars linux-amd64]# ls -la total 21360 drwxr-xr-x 2 nobody nobody 4096 Nov 23 2018 . drwxr-xr-x. 4 root root 4096 Jun 5 11:23 .. -rw-r--r-- 1 nobody nobody 2203 Nov 23 2018 cprocsp-compat-altlinux-64-1.0.0-1.noarch.rpm -rw-r--r-- 1 nobody nobody 3202690 Nov 23 2018 cprocsp-cpopenssl-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 12244 Nov 23 2018 cprocsp-cpopenssl-base-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 323564 Nov 23 2018 cprocsp-cpopenssl-devel-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 39404 Nov 23 2018 cprocsp-cpopenssl-gost-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 417875 Nov 23 2018 cprocsp-curl-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 2089134 Nov 23 2018 cprocsp-drv-64-4.0.9963-5.src.rpm -rw-r--r-- 1 nobody nobody 2132178 Nov 23 2018 cprocsp-drv-devel-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 84568 Nov 23 2018 cprocsp-ipsec-devel-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 170341 Nov 23 2018 cprocsp-ipsec-esp-64-4.0.9963-5.src.rpm -rw-r--r-- 1 nobody nobody 33792 Nov 23 2018 cprocsp-ipsec-genpsk-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 89975 Nov 23 2018 cprocsp-ipsec-ike-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 18558 Nov 23 2018 cprocsp-rdr-emv-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 58823 Nov 23 2018 cprocsp-rdr-esmart-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 30293 Nov 23 2018 cprocsp-rdr-gui-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 49544 Nov 23 2018 cprocsp-rdr-gui-gtk-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 15854 Nov 23 2018 cprocsp-rdr-infocrypt-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 33521 Nov 23 2018 cprocsp-rdr-inpaspot-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 466398 Nov 23 2018 cprocsp-rdr-jacarta-64-3.6.408.695-4.x86_64.rpm -rw-r--r-- 1 nobody nobody 12460 Nov 23 2018 cprocsp-rdr-mskey-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 12706 Nov 23 2018 cprocsp-rdr-novacard-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 45655 Nov 23 2018 cprocsp-rdr-pcsc-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 13911 Nov 23 2018 cprocsp-rdr-rosan-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 47162 Nov 23 2018 cprocsp-rdr-rutoken-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 144178 Nov 23 2018 cprocsp-rsa-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 134583 Nov 23 2018 cprocsp-stunnel-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 49816 Nov 23 2018 cprocsp-xer2print-4.0.9963-5.noarch.rpm -rwxr-xr-x 1 nobody nobody 301229 Nov 23 2018 cpverify -rw-r--r-- 1 nobody nobody 63820 Nov 23 2018 ifd-rutokens-1.0.1-1.x86_64.rpm -rwxr-xr-x 1 nobody nobody 211 Nov 23 2018 install.desktop -rwxr-xr-x 1 nobody nobody 20617 Nov 23 2018 install_gui.sh -rwxr-xr-x 1 nobody nobody 10548 Nov 23 2018 install.sh -rwxr-xr-x 1 nobody nobody 6718 Nov 23 2018 integrity.sh -rw-r--r-- 1 nobody nobody 88 Nov 23 2018 linux-amd64.ini -rw-r--r-- 1 nobody nobody 186031 Nov 23 2018 lsb-cprocsp-base-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 11667 Nov 23 2018 lsb-cprocsp-ca-certs-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 6755056 Nov 23 2018 lsb-cprocsp-capilite-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 1197101 Nov 23 2018 lsb-cprocsp-devel-4.0.9963-5.noarch.rpm -rw-r--r-- 1 nobody nobody 720274 Nov 23 2018 lsb-cprocsp-kc1-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 1015425 Nov 23 2018 lsb-cprocsp-kc2-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 192800 Nov 23 2018 lsb-cprocsp-pkcs11-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 1478332 Nov 23 2018 lsb-cprocsp-rdr-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 8682 Nov 23 2018 lsb-cprocsp-rdr-accord-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 10722 Nov 23 2018 lsb-cprocsp-rdr-ancud-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 13518 Nov 23 2018 lsb-cprocsp-rdr-crypton-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 8297 Nov 23 2018 lsb-cprocsp-rdr-maxim-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 8719 Nov 23 2018 lsb-cprocsp-rdr-sobol-64-4.0.9963-5.x86_64.rpm -rw-r--r-- 1 nobody nobody 10814 Nov 23 2018 sobol-1-7.src.rpm -rwxr-xr-x 1 nobody nobody 3574 Nov 23 2018 uninstall.shну что, шашки наголо и в атаку!
[root@bars linux-amd64]# ./install.sh Uninstalling CSP packages... CSP packages have been successfully uninstalled Installing lsb-cprocsp-base-4.0.9963-5.noarch.rpm ... error: Failed dependencies: lsb-core-noarch >= 3.0 is needed by lsb-cprocsp-base-4.0.9963-5.noarch Error: installation failed. LSB package may not be installed. Install LSB package and reinstall CryptoPro CSP. If it does not help, please read installation documentation or contact the manufacturer: support@cryptopro.ru.Ха, не так все просто (а кто вообще обещал что будет легко?)
Ну давай, потанцуем
[root@bars linux-amd64]# yum install lsb-core-noarch ... тут куча всякого, в итоге Installed: redhat-lsb-core.x86_64 0:4.0-7.el6.centos Dependency Installed: at.x86_64 0:3.1.10-49.el6 bc.x86_64 0:1.06.95-1.el6 cvs.x86_64 0:1.11.23-16.el6 ed.x86_64 0:1.1-3.3.el6 gettext.x86_64 0:0.17-18.el6 mailx.x86_64 0:12.4-10.el6_10 patch.x86_64 0:2.6-8.el6_9 pax.x86_64 0:3.4-10.1.el6 perl-CGI.x86_64 0:3.51-144.el6 perl-Test-Simple.x86_64 0:0.92-144.el6 time.x86_64 0:1.7-38.el6 Dependency Updated: perl.x86_64 4:5.10.1-144.el6 perl-CPAN.x86_64 0:1.9402-144.el6 perl-Digest-SHA.x86_64 1:5.47-144.el6 perl-ExtUtils-MakeMaker.x86_64 0:6.55-144.el6 perl-ExtUtils-ParseXS.x86_64 1:2.2003.0-144.el6 perl-Module-Pluggable.x86_64 1:3.90-144.el6 perl-Pod-Escapes.x86_64 1:1.04-144.el6 perl-Pod-Simple.x86_64 1:3.13-144.el6 perl-Test-Harness.x86_64 0:3.17-144.el6 perl-devel.x86_64 4:5.10.1-144.el6 perl-libs.x86_64 4:5.10.1-144.el6 perl-version.x86_64 3:0.77-144.el6 Complete! [root@bars linux-amd64]#Ну будем надеятся что ничего не поломается - вон сколько всего понаобновлял... :)
Второй заход...
[root@bars linux-amd64]# ./install.sh Uninstalling CSP packages... CSP packages have been successfully uninstalled Installing lsb-cprocsp-base-4.0.9963-5.noarch.rpm ... Installing lsb-cprocsp-rdr-64-4.0.9963-5.x86_64.rpm ... Adding new reader: Nick name: FLASH Name device: FLASH Succeeded, code:0x0 Installing lsb-cprocsp-kc1-64-4.0.9963-5.x86_64.rpm ... Installing lsb-cprocsp-capilite-64-4.0.9963-5.x86_64.rpm ... Installing cprocsp-curl-64-4.0.9963-5.x86_64.rpm lsb-cprocsp-ca-certs-4.0.9963-5.noarch.rpm ... CSP packages have been successfully installedОсновные каталоги в которых понадобится полазить
[root@bars linux-amd64]# ls -la /etc/opt/cprocsp/ total 40 drwxr-xr-x 2 root root 4096 Jun 5 11:33 . drwxr-xr-x. 3 root root 4096 Jun 5 11:33 .. -rw-r--r-- 1 root root 17703 Jun 5 11:33 config64.ini -rw-r--r-- 1 root root 255 Jun 5 11:33 license.ini -rw-r--r-- 1 root root 33 Nov 22 2018 release -rwxr-xr-x 1 root root 1206 Nov 22 2018 stunnel.conf [root@bars linux-amd64]# ls -la /var/opt/cprocsp/ total 32 drwxr-xr-x 8 root root 4096 Jun 5 11:33 . drwxr-xr-x. 3 root root 4096 Jun 5 11:33 .. drwxr-xr-x 4 root root 4096 Jun 5 11:33 dsrf drwxrwxrwt 3 root root 4096 Jun 5 11:33 keys drwx--x--x 10 root root 4096 Jun 5 11:33 mnt drwxrwxrwt 2 root root 4096 Jun 5 11:33 tmp drwxr-xr-x 4 root root 4096 Jun 5 11:33 tmpcerts drwxrwxrwt 4 root root 4096 Jun 5 11:33 users [root@bars linux-amd64]# ls -la /opt/cprocsp/ total 24 drwxr-xr-x 6 root root 4096 Jun 5 11:33 . drwxr-xr-x. 8 root root 4096 Jun 5 11:33 .. drwxr-xr-x 3 root root 4096 Jun 5 11:33 bin drwxr-xr-x 4 root root 4096 Jun 5 11:33 lib drwxr-xr-x 3 root root 4096 Jun 5 11:33 sbin drwxr-xr-x 4 root root 4096 Jun 5 11:33 share2 шаг. Устанавливаем SDK
[root@bars srv]# tar -xf cades_linux_amd64.tar.gz [root@bars srv]# cd cades_linux_amd64 [root@bars cades_linux_amd64]# ls -la total 31880 drwxr-xr-x 2 meksik games 4096 Sep 19 2019 . drwxr-xr-x. 5 root root 4096 Jun 5 11:53 .. -rw------- 1 meksik games 13744817 Sep 17 2019 cprocsp-pki-2.0.0-amd64-cades.rpm -rw------- 1 meksik games 307637 Sep 17 2019 cprocsp-pki-2.0.0-amd64-phpcades.rpm -rw------- 1 meksik games 812649 Sep 17 2019 cprocsp-pki-2.0.0-amd64-plugin.rpm -rw------- 1 meksik games 13727006 Sep 17 2019 cprocsp-pki-cades_2.0.0-1_amd64.deb -rw------- 1 meksik games 302540 Sep 17 2019 cprocsp-pki-phpcades_2.0.0-1_amd64.deb -rw------- 1 meksik games 804876 Sep 17 2019 cprocsp-pki-plugin_2.0.0-1_amd64.deb -rw-r--r-- 1 meksik games 212 Sep 19 2019 ._.DS_Store -rw-r--r-- 1 meksik games 6148 Sep 19 2019 .DS_Store -rw------- 1 meksik games 212 Aug 13 2019 ._lsb-cprocsp-devel_5.0.11535-4_all.deb -rw------- 1 meksik games 1442962 Aug 13 2019 lsb-cprocsp-devel_5.0.11535-4_all.deb -rw------- 1 meksik games 212 Aug 13 2019 ._lsb-cprocsp-devel-5.0.11535-4.noarch.rpm -rw------- 1 meksik games 1455146 Aug 13 2019 lsb-cprocsp-devel-5.0.11535-4.noarch.rpm [root@bars cades_linux_amd64]#нам нужно установить cprocsp-pki-2.0.0-amd64-cades.rpm, cprocsp-pki-2.0.0-amd64-phpcades.rpm и lsb-cprocsp-devel-5.0.11535-4.noarch.rpm
[root@bars cades_linux_amd64]# rpm -i cprocsp-pki-2.0.0-amd64-cades.rpm License 0A202-U0030-00ECW-RRLMF-UU2WK is set [ErrorCode: 0x00000000] License TA200-G0030-00ECW-RRLNE-BTDVV is set [ReturnCode: 0x00000000] [root@bars cades_linux_amd64]# rpm -i cprocsp-pki-2.0.0-amd64-phpcades.rpm [root@bars cades_linux_amd64]#Если устанавливать rpm-ку от Крипто-Про 4, lsb-cprocsp-devel-4.0.9963-5.noarch.rpm, то она не даст собрать расширение, надо устанавливать именно ту которая во втором архиве, но она просит зависимость - КриптоПро 5, потому указываем игнорировать зависимость
[root@bars cades_linux_amd64]# rpm -i lsb-cprocsp-devel-5.0.11535-4.noarch.rpm error: Failed dependencies: lsb-cprocsp-base >= 5.0 is needed by lsb-cprocsp-devel-5.0.11535-4.noarch [root@bars cades_linux_amd64]# rpm -i --nodeps lsb-cprocsp-devel-5.0.11535-4.noarch.rpm3 шаг Сборка PHP расширения
смотрим какая версия php установлена, и смотрим установлен ли пакет devel чтобы мы могли собрать расширение phpcades
[root@bars cades_linux_amd64]# yum list installed |grep php cprocsp-pki-phpcades.x86_64 2.0.0-1 installed php56w.x86_64 5.6.40-1.w6 @webtatic php56w-cli.x86_64 5.6.40-1.w6 @webtatic php56w-common.x86_64 5.6.40-1.w6 @webtatic php56w-devel.x86_64 5.6.40-1.w6 @webtatic php56w-gd.x86_64 5.6.40-1.w6 @webtatic php56w-imap.x86_64 5.6.40-1.w6 @webtatic php56w-ldap.x86_64 5.6.40-1.w6 @webtatic php56w-mbstring.x86_64 5.6.40-1.w6 @webtatic php56w-mysql.x86_64 5.6.38-1.w6 @webtatic php56w-pdo.x86_64 5.6.40-1.w6 @webtatic php56w-pear.noarch 1:1.10.4-1.w6 @webtatic php56w-pgsql.x86_64 5.6.40-1.w6 @webtatic php56w-process.x86_64 5.6.40-1.w6 @webtatic php56w-soap.x86_64 5.6.40-1.w6 @webtatic php56w-xml.x86_64 5.6.40-1.w6 @webtatic [root@bars cades_linux_amd64]# cd /opt/cprocsp/src/phpcades/если нет devel - устанавливаем или скачиваем исходники текущей версии php читаем /opt/cprocsp/src/phpcades/README.txt, приступаем в файле /opt/cprocsp/src/phpcades/Makefile.unix в первой строке ставим PHPDIR=/usr/include/php далее, сказано поставить libboost-dev - надо, значит надо...
[root@bars phpcades]# yum install boost-devel ... Installed: boost-devel.x86_64 0:1.41.0-28.el6 Dependency Installed: boost.x86_64 0:1.41.0-28.el6 boost-date-time.x86_64 0:1.41.0-28.el6 boost-filesystem.x86_64 0:1.41.0-28.el6 boost-graph.x86_64 0:1.41.0-28.el6 boost-iostreams.x86_64 0:1.41.0-28.el6 boost-math.x86_64 0:1.41.0-28.el6 boost-program-options.x86_64 0:1.41.0-28.el6 boost-python.x86_64 0:1.41.0-28.el6 boost-regex.x86_64 0:1.41.0-28.el6 boost-serialization.x86_64 0:1.41.0-28.el6 boost-signals.x86_64 0:1.41.0-28.el6 boost-system.x86_64 0:1.41.0-28.el6 boost-test.x86_64 0:1.41.0-28.el6 boost-thread.x86_64 0:1.41.0-28.el6 boost-wave.x86_64 0:1.41.0-28.el6 libicu.x86_64 0:4.2.1-15.el6_10 Complete! [root@bars phpcades]#
[root@bars phpcades]# [root@bars phpcades]# eval `/opt/cprocsp/src/doxygen/CSP/../setenv.sh --64`; make -f Makefile.unix g++ -DLINUX -DUNIX -DHAVE_LIMITS_H -D_COMPACT -DHAVE_STDINT_H -I/opt/cprocsp/include/ -I/opt/cprocsp/include/cpcsp -I/opt/cprocsp/include/pki -I/opt/cprocsp/include/pki/atl -I/opt/cprocsp/include/pk i/cppcades -I/opt/cprocsp/include/pki/cplib -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/Zend -I/usr/include/php/TSRM -DSIZEOF_VOID_P=8 -fPIC -DPIC -c -o PHPCadesCPSigners.o PHPCadesCP Signers.cpp ..... g++ -shared PHPCadesCPSigners.o PHPCadesCPSigner.o PHPCadesCPExtendedKeyUsage.o PHPCadesCPAttribute.o PHPCadesCPEKU.o PHPCadesCPEKUs.o PHPCadesCPBasicConstraints.o PHPCadesCPSignedData.o PHPCadesCPPubl icKey.o PHPCadesCPPrivateKey.o PHPCadesCPOID.o PHPCadesCPEncodedData.o PHPCadesCPAttributes.o PHPCadesCPCertificateStatus.o PHPCadesCPEnvelopedData.o PHPCadesCPAlgorithm.o PHPCadesCPRecipients.o PHPCade sCPKeyUsage.o PHPCadesAbout.o PHPCadesCPCardholderData.o PHPCadesCPCertificates.o PHPCadesCPSignedXML.o PHPCadesCPHashedData.o PHPCadesCPRawSignature.o PHPCadesCPCertificate.o PHPCadesCPStore.o dllmain. o PHPCadesVersion.o PHPCadesSymmetricAlgorithm.o errormsg.o -L/opt/cprocsp/lib/amd64 -lcppcades -lcapi10 -lcapi20 -lrdrsup -lcplib -g -o libphpcades.so [root@bars phpcades]#Скопируем его ко всем остальным модулям и включим
[root@bars phpcades]# cp libphpcades.so /usr/lib64/php/modules [root@bars phpcades]# echo "extension=libphpcades.so" > /etc/php.d/cryptopro.iniВсе. на этом установка расширения для PHP завершена. Но для php версии 7 так легко все не проходит, нужны дополнительные шаги
Download file EncryptFSS.c
#include <stdio.h> #include <string.h> #ifdef _WIN32 # include <windows.h> # include <wincrypt.h> # include <tchar.h> #else # include <stdlib.h> # include <CSP_WinDef.h> # include <CSP_WinCrypt.h> # include "reader/tchar.h" #endif #include <WinCryptEx.h> #include <stdint.h> //#define DEBUG_OUT 1 #define BLOCK_LENGTH 8 #define ASN1_LENGTH 172 #define ENCRKEY_OFFSET 7 #define ENCRKEY_LEN 32 #define MACKEY_OFFSET 41 #define MACKEY_LEN 4 #define PUBKEY_OFFSET 98 #define PUBKEY_LEN 64 #define SV_OFFSET 164 #define SV_LEN 8 static BYTE encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static BYTE *decoding_table = NULL; static int mod_table[] = {0, 2, 1}; BYTE * base64_encode(const BYTE *data, DWORD input_length, DWORD *output_length); static void HandleError(char *s); static void CleanUp(void); static HCRYPTPROV hProv = 0; // Дескриптор CSP static HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа static HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа static HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования static HCRYPTKEY hEphemeralKey = 0; // Дескриптор эфемерного ключа static FILE *fhCert=NULL; // Файл, в котором хранится сертификат static FILE *fhSource=NULL; // Исходный файл static FILE *fhOutput=NULL; // Исходный файл #ifdef DEBUG_OUT static FILE *fhEncrypt=NULL; // Зашифрованный файл static FILE *fhSession_SV=NULL; // Файл для хранения сессионного ключа (вектор инициализации) static FILE *fhSession_EncryptedKey=NULL; // Файл для хранения сессионного ключа (зашифрованный ключ) static FILE *fhSession_PublicKey=NULL; // Файл для хранения публичного эфемерного ключа static FILE *fhSession_MacKey=NULL; // Файл для хранения сессионного ключа (имита) static FILE *fhVector=NULL; // Файл для хранения вектора инициализации static FILE *fhEncryptionParam; // Файл для хранения неменяемой части блоба #endif static BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB static BYTE *pbIV = NULL; // Вектор инициализации сессионного ключа static BYTE *pbCipherValue = NULL; // Указатель шифрованный контент #define MAX_PUBLICKEYBLOB_SIZE 200 int main(int argc, char *argv[]) { BYTE pbKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; // Указатель на ключевой BLOB DWORD dwBlobLen = MAX_PUBLICKEYBLOB_SIZE; // Длина ключевого BLOBа DWORD dwBlobLenSimple; // Длина сессионного ключевого BLOBа BYTE pbSenderPublicKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; // Указатель на ключевой BLOB публичный, отправителя DWORD dwSenderPublicBlobLen = MAX_PUBLICKEYBLOB_SIZE; // Длина ключевого BLOBа, публичного, отправителя DWORD keyParam = CRYPT_MODE_CBC; DWORD paddingMode = ISO10126_PADDING; //cpm_ISO10126 BYTE * pbBufferB64; DWORD dwBufferB64Len; DWORD dwCipherValue; BYTE * pbPubCertData; DWORD dwPubCertData; BYTE * pbContent; // указатель на исходное содержимое DWORD cbContent = 0; // Длина содержимого DWORD dwIV = 0; // Длина вектора инициализации DWORD bufLen = sizeof(pbContent); // Длина буфера ALG_ID ke_alg = CALG_PRO_EXPORT /*CALG_PRO12_EXPORT*/; // алгоритм ключа согласования DWORD cbEncryptionParamSetStandart; // длина неизменяемой части блоба DWORD cbCert = 4000; // максимальный размер файла сертификата BYTE pbCert[4000]; // указатель на сертификат PCCERT_CONTEXT pCertContext = NULL; HCRYPTKEY hPubKey; BYTE pbKeyCipherValue[ASN1_LENGTH] = {0x30, 0x81, 0xA9, 0x30, 0x28, 4, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // offs 7: len 32 = Session Encrypted Key 4, 4, 0, 0, 0, 0, // offs 41 len 4 = Session Mac Key 0xA0, 0x7D, 6, 9, 0x2A, 0x85, 3, 7, 1, 2, 5, 1, 1, // OBJECT IDENTIFIER 1.2.643.7.1.2.5.1.1 tc26CipherZ (TC26 params Z for GOST 28147-89) 0xA0, 0x66, 0x30, 0x1F, 6, 8, 0x2A, 0x85, 3, 7, 1, 1, 1, 1, // OBJECT IDENTIFIER 1.2.643.7.1.1.1.1 gost2012PublicKey256 (GOST R 34.10-2012 256 bit public key) 0x30, 0x13, 6, 7, 0x2A, 0x85, 3, 2, 2, 0x24, 0, // OBJECT IDENTIFIER 1.2.643.2.2.36.0 cryptoProSignXA (CryptoPro ell.curve XA for GOST R 34.10-2001) 6, 8, 0x2A, 0x85, 3, 7, 1, 1, 2, 2, 3, // OBJECT IDENTIFIER 1.2.643.7.1.1.2.2 gost2012Digest256 (GOST R 34.11-2012 256 bit digest) 0x43, 0, 4, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // offs 98 = : len 64 = Session Public Key 0x4, 0x8, 0, 0, 0, 0, 0, 0, 0, 0 // offs 164: len 8 = Session SV }; // Указатель на структуру ASN1 для ключа DWORD dwKeyCipherValue = ASN1_LENGTH; static BYTE xml_1[] = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>" "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" "<SOAP-ENV:Header/>" "<SOAP-ENV:Body>" "<EncryptedData xmlns=\"http://www.w3.org/2001/04/xmlenc#\" Type=\"http://www.w3.org/2001/04/xmlenc#Element\">" "<EncryptionMethod Algorithm=\"urn:ietf:params:xml:ns:cpxmlsec:algorithms:gost28147\"/>" "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">" "<EncryptedKey xmlns=\"http://www.w3.org/2001/04/xmlenc#\">" "<EncryptionMethod Algorithm=\"urn:ietf:params:xml:ns:cpxmlsec:algorithms:transport-gost2001\"/>" "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">" "<X509Data>" "<X509Certificate>"; static BYTE xml_2[] = "</X509Certificate>" "</X509Data>" "</KeyInfo>" "<CipherData>" "<CipherValue>"; static BYTE xml_3[] = "</CipherValue>" "</CipherData>" "</EncryptedKey>" "</KeyInfo>" "<CipherData>" "<CipherValue>"; static BYTE xml_4[] = "</CipherValue>" "</CipherData>" "</EncryptedData>" "</SOAP-ENV:Body>" "</SOAP-ENV:Envelope>"; if(argc < 5) { printf("Encryption tool for FSS ELN service.\nver 0.01, IbZ(c) Studio (tm) :)\n" ); printf("\n\tusing: %s <container_name> <certificate_filename> <source_file> <output.xml>\n\n", argv[0]); HandleError( "Not enough input parameters given" ); } // Открытие файла, который будет зашифрован. if(!(fhSource = fopen(argv[3], "rb"))) HandleError( "Problem opening the source file \n" ); fseek(fhSource, 0, SEEK_END); cbContent = ftell(fhSource); rewind(fhSource); #ifdef DEBUG_OUT printf( "01. The file '%s' was opened, length = %d bytes\n", argv[3],cbContent ); #endif pbContent = (BYTE *)malloc(cbContent); if(!pbContent) HandleError("Out of memory. \n"); if(!(fhOutput = fopen(argv[4], "wb"))) HandleError( "Problem opening the file output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe file '%s' was opened\n", argv[4] ); // Открытие файла, в который будет производится запись блока зашифрованного файла. if(!(fhEncrypt = fopen("encrypt.bin", "wb"))) HandleError( "Problem opening the file 'encrypt.bin'\n" ); printf( "\tThe file 'encrypt.bin' was opened\n" ); // Открытие файла, в который производится запись синхропосылки. if(!(fhSession_SV = fopen("session_SV.bin", "wb"))) HandleError( "Problem opening the file 'session_SV.bin'\n" ); printf( "\tThe file 'session_SV.bin' was opened\n" ); // Открытие файла, в который производится запись сессионного ключа. if(!(fhSession_EncryptedKey = fopen("session_EncryptedKey.bin", "wb"))) HandleError( "Problem opening the file 'session_EncryptedKey.bin'\n" ); printf( "\tThe file 'session_EncryptedKey.bin' was opened\n" ); if(!(fhSession_PublicKey = fopen("session_PublicKey.bin", "wb"))) HandleError( "Problem opening the file 'session_PublicKey.bin'\n" ); printf( "\tThe file 'session_PublicKey.bin' was opened\n" ); // Открытие файла, в который производится запись MAC сессионного ключа. if(!(fhSession_MacKey = fopen("session_MacKey.bin", "wb"))) HandleError( "Problem opening the file 'session_MacKey.bin'\n" ); printf( "\tThe file 'session_MacKey.bin' was opened\n" ); // Открытие файла, в который производится запись вектора инициализации. if(!(fhVector = fopen("vector.bin", "wb"))) HandleError( "Problem opening the file 'vector.bin'\n" ); printf( "\tThe file 'vector.bin' was opened\n" ); // Открытие файла, в который производится запись вектора инициализации. if (!(fhEncryptionParam = fopen("EncryptionParam.bin", "wb"))) HandleError("Problem opening the file 'EncryptionParam.bin'\n"); printf( "\tThe file 'EncryptionParam.bin' was opened\n"); #endif // Получение дескриптора контейнера отправителя, находящегося в рамках провайдера. // if(CryptAcquireContext( &hProv, _TEXT("HDIMAGE\\\\2019rnik.000\\026C"), NULL, PROV_GOST_2012_256, 0)) if(!CryptAcquireContext( &hProv, argv[1], NULL, PROV_GOST_2012_256, 0)) HandleError("Error during CryptAcquireContext."); #ifdef DEBUG_OUT printf("02. The key container \"%s\" has been acquired. \n", argv[1]); #endif // Загрузка PUBLICKEYBLOB из сертификата, открытие файла, в котором содержится открытый ключ получателя. if((fhCert = fopen(argv[2], "rb"))) { #ifdef DEBUG_OUT printf( "\tThe file '%s' was opened\n", argv[2]); #endif cbCert = (DWORD)fread(pbCert, 1, cbCert, fhCert); if(!cbCert) HandleError( "Failed to read certificate\n" ); #ifdef DEBUG_OUT printf( "\tCertificate was read from the '%s'\n", argv[2] ); #endif pCertContext = CertCreateCertificateContext ( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, cbCert); if (!pCertContext) HandleError( "CertCreateCertificateContext" ); // Импортируем открытый ключ if (!CryptImportPublicKeyInfoEx( hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), 0, 0, NULL, &hPubKey)) { CertFreeCertificateContext(pCertContext); HandleError( "CryptImportPublicKeyInfoEx" ); } #ifdef DEBUG_OUT printf("\tPublic key imported from cert file\n"); #endif CertFreeCertificateContext(pCertContext); // экспорт открытого ключа получателя в BLOB if (!CryptExportKey( hPubKey, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwBlobLen)) HandleError( "CryptExportKey" ); #ifdef DEBUG_OUT printf("\tPublic key exported to blob\n"); #endif } // begin hack // генерация эфемерной ключевой пары if(!CryptGenKey(hProv, /* CALG_DH_EL_EPHEM */ CALG_DH_GR3410_12_256_EPHEM, CRYPT_EXPORTABLE, &hEphemeralKey)) HandleError("Error during CryptGenKey Ephemeral key."); #ifdef DEBUG_OUT printf("03. The Ephemeral key has been acquired. \n"); #endif // экспорт открытого ключа отправителя в BLOB if(!CryptExportKey(hEphemeralKey, 0, PUBLICKEYBLOB, 0, pbSenderPublicKeyBlob, &dwSenderPublicBlobLen)) HandleError("Error during CryptExportKey of Sender\'s public key."); #ifdef DEBUG_OUT printf("04. The Sender public key has been acquired. size of Blob = %d bytes\n", dwSenderPublicBlobLen); #endif // получаем значение открытого ключа отправителя из PUBLICKEYBLOB memcpy(&pbKeyCipherValue[PUBKEY_OFFSET], &pbSenderPublicKeyBlob[dwSenderPublicBlobLen-PUBKEY_LEN], PUBKEY_LEN); #ifdef DEBUG_OUT if(!fwrite( pbSenderPublicKeyBlob, 1, dwSenderPublicBlobLen, fhSession_PublicKey)) HandleError( "The session key can not be written to the 'session_PublicKey.bin'\n" ); printf("\tThe session key was written to the 'session_PublicKey.bin'\n" ); #endif // Получение дескриптора закрытого ключа отправителя. нам нужно для получения сертификата который вставим в запрос if(!CryptGetUserKey( hProv, AT_KEYEXCHANGE, &hKey)) HandleError("Error during CryptGetUserKey private key."); #ifdef DEBUG_OUT printf("05. The private key has been acquired. \n"); #endif // определяем размер блока памяти для получения нашего сертификата который мы пошлем в ФСС if(!CryptGetKeyParam(hKey, KP_CERTIFICATE, NULL, &dwPubCertData, 0)) HandleError("Error during CryptGetKeyParam for determinating size of certificate."); #ifdef DEBUG_OUT printf("06. The size for Public Certificate has been acquired. \n"); #endif pbPubCertData = (BYTE*)malloc(dwPubCertData); if(!pbPubCertData) HandleError("Out of memory. \n"); // получачем сам сертификата который мы пошлем в ФСС в подготовленную область памяти if(!CryptGetKeyParam(hKey, KP_CERTIFICATE, pbPubCertData, &dwPubCertData, 0)) HandleError("Error during CryptGetKeyParam when get certificate"); #ifdef DEBUG_OUT printf("07. The our Certificate has been acquired. \n"); #endif // Получение ключа согласования импортом открытого ключа получателя на эфемерном ключе, ----не на закрытом ключе отправителя. if(!CryptImportKey(hProv, pbKeyBlob, dwBlobLen, hEphemeralKey, 0, &hAgreeKey)) HandleError("Error during CryptImportKey public key."); #ifdef DEBUG_OUT printf("08. get AgreeKey by importing the responder public key. \n"); #endif // Установление ----PRO12_EXPORT (на саомм деле нет, PRO_EXPORT) алгоритма ключа согласования if(!CryptSetKeyParam( hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) HandleError("Error during CryptSetKeyParam agree key."); //printf("22. PRO12_EXPORT agree key algorithm has been set. \n"); #ifdef DEBUG_OUT printf("09. PRO_EXPORT agree key algorithm has been set. \n"); #endif // Генерация случайного сессионного ключа. if(!CryptGenKey( hProv, CALG_G28147, CRYPT_EXPORTABLE, &hSessionKey)) HandleError("ERROR -- CryptGenKey failed for random session key."); #ifdef DEBUG_OUT printf("10. Original session key is created. \n"); #endif //-------------------------------------------------------------------- // Зашифрование сессионного ключа. //-------------------------------------------------------------------- // экспорт сессионного ключа в BLOB // Определение размера BLOBа сессионного ключа и распределение памяти. if(!CryptExportKey( hSessionKey, hAgreeKey, SIMPLEBLOB, 0, NULL, &dwBlobLenSimple)) HandleError("Error computing BLOB length."); #ifdef DEBUG_OUT printf("11. Size of the BLOB for the sender session key determined. \n"); #endif pbKeyBlobSimple = (BYTE*)malloc(dwBlobLenSimple); if(!pbKeyBlobSimple) HandleError("Out of memory. \n"); // Зашифрование сессионного ключа на ключе Agree. экспорт сессионного ключа на ключе согласования if(!CryptExportKey( hSessionKey, hAgreeKey, SIMPLEBLOB, 0, pbKeyBlobSimple, &dwBlobLenSimple)) HandleError("Error during CryptExportKey."); #ifdef DEBUG_OUT printf("12. Contents have been written to the BLOB. \n"); #endif memcpy(&pbKeyCipherValue[SV_OFFSET], ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bSV, SV_LEN); memcpy(&pbKeyCipherValue[ENCRKEY_OFFSET], ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptedKey, ENCRKEY_LEN); memcpy(&pbKeyCipherValue[MACKEY_OFFSET], ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, MACKEY_LEN); // режим шифрования CBC if(!CryptSetKeyParam(hSessionKey, KP_MODE, (LPBYTE)&keyParam, 0)) HandleError("Error during CryptSetKeyParam cipher mode."); #ifdef DEBUG_OUT printf("\tSet crypt param CRYPT_MODE_CBC. \n"); #endif // режим паддинга if(!CryptSetKeyParam(hSessionKey, KP_PADDING, (LPBYTE)&paddingMode, 0)) HandleError("Error during CryptSetKeyParam padding mode."); #ifdef DEBUG_OUT printf("\tSet crypt param ISO10126_PADDING. \n"); #endif //end hacking // Определение размера вектора инициализации сессионного ключа. if(!CryptGetKeyParam( hSessionKey, KP_IV, NULL, &dwIV, 0)) HandleError("Error computing IV length."); #ifdef DEBUG_OUT printf("13. Size of the IV for the session key determined. \n"); #endif pbIV = (BYTE*)malloc(dwIV); if (!pbIV) HandleError("Out of memory. \n"); // Получение вектора инициализации сессионного ключа. if(!CryptGetKeyParam( hSessionKey, KP_IV, pbIV, &dwIV, 0)) HandleError("Error during CryptGetKeyParam."); #ifdef DEBUG_OUT printf( "14. CryptGetKeyParam succeeded. \n"); //-------------------------------------------------------------------- // Запись вектора инициализации в файл. if(!fwrite( pbIV, 1, dwIV, fhVector)) HandleError( "The IV can not be written to the 'vector.bin'\n" ); printf( "\tThe IV was written to the 'vector.bin'\n" ); //-------------------------------------------------------------------- // Запись сессионного вектора в файл. if(!fwrite( ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bSV, 1, SEANCE_VECTOR_LEN, fhSession_SV)) HandleError( "The session vector can not be written to the 'session_SV.bin'\n" ); printf( "\tThe session vector was written to the 'session_SV.bin'\n" ); //-------------------------------------------------------------------- // Запись сессионного зашифрованного ключа в файл. if(!fwrite( ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptedKey, 1, G28147_KEYLEN, fhSession_EncryptedKey)) HandleError( "The session key can not be written to the 'session_EncryptedKey.bin'\n" ); printf( "\tThe session key was written to the 'session_EncryptedKey.bin'\n" ); //-------------------------------------------------------------------- // Запись сессионного MAC ключа в файл. if(!fwrite( ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, 1, EXPORT_IMIT_SIZE, fhSession_MacKey)) HandleError( "The session key can not be written to the 'session_MacKey.bin'\n" ); printf( "\tThe session key was written to the 'session_MacKey.bin'\n" ); #endif //-------------------------------------------------------------------- // Запись неизменяемой части блоба в файл. if (((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet[0] != 0x30) HandleError("The EncryptionParam can not be written to the 'EncryptionParam.bin' - first byte is not 0x30\n"); cbEncryptionParamSetStandart = (DWORD)((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet[1] + sizeof((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet[0] + sizeof((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet[1]; #ifdef DEBUG_OUT if (!fwrite(((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet, 1, cbEncryptionParamSetStandart, fhEncryptionParam)) HandleError("The EncryptionParam can not be written to the 'EncryptionParam.bin'\n"); printf("\tThe EncryptionParam was written to the 'EncryptionParam.bin'\n"); //-------------------------------------------------------------------- // Чтение файла, и шифрование прочитанного блока и запись его в файл "encrypt.bin". Это будет // 'SOAP-ENV:Envelope -> SOAP-ENV:Body -> EncryptedData -> CipherData -> CipherValue //-------------------------------------------------------------------- printf( "15. Begin Encryption:\n"); #endif // сначала копируем вектор инициализации dwCipherValue = dwIV; pbCipherValue = (BYTE*)malloc(dwIV); if (!pbCipherValue) HandleError("Out of memory. \n"); memcpy(pbCipherValue,pbIV,dwIV); bufLen = cbContent; if( (DWORD)fread(pbContent, 1, cbContent, fhSource) != cbContent ) HandleError( "Problem reading the source xml file \n" ); // сначала определяем необходимый размер блока памяти if(!CryptEncrypt(hSessionKey, 0, TRUE, 0, NULL, &bufLen, 0)) HandleError("get memory size for Encryption failed."); #ifdef DEBUG_OUT printf( "\tget memory size %d bytes for encryption success....\n", bufLen); #endif pbContent = (BYTE*)realloc(pbContent, bufLen); if (!pbContent) HandleError("Out of memory. \n"); if(!CryptEncrypt(hSessionKey, 0, TRUE, 0, pbContent, &cbContent, bufLen)) HandleError("Encryption failed."); pbCipherValue = (BYTE*)realloc(pbCipherValue, dwCipherValue+cbContent); if (!pbCipherValue) HandleError("Out of memory. \n"); memcpy(&pbCipherValue[dwCipherValue], pbContent, cbContent); dwCipherValue += cbContent; #ifdef DEBUG_OUT if(!fwrite( pbContent, 1, cbContent, fhEncrypt)) HandleError( "The encrypted content can not be written \n" ); printf( "\tencrypted %d bytes, to %d bytes....\n" , cbContent, bufLen); #endif // ------------------------------------------------------- // начинаем запись результирующего зашифрованного XML // ------------------------------------------------------- if(!fwrite(xml_1, 1,sizeof(xml_1)-1, fhOutput)) HandleError( "The 1st part of xml can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "-------- writing result file xml ----------\n\tThe 1st part of xml was written to the '%s'\n", argv[4] ); #endif pbBufferB64 = base64_encode(pbPubCertData, dwPubCertData, &dwBufferB64Len); if(!fwrite( pbBufferB64, 1, dwBufferB64Len, fhOutput)) HandleError( "The encrypted data can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe encrypted data was written to the '%s'\n", argv[4] ); #endif if(!fwrite(xml_2, 1,sizeof(xml_2)-1, fhOutput))HandleError( "The 2nd part of xml can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe 2nd part of xml was written to the '%s'\n", argv[4] ); #endif pbBufferB64 = base64_encode(pbKeyCipherValue, ASN1_LENGTH, &dwBufferB64Len); if(!fwrite( pbBufferB64, 1, dwBufferB64Len, fhOutput)) HandleError( "The session structure can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe session structure was written to the '%s'\n", argv[4] ); #endif if(!fwrite(xml_3, 1,sizeof(xml_3)-1, fhOutput)) HandleError( "The 3rd part of xml can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe 3rd part of xml was written to the '%s'\n", argv[4] ); #endif pbBufferB64 = base64_encode(pbCipherValue, dwCipherValue, &dwBufferB64Len); if(!fwrite( pbBufferB64, 1, dwBufferB64Len, fhOutput)) HandleError( "The encrypted data can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe encrypted data was written to the '%s'\n", argv[4] ); #endif if(!fwrite(xml_4, 1,sizeof(xml_4)-1, fhOutput)) HandleError( "The 4th part of xml can not be written to the output xml file\n" ); #ifdef DEBUG_OUT printf( "\tThe 4th part of xml was written to the '%s'\n---- writing result file xml complete ------\n", argv[4] ); #endif CleanUp(); #ifdef DEBUG_OUT printf("\nThe program ran to completion without error. \n"); #endif return 0; } void CleanUp(void) { if(fhSource) fclose (fhSource); if(fhOutput) fclose (fhOutput); if(fhCert) fclose (fhCert); #ifdef DEBUG_OUT if(fhEncrypt) fclose (fhEncrypt); if(fhSession_SV) fclose (fhSession_SV); if(fhSession_EncryptedKey) fclose (fhSession_EncryptedKey); if(fhSession_PublicKey) fclose (fhSession_PublicKey); if(fhSession_MacKey) fclose (fhSession_MacKey); if(fhVector) fclose (fhVector); if(fhEncryptionParam) fclose(fhEncryptionParam); #endif if(hKey) CryptDestroyKey(hKey); if(hEphemeralKey) CryptDestroyKey(hEphemeralKey); if(hSessionKey) CryptDestroyKey(hSessionKey); if(hAgreeKey) CryptDestroyKey(hAgreeKey); if(hProv) CryptReleaseContext(hProv, 0); if(pbKeyBlobSimple) free(pbKeyBlobSimple); if(pbIV) free(pbIV); } BYTE * base64_encode(const BYTE *data, DWORD input_length, DWORD *output_length) { WORD i, j; DWORD octet_a, octet_b, octet_c, triple; *output_length = 4 * ((input_length + 2) / 3); BYTE * encoded_data = malloc(*output_length); if (encoded_data == NULL) return NULL; for (i = 0, j = 0; i < input_length;) { octet_a = i < input_length ? (BYTE)data[i++] : 0; octet_b = i < input_length ? (BYTE)data[i++] : 0; octet_c = i < input_length ? (BYTE)data[i++] : 0; triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[*output_length - 1 - i] = '='; return encoded_data; } void HandleError(char *s) { DWORD err = GetLastError(); printf("Error number : 0x%x\n", err); printf("Error description: %s\n", s); CleanUp(); if(!err) err = 1; exit(err); }Download file DecryptFSS.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef _WIN32 # include <windows.h> # include <wincrypt.h> # include <tchar.h> #else # include <stdlib.h> # include <CSP_WinDef.h> # include <CSP_WinCrypt.h> # include "reader/tchar.h" #endif #include <WinCryptEx.h> //#define DEBUG_OUT 1 #define BLOCK_LENGTH 4096 #define ENCRKEY_OFFSET 7 #define ENCRKEY_LEN 32 #define MACKEY_OFFSET 41 #define MACKEY_LEN 4 #define PUBKEY_OFFSET 98 #define PUBKEY_LEN 64 #define SV_OFFSET 164 #define SV_LEN 8 #define IV_LEN 8 #define ENCPARAMSET_LEN 13 static BYTE encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static BYTE *decoding_table = NULL; static int mod_table[] = {0, 2, 1}; void build_decoding_table(); BYTE *base64_decode(const BYTE *data, DWORD input_length, DWORD *output_length); static void CleanUp(void); static void HandleError(char *s); static HCRYPTPROV hProv = 0; // Дескриптор CSP static HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа static HCRYPTKEY hPubKey = 0; // Дескриптор открытого ключа ФСС static HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа static HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования static FILE *fhEncrypt=NULL; // Зашифрованный файл static FILE *fhOutput=NULL; // Расшифрованный файл #ifdef DEBUG_OUT static FILE *fhCertB64=NULL; // Сертификат ФСС из xml в base64 static FILE *fhCert=NULL; // Сертификат ФСС из xml бинарный static FILE *fhEncodingParametersB64; // Параметры шифрования из xml в base64 static FILE *fhEncodingParameters; // Параметры шифрования из xml в бинарном виде static FILE *fhEncodedContentB64; // Содержимое из xml в base64 static FILE *fhEncodedContent; // Содержимое из xml которое надо расшифровать в бинарном виде с вектором инициализации static FILE *fhMacKey; static FILE *fhKeyBlobSimple; static FILE *fhKeyBlob; static FILE *fhContent; // чистое содержимое которое надо расшифровать без вектора инициализации #endif char SearchStr1[] = "<ds:X509Data><ds:X509Certificate>"; char SearchStr2[] = "</ds:X509Certificate></ds:X509Data></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>"; char SearchStr3[] = "</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>"; char SearchStr4[] = "</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>"; #define MAX_PUBLICKEYBLOB_SIZE 200 int main(int argc, char *argv[]) { BYTE * pbEncrypt; // Указатель на содержимое зашифрованного файла DWORD cbEncrypt = 0; // Длина содержимого BYTE * pbClrContent; // Указатель на содержимое зашифрованного файла, после очистки от спецсимволов и пробелов DWORD cbClrContent = 0; // Длина содержимого после очистики BYTE * pbContent; DWORD cbContent = 0; BYTE * pbCert; // Указатель на сертификат ФСС DWORD cbCert = 0; // длина блока памяти BYTE pbKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; // Указатель на ключевой BLOB DWORD dwBlobLen = MAX_PUBLICKEYBLOB_SIZE; // Длина ключевого BLOBа BYTE pPublicKeyBlob[PUBKEY_LEN]; DWORD cbPublicKeyBlob; BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB DWORD cbBlobLenSimple; // Длина сессионного ключевого BLOBа BYTE pbIV[IV_LEN]; // Вектор инициализации сессионного ключа DWORD dwIV = IV_LEN; // Длина вектора инициализации DWORD keyParam = CRYPT_MODE_CBC; DWORD paddingMode = ISO10126_PADDING; //cpm_ISO10126 ALG_ID ke_alg = CALG_PRO_EXPORT /*CALG_PRO12_EXPORT*/; // алгоритм ключа согласования CRYPT_SIMPLEBLOB_HEADER tSimpleBlobHeaderStandart; //неменяемая часть блоба BYTE pbEncryptionParamSetStandart[ENCPARAMSET_LEN] = {0x30, 0x0B, 6, 9, 0x2A, 0x85, 3, 7, 1, 2, 5, 1, 1 }; // OBJECT IDENTIFIER 1.2.643.7.1.2.5.1.1 tc26CipherZ (TC26 params Z for GOST 28147-89) DWORD cbEncryptionParamSetStandart = ENCPARAMSET_LEN; //длина неменяемой части блоба PCCERT_CONTEXT pCertContext = NULL; DWORD dwBytesRead; DWORD i, pos_Cert1, pos_Cert2, pos_EncP1, pos_EncP2, pos_Enc1, pos_Enc2; BYTE * pbBufferB64; DWORD cbBufferB64; tSimpleBlobHeaderStandart.BlobHeader.aiKeyAlg = CALG_G28147; tSimpleBlobHeaderStandart.BlobHeader.bType = SIMPLEBLOB; tSimpleBlobHeaderStandart.BlobHeader.bVersion = BLOB_VERSION; tSimpleBlobHeaderStandart.BlobHeader.reserved = 0; tSimpleBlobHeaderStandart.EncryptKeyAlgId = CALG_G28147; tSimpleBlobHeaderStandart.Magic = G28147_MAGIC; if(argc < 4) { printf("Decryption tool for FSS ELN service.\nver 0.01, IbZ(c) Studio (tm) :)\n" ); printf("\n\tusing: %s <container_name> <encrypted_xml> <output.xml>\n\n", argv[0]); HandleError( "Not enough input parameters given" ); } // Открытие файла, в котором хранится зашифрованный ответ FSS. if(!(fhEncrypt = fopen(argv[2], "r+b" ))) HandleError( "Problem opening the file with encrypted xml\n" ); #ifdef DEBUG_OUT printf( "01. The file '%s' was opened\n", argv[2] ); #endif fseek(fhEncrypt, 0, SEEK_END); cbEncrypt = sizeof(char)*ftell(fhEncrypt); rewind(fhEncrypt); // Выделяем память для чтения файла целиком и читаем его в эту область pbEncrypt = (BYTE*)malloc(cbEncrypt); if (pbEncrypt == NULL) HandleError("Out of memory. \n"); dwBytesRead = (DWORD)fread(pbEncrypt, 1, cbEncrypt, fhEncrypt); if (dwBytesRead != cbEncrypt) HandleError("The Encrypted file can not be reading from the 'EncryptionParam.bin'\n"); #ifdef DEBUG_OUT printf( "02. The file '%s' was readed, size = %d butes\n", argv[2], dwBytesRead ); #endif // Убираем разрывы строк, пробелы и другие спецсимволы - поля же файле е закодированы в base64, чтобы потом легчо было пропарсить pbClrContent = (BYTE*)malloc(cbEncrypt); if (pbClrContent == NULL) HandleError("Out of memory. \n"); for(i=0; i<strlen(pbEncrypt); i++) { // if( ! ( (pbEncrypt[i] = 13) or (pbEncrypt[i] = 10) or (pbEncrypt[i] = 32) pbEncrypt[i] = 9) ) if((pbEncrypt[i] >32)&&(pbEncrypt[i]<127)) pbClrContent[cbClrContent++] = pbEncrypt[i]; } #ifdef DEBUG_OUT printf( "\tClear size = %d bytes\n", cbClrContent ); #endif // Находим позиции сертификата FSS pbContent = (char *)strstr(pbClrContent, SearchStr1); if ( pbContent == NULL) HandleError("Not found Encoding parameters structure\n"); pos_Cert1 = pbContent-pbClrContent+1+strlen(SearchStr1); pbContent = (char *)strstr(pbClrContent, SearchStr2); if ( pbContent == NULL) HandleError("Not found End of Certificate\n"); pos_Cert2 = pbContent-pbClrContent+1; #ifdef DEBUG_OUT printf ("03. Found Certificate at positions %d - %d\n",pos_Cert1, pos_Cert2); #endif // Копируем сертификат в pbCert pbCert = base64_decode(&pbClrContent[pos_Cert1-1], pos_Cert2-pos_Cert1, &cbCert); #ifdef DEBUG_OUT // Для отладки пишем сертификат в файл. if(!(fhCertB64 = fopen("certificate.txt", "w+b" ))) HandleError( "Problem opening the file 'certificate.txt'\n" ); printf( "\tThe file 'certificate.txt' was opened\n" ); if(!fwrite( &pbClrContent[pos_Cert1-1], 1, pos_Cert2-pos_Cert1, fhCertB64)) HandleError( "The content can not be written to the 'certificate.txt'\n" ); printf( "\tThe content was written to the 'certificate.txt'\n" ); if(!(fhCert = fopen("certificate.bin", "w+b" ))) HandleError( "Problem opening the file 'certificate.bin'\n" ); printf( "\tThe file 'certificate.bin' was opened\n" ); if(!fwrite( pbCert, 1, cbCert, fhCert)) HandleError( "The content can not be written to the 'certificate.bin'\n" ); printf( "\tThe content was written to the 'certificate.bin'\n" ); #endif // Находим позиции параметров шифрования pbContent = (char *)strstr(pbClrContent, SearchStr2); if ( pbContent == NULL) HandleError("Not found Encoding parameters\n"); pos_EncP1 = pbContent-pbClrContent+1+strlen(SearchStr2); pbContent = (char *)strstr(pbClrContent, SearchStr3); if ( pbContent == NULL) HandleError("Not found End of Encoding parameters structure\n"); pos_EncP2 = pbContent-pbClrContent+1; #ifdef DEBUG_OUT printf ("04. Found Encoding parameters at positions %d - %d\n",pos_EncP1, pos_EncP2); #endif pbBufferB64 = base64_decode(&pbClrContent[pos_EncP1-1], pos_EncP2-pos_EncP1, &cbBufferB64); //----------------------------------------------- // Готовим блоб структуру //----------------------------------------------- cbBlobLenSimple = cbEncryptionParamSetStandart + sizeof(CRYPT_SIMPLEBLOB_HEADER) + SEANCE_VECTOR_LEN + G28147_KEYLEN + EXPORT_IMIT_SIZE; pbKeyBlobSimple = malloc(cbBlobLenSimple); if(!pbKeyBlobSimple) HandleError("Out of memory. \n"); //копируем неменяемый хедер в блобе memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->tSimpleBlobHeader, &tSimpleBlobHeaderStandart, sizeof(CRYPT_SIMPLEBLOB_HEADER)); //копируем неменяемую ASN структуру memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet, pbEncryptionParamSetStandart, cbEncryptionParamSetStandart); // копируем session_SV memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bSV, &pbBufferB64[SV_OFFSET], SEANCE_VECTOR_LEN); // копируем session_EncryptedKey memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptedKey, &pbBufferB64[ENCRKEY_OFFSET], G28147_KEYLEN); // копируем session_MacKey memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, &pbBufferB64[MACKEY_OFFSET], EXPORT_IMIT_SIZE); #ifdef DEBUG_OUT if(!(fhMacKey = fopen("bMacKey.txt", "w+b" ))) HandleError( "Problem opening the file 'bMacKey.txt'\n" ); printf( "\tThe file 'bMacKey.txt' was opened\n" ); if(!fwrite( ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, 1, EXPORT_IMIT_SIZE, fhMacKey)) HandleError( "The content can not be written to the 'bMacKey.txt'\n" ); printf( "\tThe content was written to the 'bMacKey.txt'\n"); printf( "\t\t%d bytes of tSimpleBlobHeader \n" "\t\t%d bytes of bEncryptionParamSet\n" "\t\t%d bytes of bSV\n" "\t\t%d bytes of bEncryptedKey\n" "\t\t%d bytes of bMacKey\n" "\t\t%d bytes of total copied\n", sizeof(CRYPT_SIMPLEBLOB_HEADER),cbEncryptionParamSetStandart,SEANCE_VECTOR_LEN,G28147_KEYLEN,EXPORT_IMIT_SIZE, cbBlobLenSimple ); if(!(fhKeyBlobSimple = fopen("pbKeyBlobSimple.txt", "w+b" ))) HandleError( "Problem opening the file 'pbKeyBlobSimple.txt'\n" ); printf( "\tThe file 'pbKeyBlobSimple.txt' was opened\n" ); if(!fwrite( pbKeyBlobSimple, 1, cbBlobLenSimple, fhKeyBlobSimple)) HandleError( "The content can not be written to the 'pbKeyBlobSimple.txt'\n" ); printf( "\tThe content was written to the 'pbKeyBlobSimple.txt'\n" ); #endif memcpy(&pPublicKeyBlob, &pbBufferB64[PUBKEY_OFFSET], PUBKEY_LEN); #ifdef DEBUG_OUT // для отладки пишем в файл. if(!(fhKeyBlob = fopen("pbKeyBlob.txt", "w+b" ))) HandleError( "Problem opening the file 'pbKeyBlob.txt'\n" ); printf( "\tThe file 'pbKeyBlob.txt' was opened\n" ); if(!fwrite( pPublicKeyBlob, 1, PUBKEY_LEN, fhKeyBlob)) HandleError( "The content can not be written to the 'pbKeyBlob.txt'\n" ); printf( "\tThe content was written to the 'pbKeyBlob.txt'\n"); if(!(fhEncodingParametersB64 = fopen("encoding_parameters.txt", "w+b" ))) HandleError( "Problem opening the file 'encoding_parameters.txt'\n" ); printf( "\tThe file 'encoding_parameters.txt' was opened\n" ); if(!fwrite( &pbClrContent[pos_EncP1-1], 1, pos_EncP2-pos_EncP1, fhEncodingParametersB64)) HandleError( "The content can not be written to the 'encoding_parameters.txt'\n" ); printf( "\tThe content was written to the 'encoding_parameters.txt'\n" ); if(!(fhEncodingParameters = fopen("encoding_parameters.bin", "w+b" ))) HandleError( "Problem opening the file 'encoding_parameters.bin'\n" ); printf( "\tThe file 'encoding_parameters.bin' was opened\n" ); if(!fwrite( pbBufferB64, 1, cbBufferB64, fhEncodingParameters)) HandleError( "The content can not be written to the 'encoding_parameters.bin'\n" ); printf( "\tThe content was written to the 'encoding_parameters.bin'\n" ); #endif // Находим основное зашифрованое содержимое pbContent = (char *)strstr(pbClrContent, SearchStr3); if ( pbContent == NULL ) HandleError("Not found Encoded content\n"); pos_Enc1 = pbContent-pbClrContent+1+strlen(SearchStr3); pbContent = (char *)strstr(pbClrContent, SearchStr4); if ( pbContent == NULL ) HandleError("Not found End of encoded content\n"); pos_Enc2 = pbContent-pbClrContent+1; #ifdef DEBUG_OUT printf ("05. Found Encoded content at positions %d - %d\n",pos_Enc1, pos_Enc2); #endif // В pbBufferB64 - первые 8 байт будут вектор инициализации, затем само зашифрованное содержимое pbBufferB64 = base64_decode(&pbClrContent[pos_Enc1-1], pos_Enc2-pos_Enc1, &cbBufferB64); #ifdef DEBUG_OUT // для отладки пишем в файл. if(!(fhEncodedContentB64 = fopen("encoded_content.txt", "w+b" ))) HandleError( "Problem opening the file 'encoded_content.txt'\n" ); printf( "\tThe file 'encoded_content.txt' was opened\n" ); if(!fwrite( &pbClrContent[pos_Enc1-1], 1, pos_Enc2-pos_Enc1, fhEncodedContentB64)) HandleError( "The content can not be written to the 'encoded_content.txt'\n" ); printf( "\tThe content was written to the 'encoded_content.txt'\n" ); if(!(fhEncodedContent = fopen("encoded_content.bin", "w+b" ))) HandleError( "Problem opening the file 'encoded_content.bin'\n" ); printf( "\tThe file 'encoded_content.bin' was opened\n" ); if(!fwrite( pbBufferB64, 1, cbBufferB64, fhEncodedContent)) HandleError( "The content can not be written to the 'encoded_content.bin'\n" ); printf( "\tThe content was written to the 'encoded_content.bin'\n" ); #endif // Чтение вектора инициализации из файла. memcpy(pbIV, pbBufferB64, dwIV); pbContent = &pbBufferB64[dwIV]; cbContent = cbBufferB64-dwIV; // ------------------------------------------------------------------ // Закончили чтение файла и подготовку структур - начинаем дешифровку // ------------------------------------------------------------------ if(!CryptAcquireContext( &hProv, argv[1], NULL, PROV_GOST_2012_256, 0)) HandleError("Error during CryptAcquireContext."); #ifdef DEBUG_OUT printf("06. The key container \"%s\" has been acquired. \n", argv[1]); #endif // Загрузка PUBLICKEYBLOB из сертификата, открытие файла, в котором содержится открытый ключ получателя. pCertContext = CertCreateCertificateContext ( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, cbCert); if (!pCertContext) HandleError( "CertCreateCertificateContext" ); #ifdef DEBUG_OUT printf("07. Certificate context created\n"); #endif // Импортируем открытый ключ if (!CryptImportPublicKeyInfoEx( hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), 0, 0, NULL, &hPubKey)) HandleError( "CryptImportPublicKeyInfoEx" ); #ifdef DEBUG_OUT printf("08. Public key imported from cert file\n"); #endif // экспорт открытого ключа получателя в BLOB if (!CryptExportKey( hPubKey, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwBlobLen)) HandleError( "CryptExportKey" ); #ifdef DEBUG_OUT printf("09. Public key exported to blob\n"); #endif memcpy(&pbKeyBlob[dwBlobLen-PUBKEY_LEN], pPublicKeyBlob, PUBKEY_LEN); // получение закрытого ключа if(!CryptGetUserKey( hProv, AT_KEYEXCHANGE, &hKey)) HandleError("Error during CryptGetUserKey private key."); #ifdef DEBUG_OUT printf("10. The private key has been acquired. \n"); #endif //получение ключа согласования импортом открытого ключа отправителя на закрытом ключе if(!CryptImportKey(hProv, pbKeyBlob, dwBlobLen, hKey, 0, &hAgreeKey)) HandleError("Error during CryptImportKey public key."); #ifdef DEBUG_OUT printf("11. The sender public key has been imported. \n"); #endif // установление PRO_EXPORT алгоритма ключа согласования ---не PRO12_EXPORT ! if(!CryptSetKeyParam( hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) HandleError("Error during CryptSetKeyParam agree key."); #ifdef DEBUG_OUT printf("12. PRO_EXPORT agree key algorithm has been set. \n"); #endif // Получение сессионного ключа импортом зашифрованного сессионного ключа на ключе согласования Agree. if(!CryptImportKey( hProv, pbKeyBlobSimple, cbBlobLenSimple, hAgreeKey, 0, &hSessionKey)) HandleError("Error during CryptImportKey session key."); #ifdef DEBUG_OUT printf("13. The session key has been imported. \n"); #endif // Установка вектора инициализации - без него первые 8 байт расшифруются неправильно. if(!CryptSetKeyParam( hSessionKey, KP_IV, pbIV, 0)) HandleError("Error during CryptSetKeyParam."); #ifdef DEBUG_OUT printf("14. CryptSetKeyParam setting IV succeeded. \n"); #endif // установка режима шифрования CBC if(!CryptSetKeyParam( hSessionKey, KP_MODE, (LPBYTE)&keyParam, 0)) HandleError("Error during CryptSetKeyParam."); #ifdef DEBUG_OUT printf("15. CryptSetKeyParam setting CBC mode succeeded. \n"); #endif // установка режима паддинга if(!CryptSetKeyParam( hSessionKey, KP_PADDING, (LPBYTE)&paddingMode, 0)) HandleError("Error during CryptSetKeyParam."); #ifdef DEBUG_OUT printf("16. CryptSetKeyParam setting ISO10126 padding succeeded. \n"); #endif #ifdef DEBUG_OUT if(!(fhContent = fopen("content.bin", "w+b" ))) HandleError( "Problem opening the file 'content.bin'\n" ); printf( "\tThe file 'content.bin' was opened\n" ); if(!fwrite( pbContent, 1, cbContent, fhContent)) HandleError( "The content can not be written to the 'content.bin'\n" ); printf( "\tThe content was written to the 'content.bin', size = %d\n", cbContent ); #endif // Дешифрование зашифрованного базового SOAP-запроса, после которого в // decryptedData будет содержать дешифрованные данные, // а decryptedDataLen - длину расшифрованных данных if(!CryptDecrypt( hSessionKey, 0, TRUE, 0, pbContent, &cbContent)) HandleError("Decryption failed."); #ifdef DEBUG_OUT printf("17. Decryption succeeded. \n"); #endif // Запись расшифрованного блока в файл. if(!(fhOutput = fopen(argv[3], "w+b" ))) HandleError( "Problem opening the file for encoded xml\n" ); #ifdef DEBUG_OUT printf( "\tThe file '%s' was opened\n", argv[3] ); #endif if(!fwrite( pbContent, 1, cbContent, fhOutput)) HandleError( "The decrypted content can not be written '\n" ); #ifdef DEBUG_OUT printf( "18. The decrypted content was written to the '%s'\n", argv[3] ); #endif CleanUp(); #ifdef DEBUG_OUT printf("The program ran to completion without error. \n"); #endif return 0; } void CleanUp(void) { if(fhEncrypt) fclose (fhEncrypt); if(fhOutput) fclose (fhOutput); #ifdef DEBUG_OUT if(fhCert) fclose (fhCert); if(fhCertB64) fclose (fhCertB64); if(fhMacKey) fclose (fhMacKey); if(fhKeyBlobSimple) fclose (fhKeyBlobSimple); if(fhKeyBlob) fclose (fhKeyBlob); if(fhEncodingParametersB64) fclose (fhEncodingParametersB64); if(fhEncodingParameters) fclose (fhEncodingParameters); if(fhEncodedContentB64) fclose (fhEncodedContentB64); if(fhEncodedContent) fclose (fhEncodedContent); if(fhContent) fclose (fhContent); #endif if(hKey) CryptDestroyKey(hKey); // Уничтожение дескриптора закрытого ключа. if(hSessionKey) CryptDestroyKey(hSessionKey); // Уничтожение дескриптора сессионного ключа. if(hAgreeKey) CryptDestroyKey(hAgreeKey); // Уничтожение дескриптора ключа согласования. if(hProv) CryptReleaseContext(hProv, 0); // Освобождение дескриптора провайдера. } BYTE *base64_decode(const BYTE *data, DWORD input_length, DWORD *output_length) { int i, j; DWORD sextet_a, sextet_b, sextet_c, sextet_d, triple; if (decoding_table == NULL) build_decoding_table(); if (input_length % 4 != 0) return NULL; *output_length = input_length / 4 * 3; if (data[input_length - 1] == '=') (*output_length)--; if (data[input_length - 2] == '=') (*output_length)--; BYTE *decoded_data = malloc(*output_length); if (decoded_data == NULL) return NULL; for (i = 0, j = 0; i < input_length;) { sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF; } return decoded_data; } void build_decoding_table() { int i; decoding_table = malloc(256); for (i = 0; i < 64; i++) decoding_table[(BYTE) encoding_table[i]] = i; } //-------------------------------------------------------------------- // В этом примере используется функция HandleError, функция обработки // простых ошибок, для печати сообщения и выхода из программы. // В большинстве приложений эта функция заменяется другой функцией, // которая выводит более полное сообщение об ошибке. void HandleError(char *s) { DWORD err = GetLastError(); printf("Error number : 0x%x\n", err); printf("Error description: %s\n", s); CleanUp(); if(!err) err = 1; exit(err); }