//------------------------------------------------------------------------------
// Tumar CSP
// Copyright (c) 2011 Scientific Lab. Gamma Technologies. All rights reserved.
// SDK
// Verify CP CMP Response and install certificates
//------------------------------------------------------------------------------
#include "ex_util.h"
//------------------------------------------------------------------------------
#define CMP_CP_FILE   "cmp_cp.bin"
#define CMP_RA_CERT   "cmp_ra.cer"
#define CMP_CC_FILE   "cmp_conf.bin"
#define CMP_CRTS_FILE "cmp_crts.p7b"
#define CMP_CAS_FILE  "cmp_cas.p7b"
//------------------------------------------------------------------------------
unsigned char WBuf[8196];
unsigned char Cert[8196];
//------------------------------------------------------------------------------
// Пользователь получил ответ CMP-сервера на запрос CMP/CR
// Задача:
// 1. Проверить полученный ответ
// 2. Если получен ответ CMP-сервера в формате CMP/CP, установить в контейнер
//    выпущенный сертификат и если нужно сертификат(ы) УЦ
//------------------------------------------------------------------------------
int main(void)
{
 int code;
 HCRYPTPROV hProv=0;
 HCRYPTKEY  hKey=0,hKeyC=0,hExpKey=0;
 DWORD rv,dw,len,type,sz,szc;
 char transID[65];

 // Загружаем криптопровайдер
 code=LoadTumarCSP((char*)CSP_LIB); if (code) {printf("Load CSP error: %d\n",code); return 1;}
 //
 // Загружаем ответ сервера
 if (getFileLen(CMP_CP_FILE,     &sz)) {printf("getFileLen error\r\n"); return 1;}
 if (readFile  (CMP_CP_FILE,WBuf, sz)) {printf("readFile error\r\n");   return 1;}
 // Загружаем сертификат получателя (если имеется)
 if (getFileLen(CMP_RA_CERT,    &szc)) {printf("getFileLen error\r\n"); return 1;}
 if (readFile  (CMP_RA_CERT,Cert,szc)) {printf("readFile error\r\n");   return 1;}
 //
 // Создаем контекст CSP
 if (!CPAcquireContext(&hProv,(char*)CSP_PROF,0,NULL)) {
   printf("CPAcquireContext Error: %0X\n",GetLastErrorCSP(0));
   return 1;
 }
 // Проверим ответ сервера
 if (!CPImportKey(hProv,WBuf,sz,0,0,&hKey)) {
   printf("CPImportKey Error: %0X\n",GetLastErrorCSP(hProv));
   return 1;
 }
 // Узнаем тип ответа
 len=sizeof(type);
 if (!CPGetKeyParam(hProv,hKey,KP_CMP_TYPE,(BYTE*)&type,&len,0)) {
   printf("CPGetKeyParam KP_CMP_TYPE Error: %0X\n",GetLastErrorCSP(hProv));
   return 1;
 }
 // Сервер прислал ответ об ошибке обработки
 if (type==PKI_CMP_ERROR) {
   len=sizeof(dw);
   if (!CPGetKeyParam(hProv,hKey,KP_CMP_STATUS,(BYTE*)&dw,&len,0)) {
     printf("CPGetKeyParam KP_CMP_STATUS Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   printf("PKI_CMP_ERROR, STATUS: %d (%s)\n",dw,code2status(dw));
   dw=0xFF;
   len=sizeof(dw);
   if (!CPGetKeyParam(hProv,hKey,KP_CMP_FAIL,(BYTE*)&dw,&len,0)) {
     printf("CPGetKeyParam KP_CMP_FAIL Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   if (dw!=0xFF) printf("FAIL: %d (%s)\n",dw,code2fail(dw));
   return 1;
 }
 // Сервер прислал ответ неправильного типа
 if (type!=PKI_CMP_CP && type!=PKI_CMP_PP) {
   printf("Unknown CMP TYPE: %d\n",type);
   return 1;
 }
 // Узнаем transactionID
 len=sizeof(transID);
 if (!CPGetKeyParam(hProv,hKey,KP_CMP_TRANS_ID,(BYTE*)transID,&len,0)) {
   printf("CPGetKeyParam KP_CMP_TRANS_ID Error: %0X\n",GetLastErrorCSP(hProv));
   return 1;
 }
 transID[len]=0;
 printf("transactionID = %s\n",transID);
 // Получим сертификат подписавшего из сообщения
 len=sizeof(Cert);
 if (!CPGetKeyParam(hProv,hKey,KP_CERTIFICATE,Cert,&len,0)) {
   rv=GetLastErrorCSP(hProv);
   if (rv!=NTE_NOT_FOUND) {
     printf("CPGetKeyParam KP_CERTIFICATE Error: %0X\n",rv);
     return 1;
   }
   // Сертификат не найден, получим серийный номер сертификата подписавшего
   len=sizeof(Cert);
   if (!CPGetKeyParam(hProv,hKey,KP_KEY_SN,Cert,&len,0)) {
     printf("CPGetKeyParam KP_KEY_SN Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   writeFile("sn.bin",Cert,len);
   // Получим DN-имя подписавшего
   len=sizeof(Cert);
   if (!CPGetKeyParam(hProv,hKey,KP_CMP_SND_DN,Cert,&len,0)) {
     printf("CPGetKeyParam KP_CMP_SND_DN Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   printf("Sender: %s\r\n",(char*)Cert);
   // Получаем сертификат другим методом, зная серийный номер и DN-имя
   // В примере, просто прочитаем из файла:
   if (getFileLen(CMP_RA_CERT,    &len)) {printf("getFileLen error\r\n"); return 1;}
   if (readFile  (CMP_RA_CERT,Cert,len)) {printf("readFile error\r\n");   return 1;}
 }
 //
 // Проверяем сертификат (см. примеры проверки)
 //
 // 
 // Импортируем проверенный сертификат
 if (!CPImportKey(hProv,Cert,len,0,0,&hKeyC)) {
   printf("CPImportKey Error: %0X\n",GetLastErrorCSP(hProv));
   return 1;
 }
 // Проверяем подлинность ответа сервера
 if (!CPVerifySignature(hProv,0,WBuf,sz,hKeyC,NULL,CRYPT_OBJECT_CMP)) {
   printf("CPVerifySignature Error: %0X\n",GetLastErrorCSP(hProv));
   return 1;
 }
 if (type==PKI_CMP_PP) { // запрос отложен
   // Узнаем checkAfter...
   len=sizeof(dw);
   if (!CPGetKeyParam(hProv,hKey,KP_CMP_CHECK_AFTER,(BYTE*)&dw,&len,0)) {
     printf("CPGetKeyParam KP_CMP_CHECK_AFTER Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   printf("Check After %d sec.\n",dw);
 }
 else {
   len=sizeof(WBuf);
   if (!CPGetKeyParam(hProv,hKey,KP_CMP_CERTIFICATES,WBuf,&len,0)) {
     printf("CPGetKeyParam KP_CMP_CERTIFICATES Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   writeFile(CMP_CRTS_FILE,WBuf,len);
   len=sizeof(WBuf);
   if (!CPGetKeyParam(hProv,hKey,KP_CMP_CERTIFICATES_CA,WBuf,&len,0)) {
     printf("CPGetKeyParam KP_CMP_CERTIFICATES Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   writeFile(CMP_CAS_FILE,WBuf,len);
   CPDestroyKey(hProv,hKeyC);
   // Получим ключ на подпись
   if (!CPGetUserKey(hProv,AT_SIGNATURE,&hKeyC)) {
     printf("CPGetUserKey AT_SIGNATURE Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   // Создаем ключ экспорта запроса CMP/СС
   if (!CPGenKey(hProv,CALG_CMP_KEY,CRYPT_EXPORTABLE,&hExpKey)) {
     printf("CPGenKey CALG_CMP_KEY Error: %0X\r\n",GetLastErrorCSP(hProv));
     return 1;
   }
   // Устанавливаем тип сообщения
   dw=PKI_CMP_CERT_CONF;
   if (!CPSetKeyParam(hProv,hExpKey,KP_CMP_TYPE,(BYTE*)&dw,0)) {
     printf("CPSetKeyParam KP_CMP_TYPE Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   // Устанавливаем TRANS_ID
   if (!CPSetKeyParam(hProv,hExpKey,KP_CMP_TRANS_ID,(BYTE*)transID,0)) {
     printf("CPSetKeyParam KP_CMP_TRANS_ID Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   // Устанавливаем ключ подписи
   if (!CPSetKeyParam(hProv,hExpKey,KP_CMP_EXP_KEY,(BYTE*)&hKeyC,0)) {
     printf("CPSetKeyParam KP_CMP_EXP_KEY Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   // Устанавливаем сертификат получателя (если имеется)
   if (!CPSetKeyParam(hProv,hExpKey,KP_CMP_RCP_SUBJ,Cert,0)) {
     printf("CPSetKeyParam KP_CMP_RCP_SUBJ Error: %0X\n",GetLastErrorCSP(hProv));
     return 1;
   }
   // Формируем запрос
   len=sizeof(WBuf);
//   if (!CPExportKey(hProv,0,hExpKey,PUBLICKEYBLOB_CMP,0,WBuf,&len)) { // Отвергаем
   if (!CPExportKey(hProv,hKey,hExpKey,PUBLICKEYBLOB_CMP,0,WBuf,&len)) { // Подтверждаем
     printf("CPExportKey PUBLICKEYBLOB_CMP Error: %0X\r\n",GetLastErrorCSP(hProv));
     return 1;
   }
   CPDestroyKey(hProv,hExpKey);
   // Сохраняем запрос
   writeFile(CMP_CC_FILE,WBuf,len);
 }
 CPDestroyKey(hProv,hKey);
 CPDestroyKey(hProv,hKeyC);
 // Закрываем контекст CSP
 CPReleaseContext(hProv,0);
 printf("OK\n");
 return 0;
}
//------------------------------------------------------------------------------
