//---------------------------------------------------------------------------

#pragma hdrstop
#include <windows.h>
#include <wincrypt.h>
#define GOST_HASH_OID "1.3.6.1.4.1.6801.1.2.1"
#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define szOID_SUBJECT_X_NAME "1.3.6.1.4.1.6801.2.8"
#pragma argsused

BYTE* pbSignedMessageBlob=NULL;
DWORD  cbSignedMessageBlob=0;

int SignMessageAttach(unsigned char* request, long req_len);
bool VerifyMessageSignatureAttach(void);
int SignMessageDetach(unsigned char* request, long req_len);
bool VerifyMessageSignatureDetach(unsigned char* request, long req_len);

int main(int argc, char* argv[])
{
    BYTE pbMessage[] = "123456789";
    DWORD messLen=strlen(pbMessage);
    int code = SignMessageAttach(pbMessage,messLen);
    bool ret = VerifyMessageSignatureAttach();
    if (pbSignedMessageBlob) free(pbSignedMessageBlob);
    cbSignedMessageBlob = 0;
    code = SignMessageDetach(pbMessage,messLen);
    ret =  VerifyMessageSignatureDetach(pbMessage,messLen);
    if (pbSignedMessageBlob) free(pbSignedMessageBlob);
}

int SignMessageAttach(unsigned char* request, long req_len)
{
 DWORD ret=0;
 CRYPT_SIGN_MESSAGE_PARA  SigParams;
 BYTE* pbMessage=(BYTE*)request;
 const BYTE* MessageArray[] = {pbMessage};
 DWORD MessageSizeArray[1];
 MessageSizeArray[0] = req_len;
 HCERTSTORE hCertStore=0;
 PCCERT_CONTEXT pSignerCert=NULL;
 LPBYTE pbTime = NULL;
 DWORD  cbTime = 0;
 FILETIME   fileTime;
 SYSTEMTIME systemTime;
 DWORD code=0;
 CRYPT_ATTR_BLOB cablob[1];
 CRYPT_ATTRIBUTE ca[2];
	try
	 {
		  try
		  {	     
				// открываем хранилище сертификатов
				hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,0,NULL,CERT_SYSTEM_STORE_CURRENT_USER,L"MY");
				// получаем контекст сертификата, который будет использоваться для подписи       
				pSignerCert = CertFindCertificateInStore(hCertStore,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,L"Test",NULL);
				if (pSignerCert==NULL) throw GetLastError();
				memset(&SigParams,0,sizeof(CRYPT_SIGN_MESSAGE_PARA));
				SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
				SigParams.dwMsgEncodingType = MY_ENCODING_TYPE;
				SigParams.pSigningCert = pSignerCert;
				// добавляем в атрибуты PKCS7 время подписания
				GetSystemTime(&systemTime);
				SystemTimeToFileTime(&systemTime, &fileTime);
				code = CryptEncodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime, (LPVOID)&fileTime, NULL, &cbTime);
				if (!code)  throw GetLastError();
				pbTime = (BYTE*) calloc(cbTime,sizeof(BYTE));
				if (!pbTime) throw 300;
				code = CryptEncodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime, (LPVOID)&fileTime, pbTime, &cbTime);
				if (!code) throw GetLastError();
				cablob[0].cbData = cbTime;
				cablob[0].pbData = pbTime;
				ca[0].pszObjId = szOID_RSA_signingTime;
				ca[0].cValue = 1;
				ca[0].rgValue = cablob;
				CRYPT_ATTR_BLOB cablob2[1];
				// и имя подписавшего
				cablob2[0].cbData = pSignerCert->pCertInfo->Subject.cbData;
				cablob2[0].pbData = pSignerCert->pCertInfo->Subject.pbData;
				ca[1].pszObjId = szOID_SUBJECT_X_NAME;
				ca[1].cValue = 1;
				ca[1].rgValue = cablob2;
				SigParams.cAuthAttr = 2;
				SigParams.rgAuthAttr = ca;	        
				SigParams.HashAlgorithm.pszObjId=GOST_HASH_OID;
				SigParams.cMsgCert = 1;
				SigParams.rgpMsgCert = &pSignerCert;
				SigParams.dwInnerContentType = 0;
				SigParams.cMsgCrl = 0;
				SigParams.rgpMsgCrl=NULL;
				SigParams.dwFlags = 0;
				SigParams.pvHashAuxInfo = NULL;
				cbSignedMessageBlob=0;
				// формируем PKCS7(данные присоединены)
				if (!(CryptSignMessage(&SigParams,false,1,MessageArray,MessageSizeArray,NULL,&cbSignedMessageBlob))) throw GetLastError();
				if(!(pbSignedMessageBlob =(BYTE*)malloc(cbSignedMessageBlob))) throw 300;
				if (!(CryptSignMessage(&SigParams,false,1,MessageArray,MessageSizeArray,pbSignedMessageBlob,&cbSignedMessageBlob))) throw GetLastError();
		  }
		  catch(DWORD err)
		  {
				ret=err;
		  }
		  catch(int err)
		  {
				ret=err;
		  }
	 }
	 __finally
	 {
			if (pbTime) free(pbTime);
			if (pSignerCert) CertFreeCertificateContext(pSignerCert);
			if (hCertStore) CertCloseStore(hCertStore,0);

	 }
	 return ret;
}

bool VerifyMessageSignatureAttach(void)
{
bool ret=true;
HCRYPTPROV hCryptProv=0;
BYTE* signed_mess=NULL;
DWORD signed_mess_len=0;
CRYPT_VERIFY_MESSAGE_PARA  VerPara;
PCCERT_CONTEXT pCertContext=NULL;
try
{
	 try
	 {
			 // для проверки необходимо создать контекст на криптопровайдера
			  if (!CryptAcquireContext(&hCryptProv,NULL,"Tumar CSP",25,CRYPT_VERIFYCONTEXT)) throw GetLastError();
			  VerPara.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
			  VerPara.dwMsgAndCertEncodingType = MY_ENCODING_TYPE;
			  VerPara.hCryptProv = hCryptProv;
			  VerPara.pfnGetSignerCertificate = 0;
			  VerPara.pvGetArg = NULL;
			  // проверяем подпись PKCS7. После проверки в переменной  signed_mess будет исходное подписанное сообщение,
			  // а в  pCertContext - контекст на сертификат подписавшего
			  if (!CryptVerifyMessageSignature(&VerPara,0,pbSignedMessageBlob,cbSignedMessageBlob,signed_mess,&signed_mess_len,&pCertContext)) throw GetLastError();
			  signed_mess = (BYTE*)malloc(signed_mess_len);
			  if (!CryptVerifyMessageSignature(&VerPara,0,pbSignedMessageBlob,cbSignedMessageBlob,signed_mess,&signed_mess_len,&pCertContext)) throw GetLastError();
	 }
	 catch(DWORD err)
	 {
			ret=false;
	 }
	 catch(int err)
	 {
			ret=false;
	 }
}
__finally
{
     if (pCertContext) CertFreeCertificateContext(pCertContext);
     if (signed_mess) free(signed_mess);
     if (hCryptProv) CryptReleaseContext(hCryptProv,0);
}
return ret;
}

int SignMessageDetach(unsigned char* request, long req_len)
{
 DWORD ret=0;
 CRYPT_SIGN_MESSAGE_PARA  SigParams;
 BYTE* pbMessage=(BYTE*)request;
 const BYTE* MessageArray[] = {pbMessage};
 DWORD MessageSizeArray[1];
 MessageSizeArray[0] = req_len;
 HCERTSTORE hCertStore=0;
 PCCERT_CONTEXT pSignerCert=NULL;
 LPBYTE pbTime = NULL;
 DWORD  cbTime = 0;
 FILETIME   fileTime;
 SYSTEMTIME systemTime;
 DWORD code=0;
 CRYPT_ATTR_BLOB cablob[1];
 CRYPT_ATTRIBUTE ca[2];

	try
	 {
		  try
		  {     
				// открываем хранилище сертификатов
				hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,0,NULL,CERT_SYSTEM_STORE_CURRENT_USER,L"MY");
				// получаем контекст сертификата, который будет использоваться для подписи       
				pSignerCert = CertFindCertificateInStore(hCertStore,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,L"Ефимов Стас",NULL);
				if (pSignerCert==NULL) throw GetLastError();
				memset(&SigParams,0,sizeof(CRYPT_SIGN_MESSAGE_PARA));
				SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
				SigParams.dwMsgEncodingType = MY_ENCODING_TYPE;
				SigParams.pSigningCert = pSignerCert;
				// добавляем в атрибуты PKCS7 время подписания
				GetSystemTime(&systemTime);
				SystemTimeToFileTime(&systemTime, &fileTime);
				code = CryptEncodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime, (LPVOID)&fileTime, NULL, &cbTime);
				if (!code)  throw GetLastError();
				pbTime = (BYTE*) calloc(cbTime,sizeof(BYTE));
				if (!pbTime) throw 300;
				code = CryptEncodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime, (LPVOID)&fileTime, pbTime, &cbTime);
				if (!code) throw GetLastError();
				cablob[0].cbData = cbTime;
				cablob[0].pbData = pbTime;
				ca[0].pszObjId = szOID_RSA_signingTime;
				ca[0].cValue = 1;
				ca[0].rgValue = cablob;
				CRYPT_ATTR_BLOB cablob2[1];
				// и имя подписавшего
				cablob2[0].cbData = pSignerCert->pCertInfo->Subject.cbData;
				cablob2[0].pbData = pSignerCert->pCertInfo->Subject.pbData;
				ca[1].pszObjId = szOID_SUBJECT_X_NAME;
				ca[1].cValue = 1;
				ca[1].rgValue = cablob2;
				SigParams.cAuthAttr = 2;
				SigParams.rgAuthAttr = ca;	        
				SigParams.HashAlgorithm.pszObjId=GOST_HASH_OID;
				SigParams.cMsgCert = 1;
				SigParams.rgpMsgCert = &pSignerCert;
				SigParams.dwInnerContentType = 0;
				SigParams.cMsgCrl = 0;
				SigParams.rgpMsgCrl=NULL;
				SigParams.dwFlags = 0;
				SigParams.pvHashAuxInfo = NULL;
				cbSignedMessageBlob=0;
				// формируем PKCS7 (данные не присоединены)
				if (!(CryptSignMessage(&SigParams,true,1,MessageArray,MessageSizeArray,NULL,&cbSignedMessageBlob))) throw GetLastError();
				if(!(pbSignedMessageBlob =(BYTE*)malloc(cbSignedMessageBlob))) throw 300;
				if (!(CryptSignMessage(&SigParams,true,1,MessageArray,MessageSizeArray,pbSignedMessageBlob,&cbSignedMessageBlob))) throw GetLastError();
		  }
		  catch(DWORD err)
		  {
				ret=err;
		  }
		  catch(int err)
		  {
				ret=err;
		  }
	 }
	 __finally
	 {
			if (pbTime) free(pbTime);
			if (pSignerCert) CertFreeCertificateContext(pSignerCert);
			if (hCertStore) CertCloseStore(hCertStore,0);

	 }
	 return ret;
}

bool VerifyMessageSignatureDetach(unsigned char* request, long req_len)
{
BYTE* pbMessage=(BYTE*)request;
const BYTE* MessageArray[] = {pbMessage};
DWORD MessageSizeArray[1];
MessageSizeArray[0] = req_len;
PCCERT_CONTEXT pCertContext = NULL;
HCRYPTPROV hCryptProv=0;
CRYPT_VERIFY_MESSAGE_PARA msgPara;
bool ret=true;
try
{
	 try
	 {
			// для проверки необходимо создать контекст на криптопровайдера
			if (!CryptAcquireContext(&hCryptProv,NULL,"Tumar CSP",25,CRYPT_VERIFYCONTEXT)) throw GetLastError();
			msgPara.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
			msgPara.dwMsgAndCertEncodingType =MY_ENCODING_TYPE;
			msgPara.hCryptProv = hCryptProv;
			msgPara.pfnGetSignerCertificate = NULL;
			msgPara.pvGetArg = NULL;
			// проверяем подпись PKCS7. После проверки в переменной pCertContext - контекст на сертификат подписавшего
			if (!CryptVerifyDetachedMessageSignature(&msgPara,0,pbSignedMessageBlob,cbSignedMessageBlob,1,MessageArray,MessageSizeArray,&pCertContext)) throw GetLastError();
	 }
	 catch(DWORD err)
	 {
			ret=false;
	 }
	 catch(int err)
	 {
			ret=false;
	 }
}
__finally
{
        if (pCertContext) CertFreeCertificateContext(pCertContext);
        if (hCryptProv) CryptReleaseContext(hCryptProv,0);
}
return ret;
}
