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

#pragma hdrstop
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <winldap.h>
//---------------------------------------------------------------------------
#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define LINT ULONG

#define ATR_KEY_OID   "keyOID"
#define ATR_USER_CERT "certificateRevocationList"
#define szOID_VERIFYER "1.3.6.1.4.1.6801.11.1.4"

int CheckCertSign(wchar_t* DName);
int CheckCertDates(wchar_t* DName);
int CheckChain(wchar_t* DName);
int CheckCertWithCRL(wchar_t* DName);
int InstallCRLToStore();
void GetCRLfromLDAP(void);
unsigned char* crlblob;
unsigned long crl_size;
int main(int argc, char* argv[])
{
		wchar_t* DName = L"Ефимов Стас";
		int ret=0;



		ret = CheckCertDates(DName);    // проверить сроки действия сертификата
		ret = CheckCertSign(DName);     // проверить подпись сертификата, используя сертификат издателя
		ret = CheckChain(DName);         // проверить цепочку сертификата
		ret =  CheckCertWithCRL(DName);  // проверить сертификат, используя СОС
                GetCRLfromLDAP();        // получить СОС по LADP
                ret = InstallCRLToStore();  // установить СОС в хранилище
return 0;
}

int CheckCertSign(wchar_t* DName)
{
PCCERT_CONTEXT pCertContext = NULL;
PCCERT_CONTEXT pRootContext = NULL;
HCERTSTORE hStoreMy = 0;
HCERTSTORE hStoreRoot = 0;
DWORD flags=0;
int ret=0;
try
{
	try
	{
			hStoreMy = CertOpenSystemStore(0,"MY"); // открыть хранилище с пользовательским сертификатом
			hStoreRoot = CertOpenSystemStore(0,"ROOT"); // открыть хранилище с сертификатом издателя
			pCertContext = CertFindCertificateInStore(hStoreMy,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,DName,NULL); // найти пользовательский сертификат
			if (pCertContext == NULL) throw GetLastError();
			pRootContext =CertGetIssuerCertificateFromStore(hStoreRoot, pCertContext, NULL,&flags); // найти сертификат издателя
			if (pRootContext==NULL) throw GetLastError();
			if (!CertVerifySubjectCertificateContext(pCertContext,pRootContext,&flags)) throw GetLastError(); // проверить подпись пользовательского сертификата
	}
	catch(DWORD err)
	{
			ret=err;
	}
	catch(int err)
	{
			ret=err;
	}
	catch(...)
	{
			ret=300;
	}
}
__finally
{
 if (pCertContext) CertFreeCertificateContext(pCertContext);
 if (pRootContext) CertFreeCertificateContext(pRootContext);
 if (hStoreMy) CertCloseStore(hStoreMy,0);
 if (hStoreRoot) CertCloseStore(hStoreRoot,0);
}
return ret;
}

int CheckCertDates(wchar_t* DName)
{
int ret=0;
PCCERT_CONTEXT pCertContext = NULL;
HCERTSTORE hStoreMy = 0;
FILETIME notBefore;
FILETIME notAfter;
FILETIME now;
SYSTEMTIME systemTime;
try
{
	try
	{
		hStoreMy = CertOpenSystemStore(0,"MY");   // открыть хранилище с пользовательским сертификатом
		pCertContext = CertFindCertificateInStore(hStoreMy,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,DName,NULL);  // найти пользовательский сертификат
		if (pCertContext == NULL) throw GetLastError();
		notBefore = pCertContext->pCertInfo->NotBefore;
		notAfter = pCertContext->pCertInfo->NotAfter;
		GetSystemTime(&systemTime);
		SystemTimeToFileTime(&systemTime, &now);
		if ((CompareFileTime(&notBefore,&now)==-1)&&(  CompareFileTime(&notAfter,&now)==1)) ret=0;   // проверить сроки действия сертификата
		else ret=1;
	}
	catch(DWORD err)
	{
		 ret=err;
	}
	catch(int err)
	{
		ret=err;
	}
	catch(...)
	{
		ret=300;
	}
}__finally
{
  if (pCertContext) CertFreeCertificateContext(pCertContext);
  if (hStoreMy) CertCloseStore(hStoreMy,0);
}
return ret;
}

int CheckChain(wchar_t* DName)
{
int ret=0;
PCCERT_CONTEXT pCertContext = NULL;
PCCERT_CONTEXT pRootContext = NULL;
HCERTSTORE hStoreMy = 0;
HCERTSTORE hStoreRoot = 0;
CERT_ENHKEY_USAGE        EnhkeyUsage;
CERT_USAGE_MATCH         CertUsage;  
CERT_CHAIN_PARA          ChainPara;
CERT_CHAIN_ENGINE_CONFIG ChainConfig;
HCERTCHAINENGINE         hChainEngine=NULL;
PCCERT_CHAIN_CONTEXT     pChainContext=NULL;
CERT_CHAIN_POLICY_PARA Policy_para;       // используемые типы политик для проверки сертификатов цепочки
CERT_CHAIN_POLICY_PARA* pPolicy_para=NULL;
CERT_CHAIN_POLICY_STATUS PolicyStatus;
CERT_CHAIN_POLICY_STATUS* pPolicyStatus=NULL; // структура для возврата статуса цепочки
try
{
	try
	{
			hStoreMy = CertOpenSystemStore(0,"MY");    // открыть хранилище с пользовательским сертификатом
			hStoreRoot = CertOpenSystemStore(0,"CA");    // открыть дополнительное хранилище
			pCertContext = CertFindCertificateInStore(hStoreMy,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,DName,NULL); // найти пользовательский сертификат
			// задать параметры для построения цепочки
			EnhkeyUsage.cUsageIdentifier = 0;
			EnhkeyUsage.rgpszUsageIdentifier=NULL;    
			CertUsage.dwType = USAGE_MATCH_TYPE_AND;
			CertUsage.Usage  = EnhkeyUsage;
			ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
			ChainPara.RequestedUsage=CertUsage;
			ChainConfig.cbSize = sizeof(CERT_CHAIN_ENGINE_CONFIG);
			ChainConfig.hRestrictedRoot= NULL ;
			ChainConfig.hRestrictedTrust= NULL ;
			ChainConfig.hRestrictedOther= NULL ;
			ChainConfig.cAdditionalStore=1 ;
			ChainConfig.rghAdditionalStore = &hStoreRoot;
			ChainConfig.dwFlags=0;
			ChainConfig.dwUrlRetrievalTimeout= 0 ;
			ChainConfig.MaximumCachedCertificates=0 ;
			ChainConfig.CycleDetectionModulus = 0;
			ChainConfig.hRestrictedRoot= NULL;
			ChainConfig.hRestrictedTrust= NULL ;
			ChainConfig.hRestrictedOther= NULL;
			if(!CertCreateCertificateChainEngine(&ChainConfig,&hChainEngine)) throw GetLastError();  // создать объект цепочки
			// получить цепочку, соответствующую заданным условиям
			if(!CertGetCertificateChain(NULL,pCertContext,NULL,hStoreRoot,&ChainPara,CERT_CHAIN_CACHE_END_CERT & CERT_CHAIN_REVOCATION_CHECK_CHAIN,NULL,&pChainContext)) throw GetLastError();
			printf("The size of the chain context is %d. \n",pChainContext->cbSize);
			printf("%d simple chains found.\n",pChainContext->cChain);
			printf("\nError status for the chain:\n");
			Policy_para.cbSize=sizeof(CERT_CHAIN_POLICY_PARA);
			Policy_para.dwFlags=0; // флаги, определяющие игнорирумые причины non-valid
			Policy_para.pvExtraPolicyPara=NULL;
			PolicyStatus.cbSize=sizeof(CERT_CHAIN_POLICY_STATUS);
			pPolicyStatus=&PolicyStatus;
			pPolicy_para=&Policy_para;
			// проверка цепочки
			if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE,pChainContext, pPolicy_para,pPolicyStatus))
			if (pPolicyStatus->dwError==0) // цепочка проверена, сертификаты соотвествуют указанной политике							  
			{
				printf("Verify success! dwError=0\n");
			}
			else
			{
					 // цепочка проверена, сертификаты не соответсвуют указанной политике			
				printf("%d\n",(pPolicyStatus->lElementIndex)); // номер сертификата в цепочке
					 switch(pPolicyStatus->dwError) // тип несоответствия
					{
						case TRUST_E_CERT_SIGNATURE:
							 printf("No error found for this certificate or chain.\n");
							 break;
						case CERT_E_UNTRUSTEDROOT:
							 printf("This certificate or one of the certificates in the certificate chain is not time-valid.\n");
							 break;
						case CERT_E_UNTRUSTEDTESTROOT:
							 printf("Certificates in the chain are not properly time-nested.\n");
							 break;
						case CERT_E_CHAINING:
							 printf("Trust for this certificate or one of the certificates in the certificate chain has been revoked.\n");
							 break;
						case CERT_E_WRONG_USAGE:
							 printf("The certificate or one of the certificates in the certificate chain does not have a valid signature.\n");
							 break;
						case CERT_E_EXPIRED:
							 printf("The certificate or certificate chain is not valid in its proposed usage.\n");
							 break;
						case CERT_E_VALIDITYPERIODNESTING:
							 printf("The certificate or certificate chain is based on an untrusted root.\n");
							 break;
						case CERT_E_PURPOSE:
							 printf("The revocation status of the certificate or one of the certificates in the certificate chain is unknown.\n");
							 break;
						case TRUST_E_BASIC_CONSTRAINTS:
							 printf("One of the certificates in the chain was issued by a certification authority that the original certificate had certified.\n");
							 break;
						case CERT_E_ROLE:
							 printf("The certificate chain is not complete.\n");
							 break;
						case CERT_E_CN_NO_MATCH:
							 printf("A CTL used to create this chain was not time-valid.\n");
							 break;
						case CRYPT_E_REVOKED:
							 printf("A CTL used to create this chain did not have a valid signature.\n");
							 break;
						case CRYPT_E_REVOCATION_OFFLINE:
							 printf("A CTL used to create this chain is not valid for this usage.\n");
							 break;
						case CERT_E_REVOKED:
							 printf("A certificate in the chain has been explicitly revoked by its issuer.");
							 break;
						case CERT_E_REVOCATION_FAILURE:
							 printf("The revocation process could not continue. The certificates could not be checked.");

					} 
			}
			else  // цепочка не проверена
			{
				printf("Failed!");
			}

			printf("\nInfo status for the chain:\n"); // текущий статус для цепочки
			switch(pChainContext->TrustStatus.dwInfoStatus)
			{
			case 0:
				 printf("No information status reported.\n");
				 break;
			case CERT_TRUST_HAS_EXACT_MATCH_ISSUER :
				 printf("An exact match issuer certificate has been found for this certificate.\n");
				 break;
			case CERT_TRUST_HAS_KEY_MATCH_ISSUER: 
				printf("A key match issuer certificate has been found for this certificate.\n");
				 break;
			case CERT_TRUST_HAS_NAME_MATCH_ISSUER: 
				printf("A name match issuer certificate has been found for this certificate.\n");
				 break;
			case CERT_TRUST_IS_SELF_SIGNED:
				 printf("This certificate is self-signed.\n");
				 break;
			case CERT_TRUST_IS_COMPLEX_CHAIN:
				 printf("The certificate chain created is a complex chain.\n");
				 break;

				 // ....остальные значение описаны в wincrypt.h
			}
	}
	catch(DWORD err)
	{
		ret=err;
	}
	catch(int err)
	{
		ret=err;
	}
	catch(...)
	{
		ret=300;
	}
}
__finally
{
  if (pCertContext) CertFreeCertificateContext(pCertContext);
  if (hStoreMy) CertCloseStore(hStoreMy,0);
  if (hChainEngine) CertFreeCertificateChainEngine(hChainEngine);
  if (pChainContext)  CertFreeCertificateChain(pChainContext);
}
return ret;
}

int InstallCRLToStore()
{
PCCRL_CONTEXT pCrlContext=NULL;
HCERTSTORE hCertStore=0;
DWORD ret=0;
try
{
	try
	{
			hCertStore = CertOpenSystemStore(0,"CA");
			if  (!CertAddEncodedCRLToStore(hCertStore,MY_ENCODING_TYPE,crlblob,crl_size,CERT_STORE_ADD_REPLACE_EXISTING,&pCrlContext)) throw GetLastError();
	}
	catch(DWORD err)
	{
			ret=err;
	}
	catch(int err)
	{
			ret=err;
	}
	catch(...)
	{
			ret=300;
	}
}
__finally
{
   if (pCrlContext) CertFreeCRLContext(pCrlContext);
   if (hCertStore) CertCloseStore(hCertStore,0);
}
return ret;
}

int CheckCertWithCRL(wchar_t* DName)
{
int ret=0;
PCCERT_CONTEXT pCertContext = NULL;
HCERTSTORE hStoreMy = 0;
HCERTSTORE hStoreRoot = 0;
PCRL_ENTRY ppCrlEntry=NULL;
PCCRL_CONTEXT pCrlContext=NULL;
PCCRL_CONTEXT pDupCrlContext=NULL;
try
{
	try
	{
		 hStoreMy = CertOpenSystemStore(0,"MY");   // открыть хранилище с пользовательским сертификатом
		 hStoreRoot = CertOpenSystemStore(0,"CA");   // открыть хранилище с СОС(обычно СА)
		 pCertContext = CertFindCertificateInStore(hStoreMy,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,DName,NULL);   // найти пользовательский сертификат
		 while (pCrlContext=CertEnumCRLsInStore(hStoreRoot,pCrlContext))   // перечисляем все СОС в хранилище
		 {
			if (CertCompareCertificateName(MY_ENCODING_TYPE, &pCertContext->pCertInfo->Issuer,&pCrlContext->pCrlInfo->Issuer)) // сравниваем имя издателя сертификата и СОС
			{
					ppCrlEntry=NULL; // и если имена совпали - ищем сертификат с СОС
					if (!CertFindCertificateInCRL(pCertContext, pCrlContext,0,0, &ppCrlEntry)) throw GetLastError();
					if (ppCrlEntry==NULL)
					{
						printf("\nCertificate is valid\n\n");          // сертификат не найден
					}
					else
					{
					printf("\nCertificate is non-valid\n");        // сертификат найден
					}
					pDupCrlContext=CertDuplicateCRLContext(pCrlContext);    // необходимо для получения указателя на следующий СОС
					if (pDupCrlContext==NULL) throw GetLastError();
			}
		 }

	}
	catch(DWORD err)
	{
			 ret= err;
	}
	catch(int err)
	{
			ret= err;
	}
	catch(...)
	{
			ret=300;
	}
}
__finally
{
   if (pCertContext) CertFreeCertificateContext(pCertContext);
   if (pDupCrlContext) CertFreeCRLContext(pDupCrlContext);
   if (pCrlContext) CertFreeCRLContext(pCrlContext);
   if (hStoreRoot) CertCloseStore(hStoreRoot,0);
   if (hStoreMy) CertCloseStore(hStoreMy,0);
}
return ret;
}

void GetCRLfromLDAP(void)
{
 char *host=(char*)"localhost";
 LINT  port=62222;
 char *auth=(char*)""; // Анонимный доступ. Для simple аутентификации "user:password"
 char *base=(char*)"t=R"; // Доступ к хранилищу СОС. Для доступа к другому хранилищу: "t=X", где X=I,A,...
 char *dn  =(char*)"cn=CA;o=GAMMA;c=KZ"; // DN-имя УЦ
 LDAP *hLdap;
 LINT  opt = LDAP_VERSION3;
 LINT  code;
 LDAPMessage *result;
 char *keyOID=(char*) "1.3.6.1.4.1.6801.1.2.2"; //OID подписи СОС
 //unsigned char *cert=NULL;
 //int cert_len=0;
 struct berval **bvalues;
 char *attribute;
 struct berval bv,*bv_res=NULL;
 BerElement *be;
 char **values;
 int i,num=-1;
    
 // Инициализация соединения:
 hLdap = ldap_init(host,port);
 if (!hLdap) {
   printf("ldap_init error.\r\n");
   exit(1);
 }
 // Устанавливаем версию протокола LDAP - v3:
 ldap_set_option(hLdap,LDAP_OPT_PROTOCOL_VERSION,(void*)&opt);
 // Устанавливаем LDAP-соединение:
 code = ldap_bind_s(hLdap,base,auth,LDAP_AUTH_SIMPLE);
 if (code) {
   ldap_perror(hLdap,"ldap_bind_s");
   printf("ldap_bind_s error. code=%d.\r\n",code);
   exit(2);
 }
 // Выполняем чтение узла с именем dn:
 code = ldap_search_s(hLdap,dn,LDAP_SCOPE_BASE,NULL,NULL,0,&result);
 if (code!=LDAP_SUCCESS) {
   ldap_perror(hLdap,"ldap_search_s");
   printf("ldap_search_s error. code=%d.\r\n",code);
   ldap_unbind_s(hLdap);
   exit(3);
 }
 if (!result) {
   printf("result is null\r\n");
   ldap_unbind_s(hLdap);
   exit(4);
 }
  // Поиск в узле атрибута 'keyOID' и значения атрибута (ключа на подпись)
 for(attribute=ldap_first_attribute(hLdap,result,&be);attribute;attribute=ldap_next_attribute(hLdap,result,be)) {
   if (!strcmp(attribute,ATR_KEY_OID)) {
     if ((values=ldap_get_values(hLdap,result,attribute))!=NULL) {
       for(i=0;values[i];i++) 
         if (!strcmp(values[i],keyOID)){num=i; break;}
       ldap_value_free(values);
     }
     ldap_memfree(attribute);
     break;
   }
 }
 if (!attribute) {
   printf("attribute %s not found\r\n",ATR_KEY_OID);
   ldap_msgfree(result);
   ldap_unbind_s(hLdap);
   exit(5);
 }

 // Получение тела CRL
 for(attribute=ldap_first_attribute(hLdap,result,&be);attribute;attribute=ldap_next_attribute(hLdap,result,be)) {
   if (!strcmp(attribute,ATR_USER_CERT)) {
     if ((bvalues=ldap_get_values_len(hLdap,result,attribute))!=NULL) {
       if (ldap_count_values_len(bvalues)<num) break;
       crl_size=(int)bvalues[num]->bv_len;
       if (!crl_size) break;
       crlblob=(unsigned char*) calloc(crl_size+1,sizeof(unsigned char));
       memcpy(crlblob,bvalues[num]->bv_val,crl_size);
       ldap_value_free_len(bvalues);
     }
     ldap_memfree(attribute);
     break;
   }
 }

 ldap_msgfree(result);
 if (!attribute) {
   printf("attribute %s not found\r\n",ATR_USER_CERT);
   exit(6);
 }
 if (!crlblob) {
   printf("certificate not found\r\n");
   exit(7);
 }
 // СОС найден
 bv.bv_val=(char*)crlblob;          // Тело СОС
  bv.bv_len=crl_size; // Размер

 // Завершаем LDAP-соединение:
 ldap_unbind_s(hLdap);
 printf("OK.\r\n");
}



 