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


#include <stdio.h>
 
#ifdef _WIN32

#ifdef _DEBUG
# pragma comment(lib, "comsuppwd.lib")
#else
# pragma comment(lib, ";omsuppw.lib")
#endif
  #include <comutil.h>
  #include <windows.h>
  #include <wincrypt.h>
  #include <wincryptex.h>
#else
  #include <string.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <WinCryptEx.h>
#endif	// _WIN32
#include "UnixEnroll.h"
#define TYPE_DER (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)

//--------------------------------------------------------------------
// Пример кода для создания запроса получения сертификата

// Для функционирования данного кода необходим контейнер с ключом 
// AT_KEYEXCHANGE с именем Test(Unix) или контейнер по умолчанию(WIN),
// Ключ можно создать используя пример CryptAcquireContext.
//--------------------------------------------------------------------


static void HandleError(const char *s);


#ifdef _WIN32
#   define CONTAINER L"test"
#else
#   define CONTAINER L"\\\\.\\HDIMAGE\\test"
#endif

class Callbacks: public UnixEnroll::UserCallbacks
{
public:
	bool askPermissionToAddToRootStore( const BYTE* pbCert, DWORD cbCert, bool force = false) const;
	UserCallbacks* clone() const;
	virtual ~Callbacks() {}
};

//--------------------------------------------------------------------
// Функция askPermissionToAddToRootStore всегда будет разрешать 
// записывать сертификат в корневое хранилище

bool Callbacks::askPermissionToAddToRootStore( const BYTE* pbCert, DWORD cbCert, bool) const
{
  	int res;
	DWORD size = 0;
	PBYTE pHashValue = NULL;
	res = CryptHashCertificate(0,0,0,
							   pbCert, cbCert,
							   NULL,&size);
	if(!res) {
	    return false;
	}
	
	pHashValue =(BYTE *)malloc(size);
	if(!pHashValue) {
	    return false;
	}
	
	res = CryptHashCertificate(0,0,0,
							   pbCert, cbCert,
							   pHashValue,&size);
    
    printf("trying to add certificate to root store : %s\n\n", (char*) pHashValue);
	if(!res) {
	    free(pHashValue);
	    return false;
	}
	
    
    free (pHashValue);
    
	return true;
}

UnixEnroll::UserCallbacks* Callbacks::clone() const
{
	return new Callbacks();
}

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

void HandleError(const char *s)
{
	DWORD err = GetLastError();
	printf("Error number     : 0x%x\n", err);
	printf("Error description: %s\n", s);
	if(!err) err = 1;
	exit(err);
} 	

static long create_request(BSTR bstrRDN, BSTR bstrEKUsage, BSTR contName, char** chTmp)
{
	HRESULT res = -1;
	Callbacks tb;
    	
	DWORD dwKeySpec = AT_KEYEXCHANGE;
	BSTR strRequest = 0;
	UnixEnroll * pEnroll = new UnixEnroll(tb, false);

	//------------------------------------------------------------------------------------
	// put_ContainerName создает имя контейнера
	pEnroll->put_ContainerName(contName);
	//------------------------------------------------------------------------------------
	// put_LimitExchangeKeyToEncipherment задает ограничения ключа AT_KEYEXCHANGE, false
	// и шифрование, и подпись, true - только шифрование
	pEnroll->put_LimitExchangeKeyToEncipherment(TRUE);
	//------------------------------------------------------------------------------------
	// put_KeySpec задает тип закрытого ключа
	pEnroll->put_KeySpec(dwKeySpec);
	//------------------------------------------------------------------------------------
	// put_ProviderType задает тип CSP для создания ключа.
	pEnroll->put_ProviderType(PROV_GOST_2001_DH);
	//------------------------------------------------------------------------------------
	// put_RequestStoreFlags задаеь хранилище запроса и флаги его открытия.
	pEnroll->put_RequestStoreFlags(CERT_SYSTEM_STORE_CURRENT_USER);
	//------------------------------------------------------------------------------------
	// происходит создание запроса функцией createRequest
	res = pEnroll->createRequest(XECR_PKCS10_V2_0, bstrRDN, bstrEKUsage, &strRequest);
	*chTmp = ConvertBSTRToString(strRequest);
	printf("charRequest = %s\n",*chTmp);

	delete pEnroll;
    	
	return res;
}

int main(int argc, char* argv[])
{
    char* chTmp = 0;
    HRESULT res;
    BSTR bstrRDN = SysAllocString(L"1.2.840.113549.1.9.1=user@cryptopro.ru, 2.5.4.6=RU, 2.5.4.7=moscow, 2.5.4.10=cpro, 2.5.4.3=common name");
    BSTR bstrEKUsage = SysAllocString(L"1.3.6.1.5.5.7.3.2");
    BSTR contName = SysAllocString(CONTAINER);
    FILE* f;
    if (argc == 2)
        f = fopen(argv[1], "wb");
    else
        f = fopen("request.p10", "wb");
    
    res = create_request(bstrRDN, bstrEKUsage, contName, &chTmp);
	
    if (res != S_OK){
	HandleError( "Error create_request.");
	goto er;
    } else {
	printf("Successful creating request. \n");
    }
	
    fputs(chTmp,f);

er :
    fclose(f);
    SysFreeString(bstrRDN);
    SysFreeString(bstrEKUsage);
    SysFreeString(contName);
    free(chTmp);
    return res;
}//end of main
