//-------------------------------------------------------------------- // Данный пример демонстрирует расшифрование файла на сессионном ключе. // Для корректной работы этого примера передаваемые ему входные данные // должны являться результатом работы примера EncryptFile. // Зашифрованный сессионный ключ содержится в файле "session_EncryptedKey.bin", // синхропосылка для согласования ключей - в "session_SV.bin", // MAC зашифрованного сессионного ключа - в "session_MacKey.bin". // Для его расшифрования необходим открытый ключ отправителя, содержащийся // в файле "Sender.pub" (данный файл создается при выполнении примера // ExportInFile). // Для корректной работы этой программы необходимо получить зашифрованный // файл с именем "encrypt.bin". Для правильного расшифрования файла // необходим вектор инициализации, который хранится в файле "vector.bin". // Расшифрование производится в файл "destination.txt". // Следует сделить за тем, чтобы контейнеры "Sender" и "Responder", // используемые в программах ExportInFile, EncryptFile и DecryptFile на момент // выполнения этих программ существовали. // Замечание: под win32 рекомендуется использовать _s аналоги CRT функций. //-------------------------------------------------------------------- #define BLOCK_LENGTH 4096 static void LoadPublicKey(BYTE *pbBlob, DWORD *pcbBlob, char *szCertFile, char *szKeyFile); static void CleanUp(void); static void HandleError(const char *s); static HCRYPTPROV hProv = 0; // Дескриптор CSP static HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа static HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа static HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования static FILE *Encrypt=NULL; // Зашифрованный файл static FILE *certf=NULL; // Файл, в котором хранится сертификат static FILE *publicf=NULL; // Файл, в котором хранится открытый ключ static FILE *session_SV=NULL;// Файл для хранения сессионного ключа (вектор инициализации) static FILE *session_EncryptedKey=NULL;// Файл для хранения сессионного ключа (зашифрованный ключ) static FILE *session_MacKey=NULL; // Файл для хранения сессионного ключа (имита) static FILE *vectorf=NULL; // Файл для хранения вектора инициализации static FILE *destination=NULL; // Расшифрованный файл static FILE *EncryptionParam=NULL; // Файл для хранения неменяемой части блоба #define MAX_PUBLICKEYBLOB_SIZE 200 int main(void) { BYTE pbKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; // Указатель на ключевой BLOB DWORD dwBlobLen = MAX_PUBLICKEYBLOB_SIZE; // Длина ключевого BLOBа BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB DWORD cbBlobLenSimple; // Длина сессионного ключевого BLOBа BYTE pbIV[100]; // Вектор инициализации сессионного ключа DWORD dwIV = 0; // Длина вектора инициализации BYTE pbContent[BLOCK_LENGTH]; // Указатель на содержимое зашифрованного файла DWORD cbContent = 0; // Длина содержимого ALG_ID ke_alg = CALG_PRO12_EXPORT; // алгоритм ключа согласования CRYPT_SIMPLEBLOB_HEADER tSimpleBlobHeaderStandart; DWORD dwBytesRead; BYTE *pbEncryptionParamSetStandart; //неменяемая часть блоба DWORD cbEncryptionParamSetStandart; //длина неменяемой части блоба 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(!fopen_s( &Encrypt, "encrypt.bin", "r+b" )) if(!(Encrypt = fopen("encrypt.bin", "r+b" ))) HandleError( "Problem opening the file 'encrypt.bin'\n" ); printf( "The file 'encrypt.bin' was opened\n" ); // Открытие файла, в который производится запись сессионного ключа. if(!(session_SV = fopen("session_SV.bin", "r+b"))) HandleError( "Problem opening the file 'session_SV.bin'\n" ); printf( "The file 'session_SV.bin' was opened\n" ); // Открытие файла, в который производится запись сессионного ключа. if(!(session_EncryptedKey = fopen("session_EncryptedKey.bin", "r+b"))) HandleError( "Problem opening the file 'session_EncryptedKey.bin'\n" ); printf( "The file 'session_EncryptedKey.bin' was opened\n" ); // Открытие файла, в который производится запись сессионного ключа. if(!(session_MacKey = fopen("session_MacKey.bin", "r+b"))) HandleError( "Problem opening the file 'session_MacKey.bin'\n" ); printf( "The file 'session_MacKey.bin' was opened\n" ); // Открытие файла, в котором хранится вектор инициализации. //if(!fopen_s(&vectorf, "vector.bin", "r+b" )) if(!(vectorf = fopen("vector.bin", "r+b"))) HandleError( "Problem opening the file 'vector.bin'\n" ); printf( "The file 'vector.bin' was opened\n" ); // Открытие файла, в который будет записан расшифрованный файл. //if(!fopen_s(&destination, "destination.txt", "w+b" )) if(!(destination = fopen("destination.txt", "w+b" ))) HandleError( "Problem opening the file 'destination.txt'\n" ); printf( "The file 'destination.txt' was opened\n" ); // Открытие файла, в котором хранится неменяемая часть блоба. //if(!fopen_s(&vectorf, "vector.bin", "r+b" )) if (!(EncryptionParam = fopen("EncryptionParam.bin", "r+b"))) HandleError("Problem opening the file 'EncryptionParam.bin'\n"); printf("The file 'EncryptionParam.bin' was opened\n"); // Чтение вектора инициализации из файла. dwIV = (DWORD)fread(pbIV, 1, 100, vectorf); if(!dwIV) HandleError( "The IV can not be reading from the 'vector.bin'\n" ); printf( "The IV was read from the 'vector.bin'\n" ); // Получение дескриптора контейнера получателя с именем "Responder", // находящегося в рамках провайдера. if(!CryptAcquireContext( &hProv, _TEXT("Responder"), NULL, PROV_GOST_2012_256, 0)) { HandleError("Error during CryptAcquireContext"); } printf("The key container \"Responder\" has been acquired. \n"); // obtain file size: fseek(EncryptionParam, 0, SEEK_END); cbEncryptionParamSetStandart = sizeof(char)*ftell(EncryptionParam); rewind(EncryptionParam); // allocate memory to contain the whole file: pbEncryptionParamSetStandart = (BYTE*)malloc(cbEncryptionParamSetStandart); if (pbEncryptionParamSetStandart == NULL) HandleError("Out of memory. \n"); // copy the file into the buffer: dwBytesRead = (DWORD)fread(pbEncryptionParamSetStandart, 1, cbEncryptionParamSetStandart, EncryptionParam); if (dwBytesRead != cbEncryptionParamSetStandart) HandleError("The EncryptionParam can not be reading from the 'EncryptionParam.bin'\n"); cbBlobLenSimple = cbEncryptionParamSetStandart; cbBlobLenSimple += (sizeof(CRYPT_SIMPLEBLOB_HEADER)+SEANCE_VECTOR_LEN + G28147_KEYLEN + EXPORT_IMIT_SIZE);// +sizeof(pbEncryptionParamSetStandart); pbKeyBlobSimple = malloc(cbBlobLenSimple); if(!pbKeyBlobSimple) HandleError("Out of memory. \n"); memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->tSimpleBlobHeader, &tSimpleBlobHeaderStandart, sizeof(CRYPT_SIMPLEBLOB_HEADER)); //копируем неменяемый хедер в блобе // Чтение сесcионного ключа из файла. dwBytesRead = (DWORD)fread(((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bSV, 1, SEANCE_VECTOR_LEN, session_SV); if(!dwBytesRead) HandleError( "The session key can not be reading from the 'session_SV.bin'\n" ); printf( "The session key was read from the 'session_SV.bin'\n" ); // Чтение сесcионного ключа из файла. dwBytesRead = (DWORD)fread(((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptedKey, 1, G28147_KEYLEN, session_EncryptedKey); if(!dwBytesRead) HandleError( "The session key can not be reading from the 'session_EncryptedKey.bin'\n" ); printf( "The session key was read from the 'session_EncryptedKey.bin'\n" ); memcpy(((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet, pbEncryptionParamSetStandart, cbEncryptionParamSetStandart); //копируем неменяемую ASN структуру // Чтение сесcионного ключа из файла. dwBytesRead = (DWORD)fread(((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, 1, EXPORT_IMIT_SIZE, session_MacKey); if(!dwBytesRead) HandleError( "The session key can not be reading from the 'session_MacKey.bin'\n" ); printf( "The session key was read from the 'session_MacKey.bin'\n" ); LoadPublicKey(pbKeyBlob, &dwBlobLen, "Sender.cer", "Sender.pub"); // Получение дескриптора закрытого ключа получателя. if(CryptGetUserKey( hProv, AT_KEYEXCHANGE, &hKey)) { printf("The private key has been acquired. \n"); } else { HandleError("Error during CryptGetUserKey private key."); } // Получение ключа согласования импортом открытого ключа отправителя // на закрытом ключе получателя. if(CryptImportKey( hProv, pbKeyBlob, dwBlobLen, hKey, 0, &hAgreeKey)) { printf("The sender public key has been imported. \n"); } else { HandleError("Error during CryptImportKey public key."); } // Установление PRO12_EXPORT алгоритма ключа согласования if(CryptSetKeyParam( hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) { printf("PRO12_EXPORT agree key algorithm has been set. \n"); } else { HandleError("Error during CryptSetKeyParam agree key."); } // Получение сессионного ключа импортом зашифрованного сессионного ключа // на ключе Agree. if(CryptImportKey( hProv, pbKeyBlobSimple, cbBlobLenSimple, hAgreeKey, 0, &hSessionKey)) { printf("The session key has been imported. \n"); } else { HandleError("Error during CryptImportKey session key."); } // Установка вектора инициализации - без него первые 8 байт // расшифруются неправильно. if(!CryptSetKeyParam( hSessionKey, KP_IV, pbIV, 0)) { HandleError("Error during CryptSetKeyParam."); } printf( "CryptSetKeyParam succeeded. \n"); // Чтение зашифрованного файла блоками по 4 КБ. Расшифрование прочитанного // блока и запись его в файл "destination.txt". do { cbContent = (DWORD)fread(pbContent, 1, BLOCK_LENGTH, Encrypt); if(cbContent) { BOOL bFinal = feof(Encrypt); // Расшифрование прочитанного блока на сессионном ключе. if(CryptDecrypt( hSessionKey, 0, bFinal, 0, pbContent, &cbContent)) { printf( "Decryption succeeded. \n"); // Запись расшифрованного блока в файл. if(fwrite( pbContent, 1, cbContent, destination)) { printf( "The decrypted content was written to the 'destination.txt'\n" ); } else { HandleError( "The decrypted content can not be written to the 'destination.txt'\n" ); } } else { HandleError("Decryption failed."); } } else { HandleError( "Problem reading the file 'encrypt.bin'\n" ); } } while(!feof(Encrypt)); CleanUp(); printf("The program ran to completion without error. \n"); free(pbEncryptionParamSetStandart); free(pbKeyBlobSimple); return 0; }// Конец main // Загрузка PUBLICKEYBLOB из сертификата, если он есть, (предпочтительно) или // из блоба, записанного в файл. void LoadPublicKey(BYTE *pbBlob, DWORD *pcbBlob, char *szCertFile, char *szKeyFile) { // Открытие файла, в котором содержится открытый ключ получателя. //if(fopen_s(&certf, szCertFile, "r+b" )) if((certf = fopen(szCertFile, "rb"))) { DWORD cbCert = 2000; BYTE pbCert[2000]; PCCERT_CONTEXT pCertContext = NULL; HCRYPTKEY hPubKey; printf( "The file '%s' was opened\n", szCertFile ); cbCert = (DWORD)fread(pbCert, 1, cbCert, certf); if(!cbCert) HandleError( "Failed to read certificate\n" ); printf( "Certificate was read from the '%s'\n", szCertFile ); 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)) { printf("Public key imported from cert file\n"); } else { CertFreeCertificateContext(pCertContext); HandleError( "CryptImportPublicKeyInfoEx" ); } CertFreeCertificateContext(pCertContext); // Экспортируем его в BLOB if (CryptExportKey( hPubKey, 0, PUBLICKEYBLOB, 0, pbBlob, pcbBlob)) { printf("Public key exported to blob\n"); } else { HandleError( "CryptExportKey" ); } } else { // Открытие файла, в котором содержится открытый ключ получателя. //if(!fopen_s(&publicf, szKeyFile, "r+b" )) if(!(publicf = fopen(szKeyFile, "rb"))) HandleError( "Problem opening the public key blob file\n" ); printf( "The file '%s' was opened\n", szKeyFile ); *pcbBlob = (DWORD)fread(pbBlob, 1, *pcbBlob, publicf); if(!*pcbBlob) HandleError( "Failed to read key blob file\n" ); printf( "Key blob was read from the '%s'\n", szKeyFile ); } } void CleanUp(void) { if(Encrypt) fclose (Encrypt); if(session_SV) fclose (session_SV); if(session_EncryptedKey) fclose (session_EncryptedKey); if(session_MacKey) fclose (session_MacKey); if(vectorf) fclose (vectorf); if(destination) fclose (destination); if(certf) fclose (certf); if(publicf) fclose (publicf); if (EncryptionParam) fclose (EncryptionParam); // Уничтожение дескриптора закрытого ключа. if(hKey) CryptDestroyKey(hKey); // Уничтожение дескриптора сессионного ключа. if(hSessionKey) CryptDestroyKey(hSessionKey); // Уничтожение дескриптора ключа согласования. if(hAgreeKey) CryptDestroyKey(hAgreeKey); // Освобождение дескриптора провайдера. if(hProv) CryptReleaseContext(hProv, 0); } // Конец примера
AIX: 6/7.
FreeBSD: 11/12, pfSense 2.x.
Linux: LSB 4.x (RHEL 5/6/7/8, SuSE 11SP4/12/15, Oracle Linux 5/6/7/8, CentOS 6/7/8, Ubuntu 14.04/16.04/18.04/19.10, Linux Mint 18/19, Fedora 28/29/30/31, Debian 8/9/10 и др.).
Solaris: 10/11.
Mac OS X: 10.9/10.10/10.11/10.12/10.13/10.14/10.15.
iOS: 8/9/10/11/12/13.
Sailfish: 2/3.
Windows: 7/8/8.1/10, Server 2008/2008R2/2012/2012R2/2016/2019.