шифрование ГОСТ на php и электронные больничные ФСС

Возникла задача - добавить в нашу МИС интеграцию с сервисом ФСС по обработке электронных больничных - здесь описание.
МИСка наша крутится на сервере с линуксом. Реализовать взаимодействие с сервисом оказалось в итоге не просто.
Опытом своим хочу поделиться
Возникла задача - добавить в нашу МИС интеграцию с сервисом ФСС по обработке электронных больничных - здесь описание.
МИСка наша крутится на сервере с линуксом. Реализовать взаимодействие с сервисом оказалось в итоге не просто.
Опытом своим хочу поделиться
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);
}