// SignMes.cpp : Defines the entry point for the console application.
//
#define CRYPT_VERIFYCONTEXT  0xF0000000
#define MAX_BASE_DENCODED_SIZE(base_size)  (((base_size*3)/4)+4)
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <windows.h>
#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 TRUE 1
#define FALSE 0
#endif
#ifndef _WIN32
typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef void *HINSTANCE;
typedef char CHAR;
#endif
#define MORE_ONE_CA        0
#define MAXCERTSIZE        8196
#define CMS_SIGN_DATA_OID  "1.2.840.113549.1.7.2"
#ifdef _WIN32
#endif
typedef unsigned long hCERTSTORE;
typedef unsigned long hCERTCONTEXT;
typedef unsigned long hCERTCOLLECTION;
typedef unsigned long hCRYPTMSG;
typedef unsigned long HCRYPTPROV;

#define AT_KEYEXCHANGE          1
#define AT_SIGNATURE            2

typedef int(_cdecl *errorstring)(const int err);
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 *setldapver) (hCERTSTORE hStore, int ver);
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 *getmycertificate)
(hCERTSTORE hStore,
	const char *DName,
	const unsigned long keySpec,
	const bool isCA,
	unsigned char *certBody,
	unsigned long *bodySize);
typedef int(_cdecl *getcertificatecontext)
(const hCERTSTORE hStore,
	const unsigned char *certBody,
	const unsigned long bodySize,
	hCERTCONTEXT *phContext);
typedef int(_cdecl *getcertsubjectkey)
(const hCERTSTORE hStore,
	const unsigned char *certBody,
	const unsigned long bodySize,
	char *keyOID,
	unsigned char *key,
	unsigned long *keySize);
typedef int(_cdecl *getcertsignature)
(const hCERTSTORE hStore,
	const unsigned char *certBody,
	const unsigned long bodySize,
	char *sigOID,
	unsigned char *sign,
	unsigned long *signSize);
typedef int(_cdecl *cspoidtohashoidex)
(HCRYPTPROV hProv,
	const char *OID,
	char *hashOID);

typedef int(_cdecl *signmsgopen)
(HCRYPTPROV hProv,
	int Flags,
	hCRYPTMSG *phCrypt);
typedef int(_cdecl *signmessage)
(const hCRYPTMSG hCrypt,
	const hCERTSTORE hStore,
	const hCERTCONTEXT MYCert,
	const char *hashOID,
	const char *signOID,
	const char *dataOID,
	const bool Final,
	const unsigned char *dataToSigned,
	const unsigned long dataSize,
	const char *dataDescriptor,
	const unsigned long Flags,
	unsigned char *signedBlob,
	unsigned long *sizeBlob);
typedef int(_cdecl *bintobase)
(unsigned char *inBuf,
	long inSize,
	unsigned char *ouBuf,
	long *outSize);
typedef int(_cdecl *releasecertificatecontext)
(const hCERTSTORE hStore,
	hCERTCONTEXT *phContext);
typedef int(_cdecl *signmsgclose)
(hCRYPTMSG *phCrypt);
typedef int(_cdecl *crtclosestore)(hCERTSTORE *phStore);
typedef int(_cdecl *cspclosecontext)
(HCRYPTPROV *phProv);
cspopencontext				CSPOpenContext;
errorstring					ErrorString;
crtopenstore				CrtOpenStore;
setldapver					SetLDAPver;
crtsetstorepropertyext		CrtSetStorePropertyExt;
getmycertificate			GetMYCertificate;
getcertificatecontext		GetCertificateContext;
getcertsubjectkey			GetCertSubjectKey;
getcertsignature			GetCertSignature;
cspoidtohashoidex			CSPOIDtoHashOIDex;
signmsgopen					SignMsgOpen;
signmessage					SignMessage;
bintobase					BinToBase;
releasecertificatecontext	ReleaseCertificateContext;
signmsgclose				SignMsgClose;
crtclosestore				CrtCloseStore;
cspclosecontext				CSPCloseContext;

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;
	}
	GetMYCertificate = (getmycertificate)GetProcAddress(lib, "GetMYCertificate");
	if (!GetMYCertificate)
	{
		printf("error load function - %s", "GetMYCertificate");
		return 1;
	}
	GetCertificateContext = (getcertificatecontext)GetProcAddress(lib, "GetCertificateContext");
	if (!GetCertificateContext)
	{
		printf("error load function - %s", "GetCertificateContext");
		return 1;
	}
	GetCertSubjectKey = (getcertsubjectkey)GetProcAddress(lib, "GetCertSubjectKey");
	if (!GetCertSubjectKey)
	{
		printf("error load function - %s", "GetCertSubjectKey");
		return 1;
	}
	GetCertSignature = (getcertsignature)GetProcAddress(lib, "GetCertSignature");
	if (!GetCertSignature)
	{
		printf("error load function - %s", "GetCertSignature");
		return 1;
	}
	CSPOIDtoHashOIDex = (cspoidtohashoidex)GetProcAddress(lib, "CSPOIDtoHashOIDex");
	if (!CSPOIDtoHashOIDex)
	{
		printf("error load function - %s", "CSPOIDtoHashOIDex");
		return 1;
	}
	BinToBase = (bintobase)GetProcAddress(lib, "BinToBase");
	if (!BinToBase)
	{
		printf("error load function - %s", "BinToBase");
		return 1;
	}
	ReleaseCertificateContext = (releasecertificatecontext)GetProcAddress(lib, "ReleaseCertificateContext");
	if (!ReleaseCertificateContext)
	{
		printf("error load function - %s", "ReleaseCertificateContext");
		return 1;
	}
	SignMessage = (signmessage)GetProcAddress(lib, "SignMessage");
	if (!SignMessage)
	{
		printf("error load function - %s", "SignMessage");
		return 1;
	}
	SignMsgOpen = (signmsgopen)GetProcAddress(lib, "SignMsgOpen");
	if (!SignMsgOpen)
	{
		printf("error load function - %s", "SignMsgOpen");
		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;
	}
	SignMsgClose = (signmsgclose)GetProcAddress(lib, "SignMsgClose");
	if (!SignMsgClose)
	{
		printf("error load function - %s", "SignMsgClose");
		return 1;
	}
	ErrorString = (errorstring)GetProcAddress(lib, "ErrorString");
	if (!ErrorString)
	{
		printf("error load function - %s", "ErrorString");
		return 1;
	}
	CrtSetStorePropertyExt = (crtsetstorepropertyext)GetProcAddress(lib, "CrtSetStorePropertyExt");
	if (!CrtSetStorePropertyExt)
	{
		printf("error load function - %s", "CrtSetStorePropertyExt");
		return 1;
	}
	SetLDAPver = (setldapver)GetProcAddress(lib, "SetLDAPver");
	if (!SetLDAPver)
	{
		printf("error load function - %s", "SetLDAPver");
		return 1;
	}
	return 0;
}
//-----------------------------------------------------------------------
int file_read(char* file, unsigned char **data, long *size);
//-----------------------------------------------------------------------
int main(int argc, char *argv[])
{
	HINSTANCE lib = NULL;
	FILE *f;
	char signOID[32];
	char hashOID[32];
	char keyOID[32];
	char host[16];
	char port[6];
	char profile[260];
	char err[256];
	char fileCert[260];
	char filename[256];
	char DN[260];
	char comment[] = "Comment";
	int  datalen = 0;
	int  res = 0;
	int  addCertificateFlag =	2;
	long cmcSize = 0;
	unsigned char *crtSign = NULL;
	unsigned char *cmsBlob = NULL;
	unsigned char *cmcBlob = NULL;
	unsigned char *Data = NULL;
	unsigned char *dataToSign = NULL;
	unsigned char *dataToHashed = NULL;
	unsigned long dataSize = 0;
	unsigned long hashSize = 0;
	unsigned long signSize = 0;
	unsigned long crtSize = 0;
	unsigned long cmsSize = 0;
	hCERTSTORE    hStore = 0;
	HCRYPTPROV    hProv = 0;
	hCRYPTMSG     hCMSSign = 0;
	hCERTCONTEXT  hSign = 0;

	if (argc != 6)
	{
		printf("Specify parameters (ex. []$ ./bin/Sign profile  datafile DN host port)\n");
		return 1;
	}
	if (argv[1])
#ifdef _WIN32
		sprintf(profile, "profile://%s\0", argv[1]);
#else
		sprintf(profile, "profile://%s\0", argv[1]);
#endif
	else
		return 1;
	if (argv[2])
	{
		// filename - Name of file contain Open Data to be signed.
		strcpy(filename, argv[2]);
	}
	else
		return 2;

	if (argv[3])
	{
		strcpy(DN , argv[3]);
	}
	else
		return 3;

	if (argv[4])
		strcpy(host, argv[4]);
	else
		return 4;
	if (argv[5])
		strcpy(port, argv[5]);
	else
		return 5;
	try
	{
		lib = load_lib(strlib);
		if (get_func(lib))
		{
			printf("error load func.\n");
			exit(1);
		}
		// Получить данные для подписи
		printf("Open data file %s\n", filename);
		res = file_read(filename, &Data, (long*)&dataSize);
		if (res != 0)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "Open file %s Error\n", filename);
			throw res;
		}

		// Получить контекст криптопровайдера
		printf("Open CSP Context\n");
		res = CSPOpenContext(&hProv, profile, 25, 0, 0, NULL, 0, NULL, NULL, NULL);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [CSPOpenContext - %d] profile=[%s]\n", ErrorString(res), res, (char *)profile);
			throw res;
		}
		// Получить контекст хранилища LDAP
		printf("Open Store\n");
		res = CrtOpenStore(0, hProv, MORE_ONE_CA, false, NULL, &hStore);
		if (res)
		{
			sprintf(err, "%s  [CrtOpenStore - %d]\n", ErrorString(res), res);
			throw res;
		}
		//Установить версию протокола LDAP
		SetLDAPver(hStore, 3);
		// Настроить хранилище сертификатов
		// если есть возможность загрузить сертификат из файла, то можно вызывать сразу GetCertificateContext 
		printf("Set Store properties\n");
		res = CrtSetStorePropertyExt(hStore, MORE_ONE_CA, "t=L;", host, port, 30, NULL, NULL);
		if (res)
		{
			sprintf(err, "%s  [CrtSetStorePropertyExt - %d] \n", ErrorString(res), res);
			throw res;
		}
		// Получить сертификат из хранилища
		printf("Get My Certificate\n");
		crtSign = new unsigned char[4096];
		memset(crtSign, 0, 4096);
		res = GetMYCertificate(hStore, DN, AT_SIGNATURE, false, crtSign, &crtSize);
		if (res)
		{
			sprintf(err, "%s  [2 - %s\n GetMYCertificate - %d]\n", ErrorString(res), crtSign, res);
			throw res;
		}
		// Получить контекст сертификата для передачи в функцию SignMessage
		printf("Get certificate context\n");
		res = GetCertificateContext(hStore, crtSign, crtSize, &hSign);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [GetCertificateContext - %d]\n", ErrorString(res), res);
			throw res;
		}
		// Получить алгоритм ключа
		printf("Get key OID\n");
		memset(keyOID, 0, sizeof(keyOID));
		res = GetCertSubjectKey(hStore, crtSign, crtSize, keyOID, NULL, NULL);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [GetCertSubjectKey - %d]\n", ErrorString(res), res);
			throw res;
		}
		// Получить алгоритм подписи
		printf("Get sign OID\n");
		memset(signOID, 0, sizeof(signOID));
		res = GetCertSignature(hStore, crtSign, crtSize, signOID, NULL, NULL);
		if (res)
		{
			sprintf(err, "%s  [GetCertSignature - %d]\n", ErrorString(res), res);
			throw res;
		}
		printf("Get hash OID\n");
		// Получить алгоритм хеш-функции
		res = CSPOIDtoHashOIDex(hProv, signOID, hashOID);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [CSPOIDtoHashOIDex - %d]\n", ErrorString(res), res);
			throw res;
		}
		// Открыть контекст крипто-функции
		printf("Get CMS context\n");
		res = SignMsgOpen(hProv, 0, &hCMSSign);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [SignMsgOpen - %d]\n", ErrorString(res), res);
			throw res;
		}
		// Подписать сообщение 
		printf("Sign message\n");
		// addCertificateFlag:
		// 0 - includes Data in CMS
		// 1 - includes Data and User Certificate in CMS
		// 2 - DetachedSignature (no data and no certificate)
		// 3 - includes User Certificate in CMS, no data
		printf("Sign message1\n");
		res = SignMessage(hCMSSign, hStore, hSign, hashOID, signOID, CMS_SIGN_DATA_OID, true, Data, dataSize, comment, addCertificateFlag, NULL, &cmsSize);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [SignMessage - %d]\n", ErrorString(res), res);
			throw res;
		}
		printf("SifnMessageSize = [%d]\n", cmsSize);
		cmsBlob = new unsigned char[cmsSize];
		printf("Sign message2\n");
		res = SignMessage(hCMSSign, hStore, hSign, hashOID, signOID, CMS_SIGN_DATA_OID, true, Data, dataSize, comment, addCertificateFlag, cmsBlob, &cmsSize);
		if (res)
		{
			memset((char *)err, 0, 256);
			sprintf(err, "%s  [SignMessage - %d]\n", ErrorString(res), res);
			throw res;
		}
		printf("SignMessageSize = [%d]\n", cmsSize);
		printf("Save CMS in file message.cms \n");
		if ((f = fopen("message.cms", "wb")) == NULL) throw 1;
		cmcBlob = new unsigned char[cmsSize * 2];
		BinToBase(cmsBlob, cmsSize, cmcBlob, &cmcSize);
		// fwrite(cmcBlob,sizeof(unsigned char),cmcSize,f);
		fwrite(cmsBlob, sizeof(unsigned char), cmsSize, f);
		fclose(f);
	}
	catch (int error)
	{
		printf("Error = %d. %s, \n", error, err);
	}
	printf("Exit\n");
	if (crtSign) delete[]  crtSign;
	if (cmsBlob) delete[]  cmsBlob;
	if (cmcBlob) delete[]  cmcBlob;
	if (Data) delete[]  Data;
	if (hSign) ReleaseCertificateContext(hStore, &hSign);
	if (hCMSSign) SignMsgClose(&hCMSSign);
	if (hStore != 0) CrtCloseStore(&hStore);
	if (hProv != 0) CSPCloseContext(&hProv);
	if (lib) lib_free(lib);

	return res;
}
//------------------------------------------------------------------------------
int file_read(char* file, unsigned char **data, long *size)
{
	FILE *in = NULL;
	struct stat st;
	if (stat(file, &st)) return 1; // File not found ... 
	*size = st.st_size;
	*data = new unsigned char[*size];
	if ((in = fopen(file, "rb")) == NULL)
	{
		free(*data);
		*size = 0;
		*data = NULL;
		return 2;  // File not opened ... 
	}
	fread(*data, *size, 1, in);
	fclose(in);
	return 0;
}
//-----------------------------------------------------------------------
