#define MORE_ONE_CA          0
#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 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.1.0.0.sl"
  #define _cdecl
  #define CRYPT_VERIFYCONTEXT 0xF0000000
  #define TRUE 1
  #define FALSE 0
#endif
#ifndef _WIN32
  typedef unsigned long DWORD;
  typedef unsigned char BYTE;
  typedef void *HINSTANCE;
  typedef char CHAR;
#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 void (_cdecl *setldapver)(hCERTSTORE hStore, int ver);
typedef int (_cdecl *signmsgopen)
                                                        (HCRYPTPROV hProv,
                                                        int Flags,
                                                        hCRYPTMSG *phCrypt);
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 char* (_cdecl *errorstring)                     (const int err);
typedef int (_cdecl *basetobin)
							(unsigned char *inBuf,
							long inSize,
							unsigned char *ouBuf,
							long *outSize);
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 *messagename)
							(const unsigned char *cmsData,
							const unsigned long dataSize,
							int *senderCount,
							char **senderName,
							int *recipCount,
							char **recipName);
typedef int (_cdecl *getmessagecertificate)
							(const hCERTSTORE hStore,
							const unsigned char *CMSBlob,
							const unsigned long sizeBlob,
							hCERTCOLLECTION *hCollection);
typedef int (_cdecl *decryptmessage)
							(const hCRYPTMSG hCrypt,
							const hCERTCOLLECTION hCollection,
							const hCERTCONTEXT MYCert,
							const bool Final,
							const unsigned char *encryptedBlob,
							const unsigned long sizeBlob,
							char *SenderName,
							char *timeToCrypted,
							char *dataDescriptor,
							unsigned char *dataToDecrypted,
							unsigned long *dataSize);
typedef int (_cdecl *messagetype)
							(const unsigned char *cmsData,
							const unsigned long dataSize,
							int  *type);
typedef int (_cdecl *freecertificatecollection) (hCERTCOLLECTION *phCollection);
typedef int (_cdecl *signmsgclose) (hCRYPTMSG *phCrypt);
typedef int (_cdecl *crptmsgclose) (hCRYPTMSG *phCrypt);

cspopencontext				CSPOpenContext;
crtopenstore				CrtOpenStore;
crtclosestore				CrtCloseStore;
cspclosecontext				CSPCloseContext;
errorstring					ErrorString;
basetobin					BaseToBin;
crtsetstorepropertyext		CrtSetStorePropertyExt;
getmycertificate			GetMYCertificate;
getcertificatecontext		GetCertificateContext;
messagename					MessageName;
getmessagecertificate		GetMessageCertificate;
decryptmessage				DecryptMessage;
messagetype					MessageType;
freecertificatecollection	FreeCertificateCollection;
signmsgclose				SignMsgClose;
crptmsgclose                    CrptMsgClose;
setldapver                      SetLDAPver;
signmsgopen                     SignMsgOpen;

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;
   }
   ErrorString = (errorstring) GetProcAddress(lib,"ErrorString");
   if(!ErrorString)
   {
       printf("error load function - %s", "ErrorString");
       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;
   } 
   MessageName = (messagename) GetProcAddress(lib,"MessageName");
   if(!MessageName)
   {
       printf("error load function - %s", "MessageName");
       return 1;
   } 
   CrtSetStorePropertyExt = (crtsetstorepropertyext) GetProcAddress(lib,"CrtSetStorePropertyExt");
   if(!CrtSetStorePropertyExt)
   {
       printf("error load function - %s", "CrtSetStorePropertyExt");
       return 1;
   }  
   CrptMsgClose = (crptmsgclose) GetProcAddress(lib,"CrptMsgClose");
   if(!CrptMsgClose)
   {
       printf("error load function - %s", "CrptMsgClose");
       return 1;
   }  
   BaseToBin = (basetobin) GetProcAddress(lib,"BaseToBin");   
   if(!BaseToBin)
   {
       printf("error load function - %s", "BaseToBin");
       return 1;
   }  
   GetMessageCertificate = (getmessagecertificate) GetProcAddress(lib,"GetMessageCertificate");   
   if(!GetMessageCertificate)
   {
       printf("error load function - %s", "GetMessageCertificate");
       return 1;
   }  			
   DecryptMessage = (decryptmessage) GetProcAddress(lib,"DecryptMessage");   
   if(!DecryptMessage)
   {
       printf("error load function - %s", "DecryptMessage");
       return 1;
   }
   MessageType = (messagetype) GetProcAddress(lib,"MessageType");   
   if(!MessageType)
   {
       printf("error load function - %s", "MessageType");
       return 1;
   }  		
   FreeCertificateCollection = (freecertificatecollection) GetProcAddress(lib,"FreeCertificateCollection");   
   if(!FreeCertificateCollection)
   {
       printf("error load function - %s", "FreeCertificateCollection");
       return 1;
   }  	
   SignMsgClose = (signmsgclose) GetProcAddress(lib,"SignMsgClose");
   if(!SignMsgClose)
   {
       printf("error load function - %s", "SignMsgClose");
       return 1;
   }
   SignMsgOpen = (signmsgopen) GetProcAddress(lib,"SignMsgOpen");
   if(!SignMsgOpen)
   {
       printf("error load function - %s", "SignMsgOpen");
       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 file_write ( char* file, unsigned char *data, long size );
//-----------------------------------------------------------------------
 int main(int argc, char **argv)
 {
   HINSTANCE lib					=  NULL;
   char host[16];
   char port[6];
   char profile[256];
   char Time[16];
   char fileCMS[260];
   char *SenderName					=  NULL;
   char *timeToCrypted				=  NULL;
   char *dataDescriptor				 =  NULL;
   char **senderName				=  NULL;
   char **recipName					=  NULL;
   int  senderCount					=     0;
   int  recipCount					=     0;
   long cmcSize						=     0;
   int  retCode						=     0;
   int type							=     0;
   int j							=     0;
   unsigned char *dataToDecrypted	=  NULL;
   unsigned long dataSize			=     0;
   unsigned char *cmcBlob			=  NULL;
   unsigned char *cmsBlob			=  NULL;
   unsigned long sizeBlob			=     0;
   unsigned char *crtCrypt			=  NULL;
   unsigned long sizeCrt			=     0;
   hCERTSTORE      hStore			=     0;
   HCRYPTPROV      hProv			=     0;
   hCERTCONTEXT    hContext			=     0;
   hCRYPTMSG       hCrypt			=     0;
   hCERTCOLLECTION hCollection		=     0;

 if(argc<2)
 {
    printf("Specify parameters (ex.[]$ ./decrypt /path/CMS-filename profile host port)\n");
    return 1;
 }
 if(argc == 5)
 {
   if(argv[2])
   {
     #ifdef _WIN32
       sprintf(profile,"profile://%s\0",argv[2]);
     #else
       sprintf(profile,"%s\0",argv[2]);
     #endif
   }
   else 
     return 1;
   if(argv[3])
   {
     strcpy(host,argv[3]);
   }
   else
     return 1;
   if (argv[4])
   {
     strcpy(port,argv[4]);
   }
   else
     return 1;
 }
 else
 {
   printf("Specify parameters (ex.[]$ ./decrypt /path/CMS-filename profile host port)\n");
   return 1;
 }
 try
 {
   lib = load_lib(strlib);
   if( get_func(lib) )
   {
     printf("error load func.\n");
     exit(1);
   }  
   // Открыть зашифрованный файл для чтения 
   strcpy(fileCMS,  argv[1]);
   printf("Open sign file : %s\n", fileCMS);
   retCode = file_read(fileCMS,&cmsBlob, (long *) &sizeBlob);
   if(retCode!=0)
   {
     printf("Open file %s Error\n",fileCMS);
     throw retCode;
   }
   // Если сообщение в формате BASE64 , то сконвертировать в ASN1
   if( cmsBlob[0]=='M' ) 
   {
    if(cmcBlob)
    {
      delete [] cmcBlob;
    }
    cmcBlob = new unsigned char [MAX_BASE_DENCODED_SIZE(sizeBlob)];
    BaseToBin(cmsBlob, sizeBlob, cmcBlob, &cmcSize); 
	memcpy(cmsBlob, cmcBlob, cmcSize);
	sizeBlob=cmcSize;
   }
   // Открыть контекст криптопровайдера с загрузкой ключей
   printf("Open CSP Context\n");
   retCode = CSPOpenContext(&hProv, (char *)profile, 25, 0, 0, NULL, 0, NULL, NULL, NULL);
   if(retCode!=0)
   {
     printf("%s  [CSPOpenContext - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   // Открыть хранилище сертификатов
   printf("Open Store L\n");
   retCode = CrtOpenStore(0, hProv, MORE_ONE_CA, false, NULL, &hStore);
   if(retCode!=0)
   {
     printf("%s  [CrtOpenStore - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   // Установить версию протокола LDAP
   SetLDAPver(hStore, 3);
   // Установить параметры хранилища
   printf("Set Store properties\n");
   retCode = CrtSetStorePropertyExt(hStore, MORE_ONE_CA, "t=L;", host, port, 30, NULL, NULL);
   if(retCode!=0)
   {
     printf("%s  [CrtSetStorePropertyExt - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   printf("Get own certificate\n");
   // Получить размер для данных собственного сертификата
   // перед вызовом функции обязательно вызывать CrtSetStorePropertyExt, т.к. GetMYCertificate обращается к хранилищю LDAP, но
   // если собственный сертификат будет загружен из файла, то можно сразу вызывать MessageName
   retCode = GetMYCertificate(hStore, "c=KZ", AT_KEYEXCHANGE, false, NULL, &sizeCrt);
   if(retCode!=0)
   {
     printf("%s  [GetMYCertificate - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   crtCrypt = new unsigned char [sizeCrt];
   // Получить собственный сертификат для ключевого обмена
   retCode = GetMYCertificate(hStore, "c=KZ", AT_KEYEXCHANGE, false, crtCrypt, &sizeCrt);
   if(retCode!=0)
   {
     printf("%s  [GetMYCertificate - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   // Получить контекст сертификата
   printf("Get Certificate Context\n");
   retCode = GetCertificateContext(hStore, crtCrypt ,sizeCrt,&hContext);
   if( retCode )
   {
     printf("%s [GetCertificateContext: error = %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   // Получить информацию о количестве отправителей/получателей из CMS сообщения 
   printf("Get sender/recipient info\n");
   retCode = MessageName(cmsBlob, sizeBlob,&senderCount,NULL,&recipCount,NULL);
   if( retCode )
   {
     printf("%s [MessageName: error = %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   } 
   if(senderCount)
   {
     senderName = new char *[senderCount];
     for(j=0;j<senderCount;j++)
     {
       senderName[j] = new char [260];
     }
   }
   if(recipCount)
   {
     recipName = new char *[recipCount];
     for(j=0;j<recipCount;j++)
     {
       recipName[j] = new char [260];
     }
   }
   // Получить информацию об отправителях/получателях из CMS
   retCode = MessageName(cmsBlob, sizeBlob,&senderCount,senderName,&recipCount,recipName);
   if( retCode )
   {
     printf("%s [MessageName: error = %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   for (j=0; j<senderCount; j++)
   {
     printf("Sender Name - %s\n", senderName[j]);
   }
   for (j=0; j<recipCount; j++)
   {
     printf("Recip Name - %s\n", recipName[j]);
   }
   timeToCrypted= new char [16];
   dataDescriptor= new char [260];
   // Получить хендл на коллекцию сертификатов
   printf("Get cerificate collection\n");
   retCode = GetMessageCertificate(hStore, cmsBlob,sizeBlob,&hCollection);
   if(retCode!=0)
   {
     printf("%s  [GetMessageCertificate - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   printf("Open Crypt context\n");
   retCode = SignMsgOpen(hProv, 0, &hCrypt);
   if(retCode!=0)
   {
     printf("%s  [GetMessageCertificate - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   // Получить размер зашифрованных данных
   SenderName = new char [260];
   retCode=DecryptMessage( hCrypt, hCollection, hContext, true, cmsBlob, sizeBlob, SenderName, timeToCrypted, dataDescriptor, NULL, &dataSize);
   if(retCode!=0)
   {
     printf("%s  [DecryptMessage - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   printf("DecryptMessage : DataDescriptor = %s \n", dataDescriptor);
   dataToDecrypted=new unsigned char [dataSize];
   memset(dataToDecrypted,0,dataSize);
   printf("Decrypt Message\n");



   // Расшифровывать сообщение
   retCode=DecryptMessage( hCrypt, hCollection, hContext, true, cmsBlob, sizeBlob, SenderName, timeToCrypted, dataDescriptor, dataToDecrypted, &dataSize);
   if(retCode!=0)
   {
     printf("%s  [DecryptMessage - %d]\n",ErrorString(retCode), retCode);
     throw retCode;
   }
   else
   {  
     if( dataSize )
     {
       if( dataToDecrypted[0]=='M' ) 
       {
         if(cmcBlob)
         {
           delete [] cmcBlob;
         }
         cmcBlob = new unsigned char [MAX_BASE_DENCODED_SIZE(dataSize)];
         BaseToBin(dataToDecrypted, dataSize, cmcBlob, &cmcSize); 
         if( dataToDecrypted ) delete [] dataToDecrypted;
         dataToDecrypted = new unsigned char [cmcSize];
	     memcpy(dataToDecrypted, cmcBlob, cmcSize);
	     dataSize = cmcSize;
       }         
       file_write(fileCMS,dataToDecrypted,dataSize);
     }
   }
   printf("User encrypted[%d] - %s, timeToCrypted[%d] - %s\n", j, SenderName, j, timeToCrypted);
 }
 catch(int err)
 {
   printf("catch ... Error - %d\n",err);
 }
 // Освободить все хенлдлы
 if( hCollection ) FreeCertificateCollection(&hCollection);
 if( hCrypt ) SignMsgClose(&hCrypt);
 if( hStore ) CrtCloseStore(&hStore);
 if( hProv ) CSPCloseContext(&hProv);
 //Очистить память
 if( dataDescriptor )
 {
   delete [] dataDescriptor;
 }
 if( timeToCrypted )
 {
   delete [] timeToCrypted;
 }
 if( SenderName )
 {
   delete [] SenderName;
 }
 if (senderName )
 {
   for(j=0; j<senderCount; j++) 
   { 
     delete [] senderName[j]; 
   }
   delete [] senderName;
 }
 if (recipName )
 {
   for(j=0; j<recipCount; j++) 
   { 
      delete [] recipName[j]; 
   }
   delete [] recipName;
 }
 if( dataToDecrypted ) 
 { 
   delete [] dataToDecrypted; 
 }
 if( crtCrypt ) 
 { 
   delete [] crtCrypt;
 }
 if( cmsBlob ) 
 { 
   delete [] cmsBlob; 
 }
 if( cmcBlob )
 {
   delete [] cmcBlob;
 }
 if (lib) lib_free( lib );
 return 0;
 }
//-----------------------------------------------------------------------
int file_read ( char* file, unsigned char **data, long *size ) 
{ 
 FILE *in = NULL; 
 struct stat  st;   
 if(stat(file,&st)) return 1; // Файл не найден ... 
 *size = st.st_size;
 if( (in = fopen(file, "rb"))==NULL )  
 {
   *size = 0; 
   *data = NULL; 
   return 2;  // Файл не открыт ... 
 }   
 *data = new unsigned char [st.st_size]; 
 fread(*data, *size, 1, in); 
 fclose(in); 
 return 0; 
} 
//-----------------------------------------------------------------------
int file_write ( char* file, unsigned char *data, long size ) 
{
 FILE * in = NULL;
 if( (in = fopen(file, "wb"))!=NULL )
 {
   if( fwrite(data,sizeof(unsigned char),size,in) )
   return 2; // Данные в файл не были записаны...
 }
 else
   return 1; // Файл не открыт ...
 fclose(in);
 return 0;
}
