// CCertByDN.cpp : Defines the entry point for the console application.
//

#include  <stdio.h>
#ifdef _WIN32
#include <windows.h>
#define CRYPT_VERIFYCONTEXT     0xF0000000
#define strlib "..\\lib\\strlib.dll"
#else
#define GetProcAddress dlsym
#define FreeLibrary dlclose
#include <string>
#include  <unistd.h>
#include  <dlfcn.h>
#define strlib "../lib/libstr.so.1.0"
#define _cdecl
#define CRYPT_VERIFYCONTEXT 0xF0000000
#define TRUE 1
#define FALSE 0
#endif
typedef unsigned long hPKICONTEXT;
typedef unsigned long hCRYPTMSG;
typedef unsigned long hCERTSTORE;
typedef unsigned long HCRYPTPROV;
typedef unsigned long hCERTCOLLECTION;
#ifndef _WIN32
typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef void *HINSTANCE;
typedef char CHAR;
#endif
// Константы для хранилища :
#define STORE_TYPE "t=A;"
#define STORE_HOST "127.0.0.1""
#define STORE_PORT "62222"
#define DN "CN=ROOT;O=LOCAL;C=KZ;"

typedef int(_cdecl *cspopencontext)
(HCRYPTPROV *phProv,
	CHAR *pszContainer,
	DWORD dwProvType,
	DWORD dwProvParam,
	DWORD dwFlags,
	BYTE * pbSignature,
	DWORD dwSigLen,
	BYTE * pbContainer,
	DWORD * dwContLen,
	HINSTANCE *phLib);
typedef int(_cdecl *crtopenstore)
(DWORD StoreProvider,
	HCRYPTPROV hProv,
	unsigned long dwFlags,
	bool  bFlag,
	const void *pvParam,
	hCERTSTORE *phStore);
typedef int(_cdecl *crtclosestore)    (hCERTSTORE *phStore);
typedef int(_cdecl *cspclosecontext)  (HCRYPTPROV *phProv);
typedef int(_cdecl *crtsetstorepropertyext)
(const hCERTSTORE hStore,
	const unsigned long Flags,
	const char *DName,
	const char *host,
	const char *port,
	const int timeOut,
	const char *name,
	const char *pass);
typedef int(_cdecl *getgroupca)
(hCERTSTORE hStore,
	const char *groupName,
	const char *dataValid,
	const bool isValid,
	int *certCount,
	unsigned char **certBody,
	unsigned long *bodySize);
typedef char* (_cdecl *errorstring) (const int err);

cspopencontext         CSPOpenContext;
crtopenstore           CrtOpenStore;
crtclosestore          CrtCloseStore;
cspclosecontext        CSPCloseContext;
crtsetstorepropertyext CrtSetStorePropertyExt;
getgroupca             GetGroupCA;
errorstring            ErrorString;


HINSTANCE load_lib(char *lib)
{
#ifdef _WIN32
	return LoadLibraryA(lib);
#else
	return dlopen(lib, RTLD_LAZY);
#endif
}
void lib_free(HINSTANCE Instance)
{
#ifdef _WIN32
	FreeLibrary(Instance);
#else
	dlclose(Instance);
#endif
}
int get_func(HINSTANCE lib)
{
	CSPOpenContext = (cspopencontext)GetProcAddress(lib, "CSPOpenContext");
	if (!CSPOpenContext)
	{
		printf("error load function - %s", "CSPOpenContext");
		return 1;
	}
	CrtOpenStore = (crtopenstore)GetProcAddress(lib, "CrtOpenStore");
	if (!CrtOpenStore)
	{
		printf("error load function - %s", "CrtOpenStore");
		return 1;
	}
	CSPCloseContext = (cspclosecontext)GetProcAddress(lib, "CSPCloseContext");
	if (!CSPCloseContext)
	{
		printf("error load function - %s", "CSPCloseContext");
		return 1;
	}
	CrtCloseStore = (crtclosestore)GetProcAddress(lib, "CrtCloseStore");
	if (!CrtCloseStore)
	{
		printf("error load function - %s", "CrtCloseStore");
		return 1;
	}
	CrtSetStorePropertyExt = (crtsetstorepropertyext)GetProcAddress(lib, "CrtSetStorePropertyExt");
	if (!CrtSetStorePropertyExt)
	{
		printf("error load function - %s", "CrtSetStorePropertyExt");
		return 1;
	}
	GetGroupCA = (getgroupca)GetProcAddress(lib, "GetGroupCA");
	if (!GetGroupCA)
	{
		printf("error load function - %s", "GetGroupCA");
		return 1;
	}
	ErrorString = (errorstring)GetProcAddress(lib, "ErrorString");
	if (!ErrorString)
	{
		printf("error load function - %s", "ErrorString");
		return 1;
	}
	return 0;
}
int main(void)
{
	HINSTANCE          lib = NULL;
	int 				ret = 0;
	int 				code = 0;
	int 				i, count;
	hCERTSTORE 		hStore = 0;
	HCRYPTPROV 		hP = 0;
	int 				certCount;
	unsigned char 		**certBody = NULL;
	unsigned long 		*bodySize = NULL;
	try
	{
		lib = load_lib(strlib);
		if (get_func(lib))
		{
			printf("error load func.\n");
			exit(1);
		}
		//получить контекст криптопровайдера
		code = CSPOpenContext(&hP, NULL, 25, 0, CRYPT_VERIFYCONTEXT, NULL, 0, NULL, 0, NULL);
		if (code)
		{
			printf("%s [CSPOpenContext: error = %d]\n", ErrorString(code), code);
			throw code;
		}
		//открыть хранилище сертификатов
		code = CrtOpenStore(0, hP, 0, false, NULL, &hStore);
		if (code)
		{
			printf("%s [CrtOpenStore: error = %d]\n", ErrorString(code), code);
			throw code;
		}
		//настроить хранилище сертификатов (обязательно, при подключении к удалённому хранилищу LDAP)
		code = CrtSetStorePropertyExt(hStore, 0, STORE_TYPE, STORE_HOST, STORE_PORT, 300, 0, 0);
		if (code)
		{
			printf("%s [CrtSetStorePropertyExt: error = %d]\n", ErrorString(code), code);
			throw code;
		}
		//получить сертификаты CA по DN имени (функция работает с LDAP хранилищем)
		//получить количествово сертификатов
		code = GetGroupCA(hStore, DN, NULL, true, &certCount, NULL, NULL);
		if (code)
		{
			printf("%s [GetGroupCA: error = %d]\n", ErrorString(code), code);
			throw code;
		}
		if (certCount)
		{
			count = certCount;
			bodySize = (unsigned long*)calloc(certCount, sizeof(unsigned long));
			//получить размеры сертификатов
			code = GetGroupCA(hStore, DN, NULL, true, &certCount, NULL, bodySize);
			if (code)
			{
				printf("%s [GetGroupCA: error = %d]\n", ErrorString(code), code);
				throw code;
			}
			certBody = (unsigned char**)calloc(certCount, sizeof(unsigned char*));
			for (i = 0; i<certCount; i++)
				certBody[i] = (unsigned char*)calloc(bodySize[i], sizeof(unsigned char));
			//получить сертификаты
			code = GetGroupCA(hStore, DN, NULL, true, &certCount, certBody, bodySize);
			if (code)
			{
				printf("%s [GetGroupCA: error = %d]\n", ErrorString(code), code);
				throw code;
			}
			printf("Found %d CA certificates\n", certCount);
		}
		else { printf("Certificate Ca not found\n"); throw code; }

	}
	catch (int er) { ret = er; }
	if (certBody)
	{
		for (i = 0; i<count; i++)
			if (certBody[i]) { free(certBody[i]); certBody[i] = NULL; }
		free(certBody); certBody = NULL;
	}
	if (bodySize) { free(bodySize); bodySize = NULL; }
	//закрыть хранилище сертификатов
	if (hStore)
	{
		CrtCloseStore(&hStore);
		hStore = (hCERTSTORE)NULL;
	}
	//удалить контекст криптопровайдера
	if (hP)
	{
		CSPCloseContext(&hP);
		hP = (HCRYPTPROV)NULL;
	}
	if (lib) lib_free(lib);
	return ret;
}
