/*************************************************************************
* Rutoken                                                                *
* Copyright (c) 2003-2017, CJSC Aktiv-Soft. All rights reserved.         *
* Подробная информация:  http://www.rutoken.ru                           *
*------------------------------------------------------------------------*
* Пример работы Рутокен с криптопровайдером КриптоПро CSP                *
* с использованием интерфейса CryptoAPI на языке C                       *
*------------------------------------------------------------------------*
* Удаление ключевого контейнера КриптоПро CSP с носителя Рутокен         *
* в тихом режиме:                                                        *
*  - проверка наличия криптопровайдера КриптоПро CSP в системе;          *
*  - инициализация криптопровайдера для работы в тихом режиме;           *
*  - получение дружественного FQCN имени контейнера;                     *
*  - установка PIN-кода Рутокен в контекст криптопровайдера;             *
*  - удаление контейнера с объектами в тихом режиме;                     *
*  - освобождение контекста криптопровайдера и памяти.                   *
*------------------------------------------------------------------------*
* Пример использует объекты, созданные в памяти Рутокен примером         *
* CreateContainer.                                                       *
*************************************************************************/

#include "Common.h"

int main(void)
{
	LPCWSTR szProvNameW = CRYPTOPRO_PROV_W; // Имя криптопровайдера, заменить на CRYPTOPRO_FKN_PROV_W для ФКН
	LPCSTR szProvNameA = CRYPTOPRO_PROV_A;  // Имя криптопровайдера, заменить на CRYPTOPRO_FKN_PROV_A для ФКН
	DWORD dwProvType = CRYPTOPRO_PROV_TYPE; // Тип криптопровайдера
	HCRYPTPROV hProv = 0;                   // Дескриптор криптопровайдера
	BYTE* pbUnicContName = NULL;            // Буфер для FQCN имени контейнера
	DWORD dwUnicNameSize = 0;               // Длина FQCN имени контейнера

	for (;; ) {
		/**********************************************************************
		* Шаг 1. Проверка наличия выбранного криптопровайдера в системе       *
		**********************************************************************/
		wprintf(L"Checking whether %s provider exists", szProvNameW);
		if (!CryptAcquireContextW(
		                          &hProv,             // Указатель на дескриптор криптопровайдера
		                          NULL,               // Имя ключевого контейнера
		                          szProvNameW,        // Имя криптопровайдера
		                          dwProvType,         // Тип криптопровайдера
		                          CRYPT_VERIFYCONTEXT // Флаг операции, не требующей работы с контейнером
		                          )) {
			if (GetLastError() == NTE_KEYSET_NOT_DEF) {
				wprintf(L" -> FAILED \nProvider has not been installed\n\n");
			} else {
				wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			}
			break;
		}
		wprintf(L" -> OK\n");

		/**********************************************************************
		* Освобождение контекста криптопровайдера                             *
		**********************************************************************/
		CryptReleaseContext(hProv, 0);
		hProv = 0;

		/**********************************************************************
		* Шаг 2. Инициализация криптопровайдера                               *
		**********************************************************************/
		wprintf(L"Initializing cryptoprovider");
		if (!CryptAcquireContextW(
		                          &hProv,          // Дескриптор криптопровайдера
		                          CONT_NAME_W,     // Имя ключевого контейнера
		                          szProvNameW,     // Имя криптопровайдера
		                          dwProvType,      // Тип криптопровайдера
		                          CRYPT_SILENT)) { // Флаг тихого режима
			if (GetLastError() == NTE_BAD_KEYSET_PARAM || GetLastError() == NTE_BAD_KEYSET) {
				wprintf(L" -> FAILED, container does not exist (0x%0.8x)\n\n", GetLastError());
			} else {
				wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			}
			break;
		}
		wprintf(L" -> OK\n");

		/**********************************************************************
		* Шаг 3. Получение дружественного FQCN имени контейнера               *
		**********************************************************************/
		wprintf(L"Getting unique container name");

		/**********************************************************************
		* Получение размера буфера для имени контейнера                       *
		**********************************************************************/
		if (!CryptGetProvParam(
		                       hProv,           // Дескриптор криптопровайдера
		                       PP_FQCN,         // Флаг получения FQCN имени контейнера
		                       NULL,            // Указатель на буфер для получения имени контейнера
		                       &dwUnicNameSize, // Указатель на длину буфера
		                       0)) {
			wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			break;
		}

		/**********************************************************************
		* Выделение памяти для буфера                                         *
		**********************************************************************/
		pbUnicContName = (BYTE*)malloc(dwUnicNameSize * sizeof(BYTE));
		if (!pbUnicContName) {
			wprintf(L" -> FAILED, out of memory\n\n");
			break;
		}

		/**********************************************************************
		* Получение указателя на буфер с именем контейнера                    *
		**********************************************************************/
		if (!CryptGetProvParam(
		                       hProv,           // Дескриптор криптопровайдера
		                       PP_FQCN,         // Флаг получения FQCN имени контейнера
		                       pbUnicContName,  // Указатель на буфер для получения имени контейнера
		                       &dwUnicNameSize, // Указатель на длину буфера
		                       0)) {
			wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			break;
		}
		wprintf(L" -> OK\n");

		/**********************************************************************
		* Освобождение контекста криптопровайдера                             *
		**********************************************************************/
		CryptReleaseContext(hProv, 0);
		hProv = 0;

		/**********************************************************************
		* Шаг 4. Инициализация криптопровайдера для работы в тихом режиме     *
		**********************************************************************/
		wprintf(L"Initializing cryptoprovider for silent mode");

		/**********************************************************************
		* Используем CryptAcquireContextA, поскольку pbUnicContName является  *
		* ANSI строкой                                                        *
		**********************************************************************/
		if (!CryptAcquireContextA(
		                          &hProv,                // Дескриптор криптопровайдера
		                          (LPSTR)pbUnicContName, // FQCN имя ключевого контейнера
		                          szProvNameA,           // Имя криптопровайдера
		                          dwProvType,            // Тип криптопровайдера
		                          CRYPT_SILENT)) {       // Флаг тихого режима
			wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			break;
		}
		wprintf(L" -> OK\n");

		/**********************************************************************
		* Шаг 5. Передача PIN-кода Рутокен в параметры криптопровайдера       *
		**********************************************************************/
		wprintf(L"Setting Rutoken PIN-code");
		if (!CryptSetProvParam(
		                       hProv,              // Дескриптор криптопровайдера
		                       PP_KEYEXCHANGE_PIN, // Флаг передачи PIN-кода
		                       USER_PIN,           // Значение PIN-кода
		                       0)) {
			wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			break;
		}
		wprintf(L" -> OK\n");

		/**********************************************************************
		* Шаг 6. Удаление контейнера с заданными параметрами                  *
		**********************************************************************/
		printf("Deleting key container with name \"%s\"", pbUnicContName);
		if (!CryptSetProvParam(
		                       hProv,            // Дескриптор криптопровайдера
		                       PP_DELETE_KEYSET, // Флаг удаления контейнера
		                       0,
		                       0)) {
			wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
			break;
		}
		wprintf(L" -> OK\n");
		wprintf(L"\nContainer \"%s\" has been destroyed.\n\n", CONT_NAME_W);
		break;
	}

	/**********************************************************************
	* Шаг 7. Освобождение контекста криптопровайдера и памяти             *
	**********************************************************************/
	if (hProv) {
		CryptReleaseContext(hProv, 0);
	}

	free(pbUnicContName);

	if (GetLastError() == ERROR_SUCCESS || GetLastError() == NTE_BAD_KEYSET_PARAM) {
		wprintf(L"Test has been completed successfully.");
	} else {
		wprintf(L"Test has failed. Error number: 0x%0.8x.", GetLastError());
	}
	return 0;
}
