#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.1.0.0.sl"
  #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 DN datafile host port)\n");
   return 1;
 }
 if(argv[1])
   #ifdef _WIN32
     sprintf(profile,"profile://%s\0",argv[1]);
   #else
     sprintf(profile,"%s\0",argv[1]);
   #endif
 else
   return 1;
 if(argv[2])
 {
   strcpy(DN,argv[2]);
 }
 else
   return 2;
   
 if(argv[3])
 {
   // filename - Name of file contain Open Data to be signed.
   strcpy(filename,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, 0, 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, 0, 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;
}
//-----------------------------------------------------------------------
