/*
* Copyright(C) 2000-2014
*
* Этот файл содержит информацию, являющуюся
* собственностью компании Крипто Про.
*
* Любая часть этого файла не может быть скопирована,
* исправлена, переведена на другие языки,
* локализована или модифицирована любым способом,
* откомпилирована, передана по сети с или на
* любую компьютерную систему без предварительного
* заключения соглашения с компанией Крипто Про.
*
* Программный код, содержащийся в этом файле, предназначен
* исключительно для целей обучения и не может быть использован
* для защиты информации.
*
* Компания Крипто-Про не несет никакой
* ответственности за функционирование этого кода.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#   include <windows.h>
#   include <wincrypt.h>
#else
#   include <stdlib.h>
#   include <CSP_WinDef.h>
#   include <CSP_WinCrypt.h>
#endif
#include <WinCryptEx.h>

// Начало примера (не следует удалять данный комментарий, он используется 
// для автоматической сборки документации)
//-------------------------------------------------------------------------------------
// Данный пример демонстрирует поиск контейнера соответствующего сертификату.
// Для проверки работы примера необходимо создать контейнер и положить соответствующий
// ему сертификат в файл test_search.cer. Программа возвращает имя найденного контейнера.
//-------------------------------------------------------------------------------------


BOOL CmpCertAndContFP(LPCSTR szContainerName, LPBYTE pbFPCert, DWORD cbFPCert);

static void HandleError(const char *s);

int main(int argc, char **argv) {

    //------------------------------------------------------------------------
    // Объявление переменных

    HCERTSTORE          hFileStore;	    // Дескриптор хранилища сертификатов
    PCCERT_CONTEXT      pCertCtx;	    // Контекст сертификата
    HCRYPTPROV          hCryptProv;	    // Дескриптор криптопровайдера
    HCRYPTKEY           hPublicKey;	    // Дескриптор ключа из сертификата  
    LPBYTE		pbFPCert;	    // Параметр FP из сертификата 
    DWORD		cbFPCert;	    // Размер параметра FP из сертификата
    LPBYTE		pbContainerName;    // Имя контейнера
    DWORD		cbContainerName;    // Размер имени контейнера
    DWORD		dwFlags;


    if (2 != argc)
    {
	HandleError("Wrong arguments!");
    }

    //------------------------------------------------------------------------
    // Создание хранилища сертификатов из файла

    if ((hFileStore = CertOpenStore(
	CERT_STORE_PROV_FILENAME_A,
	0,                        
	0,                     
	CERT_STORE_OPEN_EXISTING_FLAG,
	argv[1] // Файл с сертификатом
    )))
    {
	printf("The file store was created successfully.\n");
    }
    else
    {
	HandleError("An error occurred during creation of the file store!");
    }

    //------------------------------------------------------------------------
    // Получение контекста сертификата из хранилища

    if ((pCertCtx = CertEnumCertificatesInStore(hFileStore, NULL)))
    {
	printf("Certificate context was loaded from store.\n");
    }
    else 
    {
	HandleError("Error during CertEnumCertificatesInStore!");
    }


    //------------------------------------------------------------------------
    // Получение дескриптора криптопровайдера

    if (CryptAcquireContext(
	&hCryptProv,
	NULL,
	NULL,
	PROV_GOST_2012_256,
	CRYPT_VERIFYCONTEXT))
    {
	printf("CryptAcquireContext succeeded. \n");
    }
    else
    {
	HandleError("Error during CryptAcquireContext!");
    }


    //------------------------------------------------------------------------
    // Получение дескриптора открытого ключа из сертификата

    if (CryptImportPublicKeyInfo(
	hCryptProv,
	pCertCtx->dwCertEncodingType,
	&pCertCtx->pCertInfo->SubjectPublicKeyInfo,
	&hPublicKey))
    {
	printf("CryptImportPublicKeyInfo succeeded. \n");
    }
    else
    {
	HandleError("Error during CryptImportPublicKeyInfo!");
    }

    //------------------------------------------------------------------------
    // Получение параметра KP_FP открытого ключа из сертификата

    // Получение размера параметра
    if (CryptGetKeyParam(hPublicKey, KP_FP, NULL, &cbFPCert, 0))
    {
	printf("CryptGetKeyParam KP_FP succeeded. \n");
    }
    else 
    {
	HandleError("Error during CryptGetKeyParam!");
    }

    // Выделение памяти
    pbFPCert = (LPBYTE)malloc(cbFPCert);

    if (!pbFPCert)
    {
	HandleError("Fail to allocate memory!");
    }

    // Получение параметра
    if (CryptGetKeyParam(hPublicKey, KP_FP, pbFPCert, &cbFPCert, 0))
    {
	printf("CryptGetKeyParam KP_FP succeeded. \n");
    }
    else
    {
	HandleError("Error during CryptGetKeyParam!");
    }


    //------------------------------------------------------------------------
    // Получение имени первого контейнера

    // Получение размера параметра
    if (CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, NULL, &cbContainerName, CRYPT_FIRST))
    {
	printf("CryptGetProvParam PP_ENUMCONTAINERS succeeded. \n");
    }
    else
    {
	HandleError("Error during CryptGetProvParam!");
    }

    // Выделение памяти
    pbContainerName = (LPBYTE)malloc(cbContainerName);

    if (!pbContainerName)
    {
	HandleError("Fail to allocate memory!");
    }

    //------------------------------------------------------------------------
    // Перебор всех контейнеров

    dwFlags = CRYPT_FIRST;
    while (CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, pbContainerName, &cbContainerName, dwFlags | CRYPT_FQCN)) 
    {
	if (CmpCertAndContFP((LPCSTR)pbContainerName, pbFPCert, cbFPCert))
	{
	    printf("Container found! Name: '%s'\n", pbContainerName);
	    break;
	}
	dwFlags = CRYPT_NEXT;
    }
    

    //------------------------------------------------------------------------
    // Освобождение памяти

    free(pbContainerName);

    free(pbFPCert);

    if (hFileStore) 
    {
	if (!CertCloseStore(hFileStore, CERT_CLOSE_STORE_FORCE_FLAG)) 
	{
	    HandleError("Error during CertCloseStore!");
	}
    }

    if (hPublicKey)
    {
	if (!CryptDestroyKey(hPublicKey))
	{
	    HandleError("Error during CryptDestroyKey!");
	}
    }

    if (hCryptProv)
    {
	if (!CryptReleaseContext(hCryptProv, 0))
	{
	    HandleError("Error during CryptReleaseContext!");
	}
    }

    return 0;
}

BOOL CmpCertAndContFP(LPCSTR szContainerName, LPBYTE pbFPCert, DWORD cbFPCert) {

    HCRYPTPROV          hProvCont;
    LPBYTE		pbFPCont;
    DWORD		cbFPCont;
    BOOL		result = FALSE;

    //------------------------------------------------------------------------
    // Получение дескриптора контейнера 

    if (CryptAcquireContextA(
	&hProvCont,
	szContainerName,
	NULL,
	PROV_GOST_2012_256,
	CRYPT_VERIFYCONTEXT))
    {
	printf("CryptAcquireContext for '%s' succeeded. \n", szContainerName);
    }
    else
    {
	HandleError("Error during CryptAcquireContext!\n");
    }

    //------------------------------------------------------------------------
    // Получение параметра PP_SIGNATURE_KEY_FP для ключа в контейнере

    cbFPCont = cbFPCert; // Размер FP из контейнера равен размеру FP из сертификата
    pbFPCont = (LPBYTE)malloc(cbFPCont);

    if (CryptGetProvParam(hProvCont, PP_SIGNATURE_KEY_FP, pbFPCont, &cbFPCont, 0))
    {
	printf("CryptGetProvParam PP_SIGNATURE_KEY_FP for '%s' succeeded. \n", szContainerName);
	// Сравнение FP из сертификата с FP из контейнера
	if (!memcmp(pbFPCont, pbFPCert, cbFPCert)) {
	    result = TRUE;
	    goto Done;
	}
    }

    //------------------------------------------------------------------------
    // Получение параметра PP_EXCHANGE_KEY_FP для ключа в контейнере

    if (CryptGetProvParam(hProvCont, PP_EXCHANGE_KEY_FP, pbFPCont, &cbFPCont, 0))
    {
	printf("CryptGetProvParam PP_EXCHANGE_KEY_FP for '%s' succeeded. \n", szContainerName);
	// Сравнение FP из сертификата с FP из контейнера
	if (!memcmp(pbFPCont, pbFPCert, cbFPCert)) {
	    result = TRUE;
	    goto Done;
	}
    }

Done:

    free(pbFPCont);

    if (hProvCont)
    {
	if (!CryptReleaseContext(hProvCont, 0))
	{
	    HandleError("Error during CryptReleaseContext!");
	}
    }

    return result;
}

// Конец примера
// (не следует удалять данный комментарий, он используется 
//  для автоматической сборки документации)

//  В этом примере используется функция HandleError, функция обработки
//  простых ошибок, для печати сообщения об ошибке в стандартный файл 
//  ошибок (stderr) и выхода из программы. 
//  В большинстве приложений эта функция заменяется другой функцией, 
//  которая выводит более полное сообщение об ошибке.

static void HandleError(const char *s) {
    fprintf(stderr, "An error occurred in running the program. \n");
    fprintf(stderr, "%s\n", s);
    fprintf(stderr, "Error number %x.\n", GetLastError());
    fprintf(stderr, "Program terminating. \n");
    exit(1);
}
