#include <wincrypt.h>

#pragma once

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif
//#define _WINVER 0x0400


#ifdef  APL_CRYPT_CLASS_EXPORT_DLL
#define  APL_CRYPT_CLASS_EXPORT	__declspec(dllexport)
#else
#define  APL_CRYPT_CLASS_EXPORT	__declspec(dllimport)

#ifdef _DEBUG
#ifdef UNICODE
#pragma comment (lib,"aplCryptManager_ud.lib")
#else
#pragma comment (lib,"aplCryptManagerD.lib")
#endif
#else
#ifdef UNICODE
#pragma comment (lib,"aplCryptManager_u.lib")
#else
#pragma comment (lib,"aplCryptManager.lib")
#endif
#endif
#endif


#define APL_CRYPT_MGR _T("AplCryptManager")
#define APL_CRYPTO_PRO_34_10_2001 _T("APL КриптоПро ГОСТ Р 34.10-2001")
#define APL_CRYPTO_PRO_34_10_2012 _T("APL КриптоПро ГОСТ Р 34.10-2012")
#define APL_OPENSSL_34_10_2001 _T("APL OpenSSL ГОСТ Р 34.10-2001")
#define APL_CSP_REG_ROOT _T("Software\\CALS Centre \"Applied Logistic\"\\Apl Cryptographic Provider\\CSP\\")

#define APL_CSP_2001 0 // параметры подписи для APL_CRYPTO_PRO_34_10_2001
#define APL_CSP_2012 1 // параметры подписи для APL_CRYPTO_PRO_34_10_2012


#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define APL_CRYPT_NO_ERROR			0
#define APL_CRYPT_UNKNOWN_ERROR		1
#define APL_CRYPT_INVALID_HASH		2
#define APL_CRYPT_INVALID_SIGNATURE	3
#define APL_CRYPT_BAD_FILE			4
#define APL_CRYPT_BAD_PASSWORD		5
#define APL_CRYPT_NO_SIGN			6
#define APL_ERROR_DO_NOT_SHOW		7
#define APL_CRYPT_INVALID_CERT		8
#define APL_CRYPT_NO_CRYPTOPRO		9 // Не удалось инициализировать криптопро
#define APL_CRYPT_NO_RSA		   10 // Не удалось инициализировать RSA
#define APL_CRYPT_NO_OSSL		   11 // Не удалось инициализировать OpenSSL
#define APL_CRYPT_ERROR_IMPORT_OPEN_KEY 12
#define APL_CRYPT_ERROR_RET_CREATE_KEY 13 // Было выбрано "создать сертификт"


#define APL_CRYPT_INVALID_PARAM		21  // Неверные параметры при вызове функции
#define APL_CRYPT_NO_MEM_4_RESULT	22

// режим диалога при подписи
enum ESignMode {APL_SIMPLE_MODE=0, APL_QUICK_MODE_WITH_DLG=1, APL_QUICK_MODE_WITHOUT_DLG=2, APL_MODE_WITHOUT_DLG_4_TGB=3};

class APL_CRYPT_CLASS_EXPORT CaplCryptographicManager
{
public:

	struct TCertInfo
	{
		// это чтобы знать, можно ли подписать тем ключом, 
		// котоырй выбрал пользователь
		bool bCheckRootKey;
		CStringArray saConfidentOpenKeys;

		// параметры открытого ключа
		CString sOpenKey;
		CString sCertFilePath;
		//CString sUserName;
		CString sUserRoles;
		CString sFullName;
		CString sSerialNumber;

		// параметры сертификата
		CString sSurName;			// SN = Иванов
		CString sGivenName;			// G = Иван Иванович
		CString sTitle; 			// T = Главный конструктор	
		CString sStreet;			// STREET = Набережная Академика Туполева, д.17
		CString sCommonName;		// CN = Иванов Иван Иванович
		CString sMail;				// E = ivanov@tupolev.ru
		CString sOrganization;		// O = ОАО "Туполев"
		CString sOrganizationUnit;  // OU =
		CString sLocality;			// L = Москва
		CString sRegion;			// S = 77 г. Москва
		CString sCountry;			// C = RU
		COleDateTime dNotBefor;
		COleDateTime dNotAfter;

		CString sNewContainer;				// сам ключ, если вдруг нам нужно его сохранить в базе принудительно
		CString sNewContainerPath;			// куда храним контейнер
		CString sNewContainerPassword;		// пароль ждя нового контейнера

		// секретный ключ центра сертификации
		CString sContainerCA;				// путь к файлу
		CString sPasswordCA;				// пароль для файла
		CString sContentContainerCA;		// файл целиком (используется, если не задано "sContainerCA")

		// корневой сертификат
		BYTE *pbRootCertBlob;
		DWORD nRootCertBlobSize;

		// способ шифрования
		CString sSystem;
	};


	CaplCryptographicManager();
	~CaplCryptographicManager();

	/* Public Operations */
	static bool EditCSPSet();	// Диалог выбора системы ЭЦП по умолчанию (не имеет особого смысла т.к. запоминается последняя)

	ESignMode SetSignMode(ESignMode mode);

	void ResetSignTime(); // Обновить дату использования (для минимизации запросов паролей

	static bool CheckCryptoProProvaider2001(); // Проверка наличия КриптоПро	 с поддержкой стандарта 2001г.
	static bool CheckCryptoProProvaider2012(); // Проверка наличия КриптоПро	 с поддержкой стандарта 2012г.

	//Если forsign==false, то возвращает все имеющииеся системы подписи	
	//Если forsign==false, то возвращает только те, которыми можно подписывать
	static BOOL GetAllCSP(CStringArray &allValues, bool forsign);	
	
	static CString GetDefaultCSP(); // Возвращает имя системы ЭЦП по умолчанию	
	static bool SetDefaultCSP(CString sNameDefCSP); // Устанавливает имя системы ЭЦП по умолчанию	

	/************************************************************************/
	/*						Подписывает данные                            */
	/************************************************************************/
	// sUserName - последний подписавший 
	// Если lBufferLen==0, то в pszBuffer - имя файла с данными
	// Иначе pszBuffer - буффер с данными длинной lBufferLen

	int Sign(LPCTSTR sUserName, LPCTSTR sPassword, byte* pszBuffer, long lBufferLen, CString &sHash, CString &sOpenKey, CString &sSignature, CString &sCert, CString sSystem, bool &bOldFormat, CStringArray &saKeys, char *addInfo, int infoSize);	
	int SignEx(bool doCheckEqualName, LPCTSTR sUserName, LPCTSTR sPassword, byte* pszBuffer, long lBufferLen, CString &sHash, CString &sOpenKey, CString &sSignature, CString &sCert, CString sSystem, bool &bOldFormat, CStringArray &saKeys, char *addInfo, int infoSize);	
	
	// RSA (Windows) (sUserName - для проверки сертификата)
	int SignNewContainer(LPCTSTR sUserName, LPCTSTR sPassword, byte* pszBuffer, long lBufferLen, CString &sHash, CString &sOpenKey, CString &sSignature, CString &sCert, char *addInfo, int infoSize);
	int SignNewContainerEx(bool doCheckCorrectName, LPCTSTR sUserName, LPCTSTR sPassword, byte* pszBuffer, long lBufferLen, CString &sHash, CString &sOpenKey, CString &sSignature, CString &sCert, char *addInfo, int infoSize);
	int SignOldContainer(LPCTSTR sPassword, byte* pszBuffer, long lBufferLen, CString &sHash, CString &sOpenKey, CString &sSignature, CString &sCert, char *addInfo, int infoSize);

	// КриптоПро
	int csp_Sign(void *data_buffer, DWORD data_buffer_len, CString &user_name, CString &cert_file_name, CString &sHash, CString &sOpenKey, CString &sSign, char *addInfo, int infoSize, int cspParamMode);


	/************************************************************************/
	/*						Проврека подписи                                */
	/************************************************************************/
	// если lBufferLen==0, то в buffer имя файла с нулем на конце
	// иначе в buffer буффер памяти длинной lBufferLen

	// RSA (Windows)
	int rsa_CheckSign(void *buffer, long lBufferLen, CString sHash, CString sOpenKey, CString sSignature, char *addInfo, int infoSize);
	int rsa_CheckSign(void *buffer, long lBufferLen, CString sHash, BYTE *pbCertBlob, int nCertBlobSize, CString sSignature, char *addInfo, int infoSize);
	
	// КриптоПро
	int csp_CheckSign(void *data_buffer, long data_buffer_len, CString &sHash, BYTE *pbKeyBlob,  DWORD dwBLen, CString &sSign, char *addInfo, int infoSize, int cspParamMode);	
	int csp_CheckSign(void *data_buffer, long data_buffer_len, CString &sHash, CString &sOpenKey, CString sSign, char *addInfo, int infoSize, int cspParamMode);
	
	// OpenSSL
	int ossl_CheckSign(void *buffer, long lBufferLen, LPCTSTR sHash, LPCTSTR sOpenKey, LPCTSTR sSignature, char *addInfo, int infoSize);
	int ossl_CheckSign(void *buffer, long lBufferLen, LPCTSTR sHash, BYTE *pbCertBlob, int nCertBlobSize, LPCTSTR sSignature, char *addInfo, int infoSize);


	/************************************************************************/
	/*						Вычисление хэша                                 */
	/************************************************************************/
	// Получить хэш данных (по кускам + дополнительная информация		
	// если buf_len==0, то в buffer имя файла с нулем на конце
	// иначе в buffer буффер памяти длинной buf_len

	bool GetHash(void *buffer, long buf_len,  LPCTSTR szCryptSystem, CString &sHash);
	
	// RSA (Windows)	{hHash потом надо освободить: if(hHash) CryptDestroyHash(hHash);}
	bool rsa_GetHash(void *buffer, long buf_len, HCRYPTHASH &hHash, CString &sHash, char *addInfo, long infoSize);
	
	// КриптоПро  {hHash потом надо освободить: if(hHash) CryptDestroyHash(hHash);}
	int csp_GetHash(void *data_buffer, DWORD data_buffer_len, CString &ErrorDescr, HCRYPTPROV &hProv, HCRYPTHASH &hHash, CString &strHash, char *addInfo, int infoSize, int cspParamMode);

	// OpenSSL  (bHash, если не 0, то должен быть указатель на 32 байта)
	bool ossl_GetHash(void *buffer, long buf_len, CString &sHash, BYTE *bHash, char *addInfo, long infoSize);


	/************************************************************************/
	/*					Работа с контейнерами					*/
	/************************************************************************/

	bool CreateKey(TCertInfo &certInfo, bool bRoot=false);
	
	// Получить секретный ключ из контейнера	
	bool GetPrivateKeyFromContainer(BYTE *&pbData, int &nLenght, CString sContainerCA, CString sPassword, CString container);
	
	// 	Проверяет пароль для контейнера		
	static bool CheckPassword(CString sContainerCA, CString sPassword); //По пути к контейнеру
	static bool CheckPasswordByContentOfFile(CString sContent, CString sPassword);  // По содержимому контейнера

	// Изменяет пароль
	static bool ChangePassword(CString sContainerCA, CString sPassword); //По пути к контейнеру
	static bool ChangePasswordByContentOfFile(CString sContent, CString sPassword);  // По содержимому контейнера


	/************************************************************************/
	/*				Получить Данные из сертификата			            	*/
	/************************************************************************/

	// Получение различного рода информации из сертификата    
	static bool GetDataFromCertFile(LPCTSTR cert_file, CString &sSubject, CString &sOpenKey, COleDateTime &dNotBefore, COleDateTime &dNotAfter, CString &sIssuer, CString &sCertSerialNum, CString &sAlgorithm); 
	static bool GetDataFromCert(const BYTE *cert_buf, DWORD  cert_size, CString &sSubject, CString &sOpenKey,	COleDateTime &dNotBefore, COleDateTime &dNotAfter, CString &sIssuer, CString &sCertSerialNum, CString &sAlgorithm); 

	// Получение вариантов использования ключа (расширение сертификата  
	static bool  GetKeyUsageFromCert(CString sCertFile, CStringArray &saKeyUsage);

	// Полчить открытый ключ из сертификата, который лежит в контейнере		
	static void GetOpenKeyFromCertInContainer(CString sPath, CString &sKey);

	// Полчить сертификат, который лежит в контейнере	
	static bool GetCertFromContainer(CString sPath, CString sContent, BYTE *&pbData, DWORD &dSize);

	// Получаем информацио из сертификата	
	static bool GetCertSubjectNameAndSN(CString sCert, CString& sSubjName, CString& sSN, CString& sRoles, COleDateTime &tBefore, COleDateTime &tAfter);
	static bool GetCertSubjectNameAndSN(BYTE *pbCert, int dwSLen, CString& sSubjName, CString& sSN, CString& sRoles, COleDateTime &tBefore, COleDateTime &tAfter);

	// Получить значение параметра из строки типа "CN=Имя, S=Мос..."
	static CString GetParamFromSubject(CString str, CString sParam); 

	// Парсим строку c данными владельца или издателя сертификата	
	static bool ParseCertificateParams(CString str, CStringArray &saValues);

	/************************************************************************/
	/*				Работа с CRL  и хранилищами сертификатов	        	*/
	/************************************************************************/

	// Получить серийные номера и даты отозванных ключей
	void GetInfoFromCRL(CString sCRLPath, CStringArray &saSNandRevocationDate);

	// Создать список отзыва	
	int CreateCRL(CStringArray &saSNcert, CArray<COleDateTime, COleDateTime> &RevocationDate, TCertInfo &certInfo);

	// Поиск сертификата в хранилище   
	PCCERT_CONTEXT FindCertificate(LPSTR szCertName, LPSTR szStore,	DWORD dwFlags, PCRYPT_DATA_BLOB *KeyId,	HCRYPTPROV *hProv, LPDWORD dwKeyType);
	static bool AddCertToStore(CString sPath); ///Добавить сертификат в хранилище		

	static int GetPublicKey(LPCTSTR sFile, LPCTSTR sPassword, CString &sOpenKey);	//
	
	static long GetCRC32(char *data,long size);
	static bool _xOR_Revers(BYTE* from, const DWORD fromlen, BYTE* to, const DWORD tolen);
	static bool _xOR_Revers(BYTE* from, const DWORD fromlen, const CString &to);
	static bool ReadFromFile(LPCTSTR szFileName, void* pvBuffer, int &iBufferSize);
	static bool WriteToFile(LPCTSTR szFileName, void* pvBuffer, int &iBufferSize);

	static CString GenRandom(); // Возвращает случайную последовательность из 8 символов  
	static CString CreateRandomGUIDString();
	
	static void StrToByte(DWORD cb, LPCSTR sz, BYTE* pv);
	static void ByteToStr(DWORD cb, void *pv, LPSTR sz);
	static void ByteToStr(DWORD cb, void *pv, CString  &sz);

	static bool Date2String(COleDateTime &time, CString &buf);

	static void SaveStr2Buf(CString str, char *pBuf, int &index);
	static void SaveInt2Buf(int val, char *pBuf, int &index);

	static bool IsCertUser(LPCTSTR s_sys_user, LPCTSTR s_cert_user);
	
	static bool bNoAllMessage; // Флаг для TGB

private:
	static void ShowError(LPCTSTR s);
	static void ShowError(DWORD state);
	static void DeleteKeySet(HCRYPTPROV hProv, LPCTSTR szContainer);

	static bool MakeSecretKeyFile(HCRYPTPROV *phProv, LPCTSTR szFile, LPCTSTR szPassword, HCRYPTKEY hPubKey, DWORD dwCertSize, BYTE *bpCert, CString &sConainer);	

	COleDateTime m_last_sign_time; // время последней подписи
	ESignMode m_SignMode;

public:
	CString m_sLastKeyContent;
	static bool bNoShowCheckResult; // Для вызова из TGB.  Не выводить результат проверки, а в возвращать код результата.

	bool m_bEnableBtnCreareCert4Me; // Открыть кнопку "Создать сертификат" при подписи.
};


//**************************************************************************************
//Специально для TGB

APL_CRYPT_CLASS_EXPORT 
// подпись
int aplSign(char *sUserName, char *sPassword, // sUserName - последний подписавший (по нему запоминает контейнер, чтобы не спрашивать каждый раз)
		 byte* pszBuffer, long lBufferLen,   // Если lBufferLen==0, то в pszBuffer - имя файла с данными, иначе pszBuffer - буффер с данными длинной lBufferLen
		 char* HashBuf,    int *HashBufLen,      // Буфер для хэша и его макс. длинна. HashBufLen устанавливается в новую длинну без учета нуля на конце, 
		 char* OpenKeyBuf, int *OpenKeyBufLen,   // Буфер для открытого ключа и его макс. длинна.
		 char* SignatureBuf, int *SignatureBufLen, // Буфер для подписи и его макс. длинна.
		 char* CertBuf, int *CertBufLen,    // Буфер для сертификата и его макс. длинна.
		 char* szCryptSystem);	// szCryptSystem - строка с системой  см. #define APL_CRYPT_MGR  и #define APL_CRYPTO_PRO_34_10_2001
		 // возвращает код ошибки - см.  #define сверху



APL_CRYPT_CLASS_EXPORT
// вычисление хэша
int aplGetHash(void *buffer, long buf_len, //  Если buf_len==0, то в buffer - имя файла с данными, иначе buffer - буффер с данными длинной lBufferLen
			char *szCryptSystem,	// sSystem - строка с системой  см. #define APL_CRYPT_MGR  и #define APL_CRYPTO_PRO_34_10_2001
			char* HashBuf,    int *HashBufLen);	// Буфер для хэша и его макс. длинна. HashBufLen устанавливается в новую длинну без учета нуля на конце, 	 
             // возвращает код ошибки - см.  #define сверху

APL_CRYPT_CLASS_EXPORT
//Проверка подписи (по сертификату)
int aplCheckSign(void *buffer, long lBufferLen, //  Если buf_len==0, то в buffer - имя файла с данными, иначе buffer - буффер с данными длинной lBufferLen
				  char *szHash, // строка хэша с нулем на конце
				  char *pbCertBlob, int nCertBlobSize, //буффер с сертификатом и его длинна
				  char *szSignature, // строка подписи с нулем на конце
		          char* szCryptSystem);	// szCryptSystem - строка с системой  см. #define APL_CRYPT_MGR  и #define APL_CRYPTO_PRO_34_10_2001
	// возвращает код ошибки - см.  #define сверху

APL_CRYPT_CLASS_EXPORT
//Проверка наличия Криптопро
int aplCheckCryptoPro(); // Проверяет наличие криптопро. Возвращает 1, если есть, и 0, если нет.

// Это функции для TGB, под юникод не нужны
#ifndef UNICODE

extern "C"
{
	APL_CRYPT_CLASS_EXPORT 
		// подпись
		// доп.параметр doCheckEqualName - надо ли контролировать совпадение имени Subject и User
	int aplSignEx(bool doCheckEqualName, char *sUserName, char *sPassword, // sUserName - последний подписавший (по нему запоминает контейнер, чтобы не спрашивать каждый раз)
		byte* pszBuffer, long lBufferLen,   // Если lBufferLen==0, то в pszBuffer - имя файла с данными, иначе pszBuffer - буффер с данными длинной lBufferLen
		char* HashBuf,    int *HashBufLen,      // Буфер для хэша и его макс. длинна. HashBufLen устанавливается в новую длинну без учета нуля на конце, 
		char* OpenKeyBuf, int *OpenKeyBufLen,   // Буфер для открытого ключа и его макс. длинна.
		char* SignatureBuf, int *SignatureBufLen, // Буфер для подписи и его макс. длинна.
		char* CertBuf, int *CertBufLen,    // Буфер для сертификата и его макс. длинна.
		char* szCryptSystem);	// szCryptSystem - строка с системой  см. #define APL_CRYPT_MGR  и #define APL_CRYPTO_PRO_34_10_2001
	// возвращает код ошибки - см.  #define сверху
	//extern "C"
	APL_CRYPT_CLASS_EXPORT
	bool aplCheckCertFileByRootCert(bool isFileCert, const char* cert_file_, const char *folderRootCert); 

	APL_CRYPT_CLASS_EXPORT
	bool aplGetDataFromCertFile(const char* cert_file, char* sSubject, char* sOpenKey, char* dNotBefore, char* dNotAfter, char* sIssuer, char* sCertSerialNum, char* sAlgorithm); 
};

#endif