/*************************************************************************
* Rutoken                                                                *
* Copyright (c) 2003-2017, CJSC Aktiv-Soft. All rights reserved.         *
* Подробная информация:  http://www.rutoken.ru                           *
*------------------------------------------------------------------------*
* В данном файле содержится реализация функции импорта сертификата в     *
* контейнер при помощи программы Cert2Cont.                              *
*************************************************************************/


#include "stdafx.h"
#include "Import.h"
#include "Keys.h"
#include "Util_Funcs.h"
#include "Cert2Cont.h"


/************************************************************************
* Импортировать сертификат в контейнер                                  *
************************************************************************/
BOOL ImportCertificateToContainer(IN LPWSTR lpszCSPs[],
                                  IN DWORD pdwCSPTypes[],
                                  IN DWORD dwSelectedCSP,
                                  IN LPWSTR lpszContainers[],
                                  IN DWORD dwSelectedContainer,
                                  IN DWORD dwPreferredKeySpec,
                                  IN LPVOID lpvCertBuffer,
                                  IN DWORD dwCertBufferSize)
{
	BOOL bResult = TRUE;                                    // Вспомогательная переменная для хранения результата выполнения функции

	HCRYPTPROV hCryptProv = 0;                              // Хэндл криптопровайдера
	HCRYPTKEY hCryptKey = 0;                                // Хэндл ключевой пары
	DWORD dwKeySpec = 0;

	PCCERT_CONTEXT pCertContext = NULL;                     // Указатель на структуру данных типа CERT_CONTEXT

	PCERT_PUBLIC_KEY_INFO pContPubKeyInfo = NULL;           // Указатель на структуру данных типа CERT_PUBLIC_KEY_INFO
	DWORD dwContPubKeyInfoLen = 0;                          // Размер буфера в байтах

	for (;; ) {
		/************************************************************************
		* Получить хэндл криптопровайдера                                       *
		************************************************************************/
		bResult = CryptAcquireContextW(&hCryptProv,
		                               lpszContainers[dwSelectedContainer],
		                               lpszCSPs[dwSelectedCSP],
		                               pdwCSPTypes[dwSelectedCSP],
		                               0);

		if (!bResult) {
			hCryptProv = 0;
			PrintErrorText(L"CryptAcquireContextW",
			               GetLastError());
			break;
		}

		/************************************************************************
		* Получить хэндл ключевой пары с заданным типом                         *
		************************************************************************/
		bResult = GetCryptKey(hCryptProv,
		                      dwPreferredKeySpec,
		                      &hCryptKey,
		                      &dwKeySpec);
		if (!bResult) {
			break;
		}

		/************************************************************************
		* Получить данные об открытом ключе для выбранной пары в контейнере     *
		************************************************************************/
		bResult = GetPublicKeyBlobFromCryptProv(hCryptProv,
		                                        dwKeySpec,
		                                        &pContPubKeyInfo,
		                                        &dwContPubKeyInfoLen);
		if (!bResult) {
			break;
		}

		/************************************************************************
		* Получить данные об открытом ключе из тела переданного сертификата     *
		************************************************************************/
		pCertContext = CertCreateCertificateContext(C2C_CERT_ENCODING,
		                                            (const BYTE*)lpvCertBuffer,
		                                            dwCertBufferSize);
		bResult = (pCertContext != NULL);
		if (!bResult) {
			break;
		}


		/************************************************************************
		* Сравнить открытые ключи                                               *
		************************************************************************/
		bResult = CertComparePublicKeyInfo(C2C_CERT_ENCODING,
		                                   pContPubKeyInfo,
		                                   &(pCertContext->pCertInfo->SubjectPublicKeyInfo));
		if (!bResult) {
			wprintf(L"Error: certificate doesn't correspond with public key in container.\n");
			break;
		}

		/************************************************************************
		* Импортировать сертификат в контейнер                                  *
		************************************************************************/
		bResult = CryptSetKeyParam(hCryptKey,
		                           KP_CERTIFICATE,
		                           (const BYTE*)lpvCertBuffer,
		                           0);

		if (!bResult) {
			PrintErrorText(L"CryptSetKeyParam",
			               GetLastError());
			break;
		}

		break;
	}

	/************************************************************************
	* Освободить память, использованную для хранения                        *
	* структуры CERT_PUBLIC_KEY_INFO                                        *
	************************************************************************/
	LocalFree(pContPubKeyInfo);

	/************************************************************************
	* Освободить память, использованную для хранения                        *
	* структуры CERT_CONTEXT                                                *
	************************************************************************/
	if (pCertContext) {
		CertFreeCertificateContext(pCertContext);
	}

	/************************************************************************
	* Освободить хэндл ключевой пары                                        *
	************************************************************************/
	if (hCryptKey) {
		CryptDestroyKey(hCryptKey);
	}

	/************************************************************************
	* Освободить хэндл криптопровайдера                                     *
	************************************************************************/
	if (hCryptProv) {
		CryptReleaseContext(hCryptProv,
		                    0);
	}

	return bResult;
}
