/*!
* \file $RCSfile$
* \version $Revision: 229589 $
* \date $Date:: 2021-07-05 17:40:07 +0300#$
* \author $Author: sdenis $
*
* \brief Расширение модуля atlcrypt.h из библиотеки ATL Microsoft.
* 
* Подробное описание модуля.
*/
#ifndef _ATLCRYPT2_H_INCLUDED
#define _ATLCRYPT2_H_INCLUDED
#include <list>

#ifndef UNIX
#pragma warning (push)
// warning C6011: dereferencing NULL pointer <name>
#pragma warning (disable:6011)
#endif
#include <boost/shared_ptr.hpp>
#ifndef UNIX
#pragma warning (pop)
#endif

#if defined ATLCRYPT2_H__out
    #undef ATLCRYPT2_H__out
    #define __out
#endif

#include "atldef2.h"

#ifndef UNIX
#pragma warning (push)
#pragma warning (disable:4838)
#endif
#include <atlbase.h>
#ifndef UNIX
#pragma warning (pop)

#pragma warning (push)
#pragma warning (disable:6387)
#pragma warning (disable:6309)
#endif
#include <atlcrypt.h>
#ifndef UNIX
#pragma warning (pop)

#pragma warning (push)
#endif
#include <atlmem.h>
#ifndef UNIX
#pragma warning (pop)

#pragma warning (push)
#endif
#include <atlstr.h>
#ifndef UNIX
#pragma warning (pop)

#pragma warning (push)
#pragma warning (disable:6387)
#endif
#include <atlfile.h>
#ifndef UNIX
#pragma warning (pop)
#endif

#ifdef WINCRYPTEX
#define CPCSP_C_SOURCE 30
#include "WinCryptEx.h"
#endif // WINCRYPTEX

#ifdef CRYPTUI
#include "cryptui.h"
#pragma comment(lib, "cryptui.lib")
#endif // CRYPTUI

#ifdef READPKIOBJECT
#include <atlenc.h>
#endif // READPKIOBJECT

#include "atlalloc2.h"

#ifndef _WIN32
#ifdef CRYPT_DECODE_SHARE_OID_STRING_FLAG
#undef CRYPT_DECODE_SHARE_OID_STRING_FLAG
#define CRYPT_DECODE_SHARE_OID_STRING_FLAG 0
#endif
#endif

typedef const CMSG_CMS_SIGNER_INFO * PCCMSG_CMS_SIGNER_INFO;
typedef const CRYPT_ALGORITHM_IDENTIFIER * PCCRYPT_ALGORITHM_IDENTIFIER;
typedef const CRYPT_ATTRIBUTE * PCCRYPT_ATTRIBUTE;
typedef const CRYPT_ATTRIBUTES * PCCRYPT_ATTRIBUTES;

namespace ATL2 {

extern ATL::CCryptProv EmptyProv;
class CCertStore;
class CCertChainContext;

class CCertContext
{
protected:
    PCCERT_CONTEXT m_pUserCert;
public:
    CCertContext() throw();
    CCertContext(const CCertContext& ctx) throw();
    CCertContext &operator=(const CCertContext& ctx) throw();
    explicit CCertContext(PCCERT_CONTEXT pUserCert,
	BOOL bTakeOwnership = FALSE) throw();
    ~CCertContext() throw();

    void Attach(PCCERT_CONTEXT pUserCert, BOOL bTakeOwnership = FALSE) throw();
    void Destroy() throw();
    PCCERT_CONTEXT Detach() throw();
    PCCERT_CONTEXT Duplicate() const throw();

    HRESULT Uninitialize() throw();
    HRESULT Initialize(const BYTE *pbCertEncoded, DWORD cbCertEncoded,
	DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
	throw();

    HRESULT Initialize(__in const CStringBlob& certEncoded,
	DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
	throw();

    HRESULT DetachFromStore() throw();

    HRESULT GetProperty(__in DWORD dwPropId,
	__out_bcount_part_opt(*pcbData, *pcbData) void *pvData,
	__inout DWORD *pcbData) throw();
    HRESULT GetProperty(__in DWORD dwPropId, __out CStringBlob &Result); //throw(...);
    HRESULT SetProperty (__in DWORD dwPropId, __in DWORD dwFlags,
	__in const void *pvData) throw();

    // CERT_KEY_IDENTIFIER_PROP_ID property
    HRESULT GetKeyIdentifier (
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData) throw();
    HRESULT GetKeyIdentifier (__out CStringBlob &Result); //throw(...);
    HRESULT SetKeyIdentifier (__in const CRYPT_DATA_BLOB *keyId) throw();

    // CERT_KEY_PROV_INFO_PROP_ID property
    HRESULT GetKeyProvInfo (
	__out_bcount_part(*pcbData, *pcbData) CRYPT_KEY_PROV_INFO *provInfo,
	__inout DWORD *pcbData) throw();
    HRESULT GetKeyProvInfo (__out CStringBlob &Result); //throw(...);
    HRESULT SetKeyProvInfo (__in const CRYPT_KEY_PROV_INFO *provInfo) throw();

    //CERT_PUBLIC_KEY_INFO
    HRESULT GetPublicKeyInfo(CERT_PUBLIC_KEY_INFO * keyInfo);

    HRESULT GetNameString (DWORD dwType, DWORD dwFlags,
	void *pvTypePara, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();

    HRESULT GetSubjectNameString (DWORD dwType,
	void *pvTypePara, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameString (DWORD dwType,
	void *pvTypePara, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();

    HRESULT GetSubjectNameStringEmail(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringToStr(DWORD dwStrType, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringAttr(LPCSTR szAttrOid, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringSimple(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringFriendlyDisplay(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringDns(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringUrl(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetSubjectNameStringUpn(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();

    HRESULT GetIssuerNameStringEmail(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringToStr(DWORD dwStrType, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringAttr(LPCSTR szAttrOid, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringSimple(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringFriendlyDisplay(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringDns(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringUrl(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetIssuerNameStringUpn(LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();

    HRESULT GetNameString (DWORD dwType, DWORD dwFlags, void *pvTypePara,
	ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameString (DWORD dwType, void *pvTypePara,
	ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameString (DWORD dwType, void *pvTypePara,
	ATL::CAtlString& Result); //throw(...);

    HRESULT GetSubjectNameStringEmail(ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringToStr(DWORD dwStrType,
	ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringAttr(LPCSTR szAttrOid,
	ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringSimple(ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringFriendlyDisplay(ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringDns(ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringUrl(ATL::CAtlString& Result); //throw(...);
    HRESULT GetSubjectNameStringUpn(ATL::CAtlString& Result); //throw(...);

    HRESULT GetIssuerNameStringEmail(ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringToStr(DWORD dwStrType,
	ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringAttr(LPCSTR szAttrOid,
	ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringSimple(ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringFriendlyDisplay(ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringDns(ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringUrl(ATL::CAtlString& Result); //throw(...);
    HRESULT GetIssuerNameStringUpn(ATL::CAtlString& Result); //throw(...);

    HRESULT AddToStore (CCertStore& CertStore,
	DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext = NULL) const throw();

    HRESULT AddToStoreAlways (CCertStore& CertStore,
	PCCERT_CONTEXT *ppStoreContext = NULL) const throw();
    HRESULT AddToStoreNew (CCertStore& CertStore,
	PCCERT_CONTEXT *ppStoreContext = NULL) const throw();
    HRESULT AddToStoreReplaceExisting (CCertStore& CertStore,
	PCCERT_CONTEXT *ppStoreContext = NULL) const throw();
    HRESULT AddToStoreReplaceExistingInheritProperties (CCertStore& CertStore,
	PCCERT_CONTEXT *ppStoreContext = NULL) const throw();
    HRESULT AddToStoreUseExisting (CCertStore& CertStore,
	PCCERT_CONTEXT *ppStoreContext = NULL) const throw();

    HRESULT DeleteFromStore() throw();

    HRESULT InitPrivateKey(DWORD dwFlags, HCRYPTPROV *phCryptProv,
	DWORD *pdwKeySpec, BOOL *pfCallerFreeProv,
	void *pvReserved = NULL) throw();

    HRESULT BuildChain(
	PCCERT_CHAIN_CONTEXT* ppChainContext,
	HCERTSTORE hAdditionalStore = NULL,
	DWORD dwFlags
	    = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
	    | CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE,
	LPFILETIME pTime = NULL,
	HCERTCHAINENGINE hChainEngine = NULL,
	PCERT_CHAIN_PARA pChainPara = NULL) throw();

    HRESULT BuildChain(
	CCertChainContext& cChainContext,
	HCERTSTORE hAdditionalStore = NULL,
	DWORD dwFlags
	    = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
	    | CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE,
	LPFILETIME pTime = NULL,
	HCERTCHAINENGINE hChainEngine = NULL,
	PCERT_CHAIN_PARA pChainPara = NULL) throw();
    void GetValidFromFiletime(FILETIME *notBefore); //throw(...);
    void GetValidToFiletime(FILETIME *notAfter); //throw(...);

#ifdef _WIN32
    void GetValidFrom(ATL::CAtlString& Result, LCID locale = LOCALE_USER_DEFAULT ); //throw(...);
    void GetValidTo(ATL::CAtlString& Result, LCID locale = LOCALE_USER_DEFAULT ); //throw(...);
#  ifdef WINCRYPTEX
    BOOL GetPrivateKeyValidityPeriod(ATL::CAtlString& notBefore,
				    ATL::CAtlString& notAfter, LCID locale = LOCALE_USER_DEFAULT ); //throw(...);
    BOOL GetPrivateKeyValidityPeriodFiletime(FILETIME *notBefore,
				    FILETIME *notAfter); //throw(...);
#  endif // WINCRYPTEX
    void GetSerialNumberString(ATL::CAtlString& Result); //throw(...);
#endif //_WIN32

#if !defined(_WIN32) || _WIN32_WINNT >= 0x0501
    //Необходимая для реализации функция CryptBinaryToString
    //есть, начиная с XP.
    HRESULT GetThumbprintString(ATL::CAtlString& Result); //throw(...);
#endif // !defined(_WIN32) || _WIN32_WINNT >= 0x0501

    inline PCCERT_CONTEXT GetHandle() const throw()
    {
	return m_pUserCert;
    }
};

class CCRLContext
{
protected:
    PCCRL_CONTEXT m_pUserCRL;
public:
    CCRLContext() throw();
    CCRLContext(const CCRLContext& ctx) throw();
    CCRLContext &operator=(const CCRLContext& ctx) throw();
    explicit CCRLContext(PCCRL_CONTEXT pUserCRL,
	BOOL bTakeOwnership = FALSE) throw();
    ~CCRLContext() throw();

    void Attach(PCCRL_CONTEXT pUserCRL, BOOL bTakeOwnership = FALSE) throw();
    void Destroy() throw();
    PCCRL_CONTEXT Detach() throw();
    PCCRL_CONTEXT Duplicate() const throw();

    HRESULT Uninitialize() throw();
    HRESULT Initialize(const BYTE *pbCRLEncoded, DWORD cbCRLEncoded,
	DWORD dwCRLEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
	throw();

    HRESULT DetachFromStore() throw();

    HRESULT GetProperty(DWORD dwPropId, void *pvData, DWORD *pcbData) throw();
    HRESULT SetProperty(DWORD dwPropId, DWORD dwFlags,
	const void *pvData) throw();

    HRESULT GetProperty(DWORD dwPropId, CStringBlob &Result); //throw(...);

#ifdef _WIN32
    HRESULT GetNameString(DWORD dwStrType, LPTSTR pszNameString,
	DWORD *pdwNameStringLen) throw();
    HRESULT GetNameString(DWORD dwStrType, ATL::CAtlString &NameString) throw();
#endif //_WIN32

    HRESULT AddToStore (CCertStore& CertStore,
	DWORD dwAddDisposition, PCCRL_CONTEXT *ppStoreContext = NULL) throw();

    HRESULT AddToStoreAlways (CCertStore& CertStore,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddToStoreNew (CCertStore& CertStore,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddToStoreReplaceExisting (CCertStore& CertStore,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddToStoreReplaceExistingInheritProperties (CCertStore& CertStore,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddToStoreUseExisting (CCertStore& CertStore,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();

    HRESULT DeleteFromStore() throw();

    inline PCCRL_CONTEXT GetHandle() const throw()
    {
	return m_pUserCRL;
    }
};

class CCertChainContext
{
protected:
    PCCERT_CHAIN_CONTEXT m_pCertChain;
public:
    CCertChainContext() throw();
    CCertChainContext(const CCertChainContext& ctx) throw();
    CCertChainContext &operator=(const CCertChainContext& ctx) throw();
    explicit CCertChainContext(PCCERT_CHAIN_CONTEXT pCertChain,
	BOOL bTakeOwnership = FALSE) throw();
    ~CCertChainContext() throw();

    void Attach(PCCERT_CHAIN_CONTEXT pCertChain, BOOL bTakeOwnership = FALSE) throw();
    void Destroy() throw();
    PCCERT_CHAIN_CONTEXT Detach() throw();
    PCCERT_CHAIN_CONTEXT Duplicate() const throw();

    HRESULT Uninitialize() throw();

    inline PCCERT_CHAIN_CONTEXT GetHandle() const throw()
    {
	return m_pCertChain;
    }
};

class CCertChainEngine
{
protected:
    HCERTCHAINENGINE m_hChainEngine;
public:
    CCertChainEngine() throw();
    explicit CCertChainEngine(HCERTCHAINENGINE hChainEngine) throw();
    ~CCertChainEngine() throw();

    void Attach(HCERTCHAINENGINE hChainEngine) throw();
    void Destroy() throw();
    HCERTCHAINENGINE Detach() throw();
    HRESULT Create(PCERT_CHAIN_ENGINE_CONFIG pConfig) throw();

    HRESULT Uninitialize() throw();


    inline HCERTCHAINENGINE GetHandle() const throw()
    {
	return m_hChainEngine;
    }
private:
    CCertChainEngine(const CCertChainEngine& ctx) throw();
};

#ifdef CRYPTUI
class CCertContextUI: public CCertContext
{
public:
    HRESULT View (HWND hwndParent, LPCWSTR szTitle, DWORD dwFlags = 0,
	BOOL *pPropertiesChanged = NULL, HCERTSTORE hAdditionalStore = NULL) throw();
    HRESULT View (HWND hwndParent, LPCSTR szTitle, DWORD dwFlags = 0,
	BOOL *pPropertiesChanged = NULL, HCERTSTORE hAdditionalStore = NULL) throw();
};

class CCRLContextUI: public CCRLContext
{
public:
    HRESULT View(HWND hwndParent, LPCSTR szTitle, 
	DWORD dwFlags = 0) throw();
    HRESULT View (HWND hwndParent, LPCWSTR szTitle, 
	DWORD dwFlags = 0) throw();
    HRESULT CCRLContextUI::WizExport(
	HWND hwndParent, LPCWSTR szWizardTitle,
	DWORD dwFlags = 0) throw();
};
#endif // CRYPTUI

//Правильным использованием хранилища считается закрытие всех сертификатов и
//прочего, полученного из этого хранилища, до закрытия самого хранилища. По
//утверждению в документации MS, нарушение порядка закрытия не смертельно, но
//может приводить к утечкам памяти.
//
//Чтобы это отследить, в функцию CertCloseStore() подаётся флаг
//CERT_CLOSE_STORE_CHECK_FLAG. Беда в том, что CertDuplicateStore()
//увеличивает счётчик ссылок на данное хранилище, и после этого вызова все
//CertCloseStore(CERT_CLOSE_STORE_CHECK_FLAG) кроме последнего вернут ошибку.
//Чтобы исключить вызовы CertDuplicateStore() парадигма класса CCertStore
//отличается от стандартной. Он хранит в себе HCERTSTORE не непосредственно,
//а в виде умного указателя boost::shared_ptr<HCERTSTORE>. Копирование
//объектов CCertStore при этом не вызывает CertDuplicateStore(), а увеличивает
//собственный счётчик ссылок в объекте shared_ptr.
//
//Присоединение дескриптора HCERTSTORE к CCertStore без завладения им требует
//вызова CertDuplicateStore. Реализовано только в методе AttachWeak(hStore).
//Этот метод создаёт объект, который не будет следить за корректностью закрытия
//хранилища. Использовать его следует только при крайней необходимости.
//Например, если извне передали HCERTSTORE, которое закрывать нельзя.
//Аналогичный конструктор не реализован, дабы не усложнить использование таких
//объектов и не дать запутаться в логике конструктора, который работал бы
//по-другому, нежели в аналогичных классах-обёртках.
//
//Присоединение дескриптора HCERTSTORE к CCertStore для его последующего
//закрытия реализовано в методе AttachOwnership(hStore).
//
//Выбранная парадигма приводит также к тому, что нельзя реализовать метод
//Detach, т.к. отсоединить HCERTSTORE от shared_ptr нельзя, когда на него есть
//несколько ссылок.
//
//Кроме того, класс CCertStore не пытается обмануть пользователей и выполнить
//соглашение об отсутствии исключений во всех членах, как это делает,
//например, CCryptProv. CCryptProv, конечно, не кидает исключений, но некоторые
//методы при ошибках приводят объект в неправильное состояние, после чего его
//использовать небезопасно, а выяснить это нельзя.
//
//Итак, некоторые методы класса CCertStore могут кидать исключения, но по сути
//это возможно только при ошибках выделения памяти (явное использование
//оператора new и неявное его использование через m_collectedStores).
//Даётся базовая гарантия безопасности исключений - после исключения объект
//может оказаться в неопределённом состоянии и использовать его нельзя, если
//только не позвать оператор присваивания или метод Destroy(). В любом случае
//все ресурсы будут корректно освобождены.
//
//При необходимости можно реализовать более строгую гарантию безопасности
//исключений.
class CCertStore
{
protected:
    boost::shared_ptr<HCERTSTORE> m_phStore;
private:
    std::list<CCertStore> m_collectedStores;
public:
    CCertStore();
    //Конструктор копирования, сгенерированный компилятором, нам
    //вполне подойдёт.
    CCertStore& operator=(const CCertStore& store);
    ~CCertStore() throw();

    HRESULT AttachOwnership(HCERTSTORE hStore) throw();
    HRESULT AttachWeak(HCERTSTORE hStore) throw();
    void Destroy();

    HRESULT Initialize (LPCSTR lpszStoreProvider,
	DWORD dwMsgAndCertEncodingType, ATL::CCryptProv& CryptProv,
	DWORD dwFlagsLow, DWORD dwFlagsHigh, const void *pvPara) throw();

    HRESULT InitMemoryStore (
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv *CryptProv = NULL) throw();

    HRESULT InitFileStore (HANDLE hStoreFileHandle,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitFileNameStore (LPCWSTR wszStoreFileName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT InitFileNameStore (LPCSTR szStoreFileName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT InitCollectionStore (
	DWORD dwFlagsLow = 0, ATL::CCryptProv& CryptProv = EmptyProv) throw();

#ifdef _WIN32
    HRESULT InitRegStore (HKEY hStoreRegKey,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();
#endif

    HRESULT InitSystemStore (LPCWSTR wszStoreName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitSystemStore (LPCSTR szStoreName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitSystemRegistryStore (LPCWSTR wszStoreName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitSystemRegistryStore (LPCSTR szStoreName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitPhysicalStore (LPCWSTR wszStoreName,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitMsgStore (HCRYPTMSG hStoreMsg,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT InitPkcs7Store (CRYPT_DATA_BLOB *pEncodedStore,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT InitSerializedStore (CRYPT_DATA_BLOB *pSerializedStore,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitPkcs7Store (const CStringBlob& EncodedStore,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT InitSerializedStore (const CStringBlob& SerializedStore,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT InitLdapStore (LPCWSTR wszLdapQuery,
	DWORD dwFlagsLow = 0, DWORD dwFlagsHigh = 0,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT OpenSystemStore (
	LPCTSTR szSubsystemProtocol,
	ATL::CCryptProv& CryptProv = EmptyProv) throw();

    HRESULT OpenContainerExtensionStore(
	ATL::CCryptProv &cProv) throw();

    HRESULT AddStoreToCollection (const CCertStore& SiblingStore, 
	DWORD dwUpdateFlag = 0, DWORD dwPriority = 0) throw();

    HRESULT EnumCertificatesInStore(PCCERT_CONTEXT pPrevCertContext,
	PCCERT_CONTEXT *ppCertContext) throw();
    HRESULT EnumCertificatesInStore(CCertContext& cert) throw();

    HRESULT EnumCRLsInStore(PCCRL_CONTEXT pPrevCRLContext,
	PCCRL_CONTEXT *ppCRLContext) throw();

#if defined _WIN32
    static HRESULT EnumSystemStore (DWORD dwFlags,
	void *pvSystemStoreLocationPara,
	void *pvArg, PFN_CERT_ENUM_SYSTEM_STORE pfnEnum) throw();
#endif // _WIN32

#if defined _WIN32
    static HRESULT EnumPhysicalStore (const void *pvSystemStore,
	DWORD dwFlags, void *pvArg,
	PFN_CERT_ENUM_PHYSICAL_STORE pfnEnum) throw();
#endif // _WIN32

#if defined _WIN32
    static HRESULT EnumSystemStoreLocation(void *pvArg,
	PFN_CERT_ENUM_SYSTEM_STORE_LOCATION pfnEnum,
	DWORD dwFlags = 0) throw();
#endif // _WIN32

#if defined _WIN32
    HRESULT GetProperty(DWORD dwPropId, BYTE * pbData,
	DWORD * pdwDataLen) throw();
#endif // _WIN32

#if defined _WIN32
    HRESULT SetProperty(DWORD dwPropId, CRYPT_DATA_BLOB *pbData,
	DWORD dwFlags = 0) throw();
#endif /* _WIN32 */

#if defined _WIN32
    HRESULT GetProperty(DWORD dwPropId, CStringBlob &Result); //throw(...);
#endif /* _WIN32 */

#if defined _WIN32
    HRESULT SetProperty(DWORD dwPropId, const CStringBlob &Data,
	DWORD dwFlags = 0) throw();
#endif /* _WIN32 */

#if defined _WIN32
    HRESULT GetLocalizedName(LPWSTR wszBuf, DWORD *pdwStringLength) throw();
#endif /* _WIN32 */

#if defined _WIN32
    HRESULT SetLocalizedName(LPWSTR wszBuf) throw();
#endif /* _WIN32 */

#if defined _WIN32
    HRESULT GetLocalizedName(ATL::CAtlStringW& Result); //throw(...);
#endif /* _WIN32 */

    HRESULT Commit() throw();

    HRESULT AddCertificateContext (PCCERT_CONTEXT pCertContext,
	DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext = NULL) throw();

    HRESULT AddCertificateContextAlways (PCCERT_CONTEXT pCertContext,
	PCCERT_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCertificateContextNew (PCCERT_CONTEXT pCertContext,
	PCCERT_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCertificateContextReplaceExisting (
	PCCERT_CONTEXT pCertContext,
	PCCERT_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCertificateContextReplaceExistingInheritProperties (
	PCCERT_CONTEXT pCertContext,
	PCCERT_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCertificateContextUseExisting (PCCERT_CONTEXT pCertContext,
	PCCERT_CONTEXT *ppStoreContext = NULL) throw();

    HRESULT AddEncodedCertificate (
	const BYTE *pbCertEncoded, DWORD cbCertEncoded, DWORD dwAddDisposition,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT AddEncodedCertificateAlways (
	const BYTE *pbCertEncoded, DWORD cbCertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateNew (
	const BYTE *pbCertEncoded, DWORD cbCertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateReplaceExisting (
	const BYTE *pbCertEncoded, DWORD cbCertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateReplaceExistingInheritProperties (
	const BYTE *pbCertEncoded, DWORD cbCertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateUseExisting (
	const BYTE *pbCertEncoded, DWORD cbCertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT AddEncodedCertificate (const CStringBlob& CertEncoded,
	DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT AddEncodedCertificateAlways (const CStringBlob& CertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateNew (const CStringBlob& CertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateReplaceExisting (const CStringBlob& CertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateReplaceExistingInheritProperties (
	const CStringBlob& CertEncoded, PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT AddEncodedCertificateUseExisting (const CStringBlob& CertEncoded,
	PCCERT_CONTEXT *ppCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT AddCRLContext (PCCRL_CONTEXT pCrlContext,
	DWORD dwAddDisposition, PCCRL_CONTEXT *ppStoreContext = NULL) throw();

    HRESULT AddCRLContextAlways (PCCRL_CONTEXT pCrlContext,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCRLContextNew (PCCRL_CONTEXT pCrlContext,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCRLContextReplaceExisting (
	PCCRL_CONTEXT pCrlContext,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCRLContextReplaceExistingInheritProperties (
	PCCRL_CONTEXT pCrlContext,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();
    HRESULT AddCRLContextUseExisting (PCCRL_CONTEXT pCrlContext,
	PCCRL_CONTEXT *ppStoreContext = NULL) throw();

    HRESULT CountCertsInStore (DWORD& dwCertNumber) throw();

    HRESULT Save (DWORD dwSaveAs, DWORD dwSaveTo, void* pvSaveToPara,
	DWORD dwFlags = 0, DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveAsStore (DWORD dwSaveTo, void* pvSaveToPara) throw();
    HRESULT SaveAsPkcs7 (DWORD dwSaveTo, void* pvSaveToPara,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT SaveToFile (DWORD dwSaveAs, HANDLE hStoreFileHandle,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveToMemory (DWORD dwSaveAs, CRYPT_DATA_BLOB *pStoreBlob,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveToMemory (DWORD dwSaveAs, CStringBlob& StoreBlob,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); //throw(...);
    HRESULT SaveToFileName (DWORD dwSaveAs, LPCWSTR wszStoreFileName,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveToFileName (DWORD dwSaveAs, LPCSTR szStoreFileName,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT SaveAsStoreToFile (HANDLE hStoreFileHandle) throw();
    HRESULT SaveAsStoreToMemory (CRYPT_DATA_BLOB *pStoreBlob) throw();
    HRESULT SaveAsStoreToMemory (CStringBlob& StoreBlob); //throw(...);
    HRESULT SaveAsStoreToFileName (LPCWSTR wszStoreFileName) throw();
    HRESULT SaveAsStoreToFileName (LPCSTR szStoreFileName) throw();

    HRESULT SaveAsPkcs7ToFile (HANDLE hStoreFileHandle,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveAsPkcs7ToMemory (CRYPT_DATA_BLOB *pStoreBlob,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveAsPkcs7ToMemory (CStringBlob& StoreBlob,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveAsPkcs7ToFileName (LPCWSTR wszStoreFileName,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT SaveAsPkcs7ToFileName (LPCSTR szStoreFileName,
	DWORD dwMsgAndCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT FindCertificate (PCCERT_CONTEXT *ppCertContext, DWORD dwFindType,
	const void* pvFindPara = NULL, DWORD dwFindFlags = 0,
	PCCERT_CONTEXT pPrevCertContext = NULL,
	DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT FindCertificate (CCertContext &ctx, DWORD dwFindType, 
	const void* pvFindPara = NULL, DWORD dwFindFlags = 0, 
	DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT FindCertificateExisting (PCCERT_CONTEXT pCertToFind,
	PCCERT_CONTEXT *ppCertContext = NULL, DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT FindCertificateExisting (CCertContext& CertToFind,
        CCertContext *pCert = NULL, DWORD dwCertEncodingType
        = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();

    HRESULT FindCRL (PCCRL_CONTEXT *ppCrlContext, DWORD dwFindType,
	const void* pvFindPara = NULL, DWORD dwFindFlags = 0,
	PCCRL_CONTEXT pPrevCrlContext = NULL,
	DWORD dwCertEncodingType = 0) throw();
    HRESULT FindCRL (CCRLContext &ctx, DWORD dwFindType, 
	const void* pvFindPara = NULL, DWORD dwFindFlags = 0, 
	PCCRL_CONTEXT pPrevCrlContext = NULL, 
	DWORD dwCertEncodingType = 0) throw();

    inline HCERTSTORE GetHandle() const throw()
    {
	return *m_phStore;
    }
};

#ifdef CRYPTUI
class CCertStoreUI: public CCertStore
{
public:
    HRESULT SelectStore (HWND hwndParent,
	LPCWSTR szTitle = NULL, LPCWSTR szDisplayString = NULL,
	DWORD dwEnumFlags = CERT_SYSTEM_STORE_CURRENT_USER,
	DWORD dwFlags = CRYPTUI_ALLOW_PHYSICAL_STORE_VIEW
	| CRYPTUI_RETURN_READ_ONLY_STORE) throw();
    HRESULT SelectStore (HWND hwndParent,
	LPCSTR szTitle, LPCSTR szDisplayString = NULL,
	DWORD dwEnumFlags = CERT_SYSTEM_STORE_CURRENT_USER,
	DWORD dwFlags = CRYPTUI_ALLOW_PHYSICAL_STORE_VIEW
	| CRYPTUI_RETURN_READ_ONLY_STORE) throw();

    HRESULT SelectCertificate (HWND hwndParent, CCertContext &ctx,
	LPCWSTR szTitle = NULL, LPCWSTR szDisplayString = NULL,
	DWORD dwDontUseColumn = 0, HCERTSTORE hSelectedCertStore = NULL,
	PFNCFILTERPROC pFilterCallback = NULL,
	PFNCCERTDISPLAYPROC pDisplayCallback = NULL,
	void *pvCallbackData = NULL) throw();
    HRESULT SelectCertificate (HWND hwndParent, CCertContext &ctx,
	LPCSTR szTitle, LPCSTR szDisplayString = NULL,
	DWORD dwDontUseColumn = 0, HCERTSTORE hSelectedCertStore = NULL,
	PFNCFILTERPROC pFilterCallback = NULL,
	PFNCCERTDISPLAYPROC pDisplayCallback = NULL,
	void *pvCallbackData = NULL) throw();
    HRESULT SelectCertificate (HWND hwndParent, CCertContext &ctx,
	LPCWSTR szTitle, DWORD dwCertificateDlgFlags,
	LPCWSTR szCertificateDlgTitle = NULL, LPCWSTR szDisplayString = NULL,
	DWORD dwDontUseColumn = 0, HCERTSTORE hSelectedCertStore = NULL,
	PFNCFILTERPROC pFilterCallback = NULL) throw();
    HRESULT SelectCertificate (HWND hwndParent, CCertContext &ctx,
	LPCSTR szTitle, DWORD dwCertificateDlgFlags,
	LPCSTR szCertificateDlgTitle, LPCSTR szDisplayString = NULL,
	DWORD dwDontUseColumn = 0, HCERTSTORE hSelectedCertStore = NULL,
	PFNCFILTERPROC pFilterCallback = NULL) throw();

    static BOOL WINAPI SelectCertSetViewParamDisplayCallbackW (
	PCCERT_CONTEXT pCertContext, HWND hWndSelCertDlg,
	void *pvCallbackData) throw();
    static BOOL WINAPI SelectCertSetViewParamDisplayCallbackA (
	PCCERT_CONTEXT pCertContext, HWND hWndSelCertDlg,
	void *pvCallbackData) throw();
};
#endif // CRYPTUI

class CCryptProvEx: public ATL::CCryptProv
{
public:
    CCryptProvEx() throw();
    CCryptProvEx( const CCryptProvEx& prov ) throw();
    CCryptProvEx( const CCryptProv& prov ) throw();
    explicit CCryptProvEx( HCRYPTPROV hProv, BOOL bTakeOwnership = FALSE ) throw();
    //~CCryptProvEx() throw();

    CCryptProvEx& operator=( const CCryptProvEx& prov ) throw();
    CCryptProvEx& operator=( const CCryptProv& prov ) throw();

    HRESULT InitializeByCertificate(
	__in const CCertContext& Cert, __in DWORD dwFlags,
	__out_opt DWORD *pdwKeySpec = NULL,
	__reserved void *pvReserved = NULL) throw();

    static HRESULT SetClientHwnd(__in_opt HWND hWnd) throw();
    HRESULT SetKeyExchangePin(__in_z_opt LPCSTR szPin) throw();
    HRESULT SetKeyExchangePin(__in_z_opt LPCWSTR wszPin) throw();

    HRESULT SetSignaturePin(__in_z_opt LPCSTR szPin) throw();
    HRESULT SetSignaturePin(__in_z_opt LPCWSTR wszPin) throw();

    HRESULT SetUseHardwareRng() throw();
    HRESULT GetKeySetType( DWORD * pdwType ) throw( );
    HRESULT GenRandom(__in ULONG nLength, __out_ecount(nLength) BYTE *pbBuffer) throw();
    HRESULT GenRandom(__in ULONG nLength, __out CStringBlob& Result); //throw(...);

    HRESULT ExportPublicKeyInfo (
	__out_bcount_part(*pcbInfo, *pcbInfo) CERT_PUBLIC_KEY_INFO *pInfo,
	__inout DWORD *pcbInfo, __in DWORD dwKeySpec,
	__in DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) throw();
    HRESULT ExportPublicKeyInfo (__out CStringBlob &Result,
	__in DWORD dwKeySpec, __in DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); //throw(...);

    HRESULT SignAndEncodeCertificate(
        __out_bcount_part(*pcbEncoded, *pcbEncoded) BYTE *pbEncoded,
        __inout DWORD *pcbEncoded, __in LPCSTR lpszStructType,
        __in const void *pvStructInfo,
        __in PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
        __in DWORD dwKeySpec,
        __in DWORD dwCertEncodingType
        = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    HRESULT SignAndEncodeCertificate(
        __out CStringBlob &Result, __in LPCSTR lpszStructType,
        __in const void *pvStructInfo,
        __in PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
        __in DWORD dwKeySpec,
        __in DWORD dwCertEncodingType
        = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); //throw(...)

    HRESULT GetName(
	__out_ecount_part_z(*pdwLength, *pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetName(__out ATL::CAtlStringA& Result); //throw(...);
    HRESULT GetName(__out ATL::CAtlStringW& Result); //throw(...);

    HRESULT GetContainer(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetContainer(__out ATL::CAtlStringA& Result); //throw(...);
    HRESULT GetContainer(__out ATL::CAtlStringW& Result); //throw(...);

    HRESULT GetUniqueContainer(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetUniqueContainer(__out ATL::CAtlStringA &Result); //throw(...);
    HRESULT GetUniqueContainer(__out ATL::CAtlStringW &Result); //throw(...);

#ifdef WINCRYPTEX
    // Возможности КриптоПро CSP 
    HRESULT GetFQCN(
	__out_ecount_part_z(*pdwLength, *pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetFQCN(__out ATL::CAtlStringA &Result); //throw(...);
    HRESULT GetFQCN(__out ATL::CAtlStringW &Result); //throw(...);

    HRESULT GetSelectContainer(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength, __in BOOL bFullyQualified = FALSE) throw();
    HRESULT GetSelectContainer(__out ATL::CAtlStringA &Result,
	__in BOOL bFullyQualified = FALSE); //throw(...);
    HRESULT GetSelectContainer(__out ATL::CAtlStringW &Result,
	__in BOOL bFullyQualified = FALSE); //throw(...);

    HRESULT GetHashOid(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetHashOid(__out ATL::CAtlStringA& Result); //throw(...);
    HRESULT SetHashOid(__in_z LPCSTR szBuf) throw();

    HRESULT GetCipherOid(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetCipherOid(__out ATL::CAtlStringA& Result); //throw(...);
    HRESULT SetCipherOid(__in_z LPCSTR szBuf) throw();

    HRESULT GetSignatureOid(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetSignatureOid(__out ATL::CAtlStringA& Result); //throw(...);
    HRESULT SetSignatureOid(__in_z LPCSTR szBuf) throw();

    HRESULT GetDhOid(
	__out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
	__inout DWORD *pdwLength) throw();
    HRESULT GetDhOid(__out ATL::CAtlStringA& Result); //throw(...);
    HRESULT SetDhOid(__in_z LPCSTR szBuf) throw();

    HRESULT SetContainerExtension(
	__in const CONTAINER_EXTENSION *pContExt) throw();

    // Далее следуют очень сомнительные функции, их следует использовать
    // только для того, чтобы прогружать программно ключи, защищенные на
    // других ключах.

    // Внутренний дескриптор провайдера, всегда 32-битный (даже на x64)
    // Этот дескриптор может быть использован только для PP_SET_PIN
    // Побочным эффектом является полная загрузка ключа
    HRESULT GetInternalCryptProv(__out DWORD *pdwInternalProv) throw();
    HRESULT InternalLoadKey() throw();
    HRESULT SetPin(__in const CRYPT_PIN_INFO *pPinInfo) throw();
#endif //WINCRYPTEX
};

class CCryptHashEx : public ATL::CCryptHash
{
public:
    HRESULT Initialize(__in ATL::CCryptProv &Prov, __in ALG_ID Algid,
	__in_opt LPCTSTR szText = NULL) throw();
    HRESULT Initialize(__in ATL::CCryptProv &Prov, __in ALG_ID Algid,
        __in ATL::CCryptKey &key) throw();
    HRESULT GetValue(
	__out_ecount_part_opt(*pdwSize, *pdwSize) BYTE *pBuf,
	__inout DWORD *pdwSize) throw();
    HRESULT Sign(
	__out_ecount_part_opt(*pdwSigLen, *pdwSigLen) BYTE *pbSignature,
	__inout DWORD *pdwSigLen, __in DWORD dwFlags = 0,
	__in DWORD dwKeySpec = AT_SIGNATURE) throw();
    HRESULT GetValue(__out CStringBlob &Result); //throw(...);
    HRESULT Sign(
	__out CStringBlob &Signature, __in DWORD dwFlags = 0,
	__in DWORD dwKeySpec = AT_SIGNATURE); //throw(...);
    HRESULT VerifySignature(
	__in CStringBlob &Signature, __in ATL::CCryptKey &PubKey,
	__in DWORD dwFlags = 0); //throw(...)
    HRESULT AddSessionKey(__in ATL::CCryptKey &SessionKey,
	__in DWORD dwFlags = 0); //throw(...)
}; // class CCryptHashEx

#ifdef WINCRYPTEX
class CCryptGOST3411Hash : public ATL::CCryptHash
{
public:
    HRESULT Initialize(ATL::CCryptProv &Prov, LPCTSTR szText = NULL) throw();
}; // class CCryptGOST3411Hash
class CCryptGOST3411HashEx : public CCryptHashEx
{
public:
    HRESULT Initialize(ATL::CCryptProv &Prov, LPCTSTR szText = NULL) throw();
}; // class CCryptGOST3411Hash

#endif //WINCRYPTEX

// Класс ATL::CCryptKey инкапсулирует понятие ключа CAPI.
// С ним надо обращаться так же аккуратно, как с ключом CAPI, т.е.
// пореже его копировать, недопускать долгого его хранения и т.п.
// При копировании этого объекта, при конструировании из такого же
// объекта, происходят вызовы CryptDuplicateKey. Для некоторых
// провайдеров, например, нашего, далеко не все ключи позволяют
// операцию дублирования себя. Таким образом, в отличие от класса
// ATL::CCryptProv, класс ATL::CCryptKey не надо рассматривать
// как очень простую обёртку вокруг дескриптора HCRYPTKEY.
//
// Расширить такой класс через наследование проблематично.
// Класс-наследник, содержащий дополнительные функции, но не имеющий
// своих данных, кроме самого дескриптора, очень проблематично
// использовать, не вызывая функции дублирования ключа.
//
// Поэтому парадигма класса ATL2::CCryptKeyEx изменена. Теперь он
// сам не содержит данных, его необходимо инициализировать ссылкой
// на настоящий ATL::CCryptKey или его наследник, конструктор без
// аргументов отсутствует. Т.е. класс ATL2::CCryptKeyEx содержит
// только дополнительные методы, которые может применять к внешнему
// ATL::CCryptKey.
//
// Функции, в которые передавались дескрипторы HCRYPTKEY, которые
// в свою очередь содержались в ATL::CCryptKey или ATL2::CCryptKeyEx,
// лучше переделать в функции, которые принимают на вход ссылку
// на единственный экземпляр ATL::CCryptKey. Если внутри этой функции
// необходимо воспользоваться расширнной функциональностью, нужно
// создать в стеке простой объект ATL2::CCryptKeyEx и инициализировать
// его ссылкой на формальный параметр ::CCryptKey.
class CCryptKeyEx
{
public:
    CCryptKeyEx(__in const ATL::CCryptKey& key) throw();

    HRESULT GetCertificate(
	__out_ecount_part_opt(*pdwCertDataLen,*pdwCertDataLen)
	BYTE *pbCertData,
	__inout DWORD *pdwCertDataLen) throw();
    HRESULT SetCertificate(__in const BYTE *pbCertData) throw();

    HRESULT GetCertificate(__out CStringBlob& Result); //throw(...);
    HRESULT SetCertificate(__in const CStringBlob& Result); //throw(...);

    HRESULT GetX(
	__out_ecount_part_opt(*pdwXLen,*pdwXLen) BYTE *pbX,
	__inout DWORD *pdwXLen) throw();
    HRESULT SetX(__in const _CRYPTOAPI_BLOB *pBlobX) throw();

    HRESULT GetX(__out CStringBlob& Result); //throw(...);
    HRESULT SetX(__in const CStringBlob& Result); //throw(...);

    HRESULT GetParam(__in DWORD dwParam, __out CStringBlob& Result,
	__in DWORD dwFlags = 0); //throw(...);
    HRESULT SetParam(__in DWORD dwParam, __in const CStringBlob& Result,
	__in DWORD dwFlags = 0); //throw(...);

    HRESULT Decrypt(__in BOOL final,
	__inout_ecount_part(*pdwDataLen,*pdwDataLen) BYTE *pbData,
	__inout DWORD *pdwDataLen,
	__in ATL::CCryptHash &Hash = ATL::CCryptHash::EmptyHash, __in DWORD dwFlags = 0)
	throw();
    HRESULT Decrypt(__in_ecount(dwCipherTextLen) const BYTE *pbCipherText,
	__in DWORD dwCipherTextLen,
	__out_ecount_part(*pdwPlainTextLen,*pdwPlainTextLen) BYTE *pbPlainText,
	__inout DWORD *pdwPlainTextLen,
	__in ATL::CCryptHash &Hash = ATL::CCryptHash::EmptyHash, __in DWORD dwFlags = 0)
	throw();

#ifdef WINCRYPTEX
    HRESULT GetHashOid(__out CStringBlob& Result); //throw(...);
    HRESULT SetHashOid(__in const CStringBlob& Value); //throw(...);
    HRESULT GetDhOid(__out CStringBlob& Result); //throw(...);
    HRESULT SetDhOid(__in const CStringBlob& Value); //throw(...);
    HRESULT GetCipherOid(__out CStringBlob& Result); //throw(...);
    HRESULT SetCipherOid(__in const CStringBlob& Value); //throw(...);
#endif //WINCRYPTEX

    HRESULT ExportSimpleBlob(__out CStringBlob& Result,
	__in ATL::CCryptKey &ExpKey, __in DWORD dwFlags = 0); //throw(...);
    HRESULT ExportPublicKeyBlob(__out CStringBlob& Result,
	__in ATL::CCryptKey &ExpKey = ATL::CCryptKey::EmptyKey,
	__in DWORD dwFlags = 0); //throw(...);
    HRESULT ExportPrivateKeyBlob(__out CStringBlob& Result,
	__in ATL::CCryptKey &ExpKey, __in DWORD dwFlags = 0); //throw(...);

    HRESULT GetKeySpec(__out DWORD *pdwKeySpec) throw();

    HRESULT GetKeyLength(__out DWORD * pdwKeyLen) throw();

    HRESULT GetNotAfter(__out FILETIME * pNotAfter) throw();

protected:
    ATL::CCryptKey& m_key;
private:
    // Запрещаем копирование этого класса
    CCryptKeyEx(const CCryptKeyEx&);
    CCryptKeyEx& operator= (const CCryptKeyEx&);
};

// class CCryptImportKeyEx
// Forms a key from an imported key blob
class CCryptImportKeyEx : public ATL::CCryptImportKey
{
public:
    HRESULT Initialize(
	__in ATL::CCryptProv &Prov, __in const CStringBlob& Value,
	__in ATL::CCryptKey &PubKey, __in DWORD dwFlags = 0); //throw(...);
}; // class CCryptImportKeyEx

class CCryptImportPublicKeyInfo : public ATL::CCryptKey
{
public:
    HRESULT Initialize(__in ATL::CCryptProv &Prov,
	__in const PCERT_PUBLIC_KEY_INFO pInfo,
	__in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
	) throw();
}; // class CCryptImportPublicKeyInfo

// class CCryptRandomDefferedKey
// Случайный ключ с отложенной генерацией
class CCryptRandomDefferedKey : public ATL::CCryptRandomKey
{
public:
    HRESULT Initialize(__in ATL::CCryptProv &Prov,
	__in ALG_ID algid = CALG_RC4,
	__in DWORD dwFlags = CRYPT_EXPORTABLE) throw();
    HRESULT Generate() throw();
}; // class CCryptRandomKey

// class CCryptUserKey
// Obtains the user's key exchange or signature key pair.
class CCryptUserKey : public ATL::CCryptKey
{
public:
    HRESULT Initialize(__in ATL::CCryptProv &Prov, __in DWORD dwKeySpec) throw();
    HRESULT Create(__in ATL::CCryptProv &Prov, __in DWORD dwKeySpec,
	__in DWORD dwKeyLen = 0, __in DWORD dwFlags = 0) throw();
}; // class CCryptUserKey


HRESULT CopyKeyParam(ATL::CCryptKey& keyDst, DWORD pp, ATL::CCryptKey& keySrc,
    DWORD dwSetFlags = 0); //throw(...);

// Класс CCryptKeyNotDuplicatable предназначен для оборачивания HCRYPTKEY
// в класс, приводимый к ATL::CCryptKey, для тех HCRYPTKEY, которые не
// поддерживают операцию CryptDuplicateKey() (наример, ключевая пара
// в нашем CSP). Фактически ключ не берётся под контроль, а только
// становится приводимым к ATL::CCryptKey. Пользоваться надо осторожно.
// Нельзя его передавать под контроль другого кода в виде ATL::CCryptKey,
// иначе будет позван неправильный деструктор и ключ уничтожится.
class CCryptKeyNotDuplicatable : public ATL::CCryptKey
{
public:
    explicit CCryptKeyNotDuplicatable(__in HCRYPTKEY hKey) throw();
    ~CCryptKeyNotDuplicatable() throw();
};

// COidInfoEnum - класс для перечисления всех OID-ов
//
// Пользоваться классом COidInfoEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnOidInfo.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public COidInfoEnum {
// public:
//    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class COidInfoEnum
{
public:
    HRESULT Enum(DWORD dwGroupId);
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo) = 0;
    virtual ~COidInfoEnum() {}
private:
    static BOOL WINAPI EnumOIDInfoCallback(PCCRYPT_OID_INFO pInfo, void *pvArg);
};

// The CRYPT_EXT_OR_ATTR_OID_GROUP_ID,
// CRYPT_ENHKEY_USAGE_OID_GROUP_ID, CRYPT_POLICY_OID_GROUP_ID or
// CRYPT_TEMPLATE_OID_GROUP_ID don't have a dwValue.

// The CRYPT_*_ALG_OID_GROUP_ID's have an Algid.

// CHashAlgorithmOidEnum - класс для перечисления всех OID-ов
// алгоритмов хэш функции
//
// Пользоваться классом CHashAlgorithmOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnHashAlg.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CHashAlgorithmOidEnum {
// public:
//    virtual bool OnHashAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CHashAlgorithmOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    virtual bool OnHashAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_*_ALG_OID_GROUP_ID's have an Algid.

// CEncryptionAlgorithmOidEnum - класс для перечисления всех OID-ов
// алгоритмов шифрования
//
// Пользоваться классом CEncryptionAlgorithmOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnEncryptAlg.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CEncryptionAlgorithmOidEnum {
// public:
//    virtual bool OnEncryptAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CEncryptionAlgorithmOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    virtual bool OnEncryptAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_*_ALG_OID_GROUP_ID's have an Algid.
//
// CRYPT_PUBKEY_ALG_OID_GROUP_ID has the following optional ExtraInfo:
//  DWORD[0] - Flags. CRYPT_OID_INHIBIT_SIGNATURE_FORMAT_FLAG can be set to
//             inhibit the reformatting of the signature before
//             CryptVerifySignature is called or after CryptSignHash
//             is called. CRYPT_OID_USE_PUBKEY_PARA_FOR_PKCS7_FLAG can
//             be set to include the public key algorithm's parameters
//             in the PKCS7's digestEncryptionAlgorithm's parameters.
//             CRYPT_OID_NO_NULL_ALGORITHM_PARA_FLAG can be set to omit
//             NULL parameters when encoding.

// CPublicKeyAlgorithmOidEnum - класс для перечисления всех OID-ов
// алгоритмов открытого ключа
//
// Пользоваться классом CPublicKeyAlgorithmOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnPubKeyAlg.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CPublicKeyAlgorithmOidEnum {
// public:
//    virtual bool OnPubKeyAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid,
//        DWORD dwFlags)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CPublicKeyAlgorithmOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    // dwFlags может принимать следующие значения:
    // CRYPT_OID_INHIBIT_SIGNATURE_FORMAT_FLAG
    // CRYPT_OID_USE_PUBKEY_PARA_FOR_PKCS7_FLAG
    // CRYPT_OID_NO_NULL_ALGORITHM_PARA_FLAG
    virtual bool OnPubKeyAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid,
	DWORD dwFlags) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_*_ALG_OID_GROUP_ID's have an Algid.

// CRYPT_SIGN_ALG_OID_GROUP_ID has the following optional ExtraInfo:
//  DWORD[0] - Public Key Algid.
//  DWORD[1] - Flags. Same as above for CRYPT_PUBKEY_ALG_OID_GROUP_ID.
//  DWORD[2] - Optional CryptAcquireContext(CRYPT_VERIFYCONTEXT)'s dwProvType.
//             If omitted or 0, uses Public Key Algid to select
//             appropriate dwProvType for signature verification.

// CSignatureAlgorithmOidEnum - класс для перечисления всех OID-ов
// алгоритмов подписи
//
// Пользоваться классом CSignatureAlgorithmOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnSignAlg.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CSignatureAlgorithmOidEnum {
// public:
//    virtual bool OnSignAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid,
//         ALG_ID PublicKeyAlgid, DWORD dwFlags, DWORD dwProvType)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CSignatureAlgorithmOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    // dwFlags может принимать следующие значения:
    // CRYPT_OID_INHIBIT_SIGNATURE_FORMAT_FLAG
    // CRYPT_OID_USE_PUBKEY_PARA_FOR_PKCS7_FLAG
    // CRYPT_OID_NO_NULL_ALGORITHM_PARA_FLAG
    virtual bool OnSignAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid,
	ALG_ID PublicKeyAlgid, DWORD dwFlags, DWORD dwProvType) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_RDN_ATTR_OID_GROUP_ID has a dwLength.

// CRYPT_RDN_ATTR_OID_GROUP_ID has the following optional ExtraInfo:
//  Array of DWORDs:
//   [0 ..] - Null terminated list of acceptable RDN attribute
//            value types. An empty list implies CERT_RDN_PRINTABLE_STRING,
//            CERT_RDN_UNICODE_STRING, 0.

// CRdnAttributeOidEnum - класс для перечисления всех OID-ов
// атрибутов RDN
//
// Пользоваться классом CRdnAttributeOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnRdnAttr.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CRdnAttributeOidEnum {
// public:
//    virtual bool OnRdnAttr(LPCSTR pszOID, LPCWSTR pwszName, DWORD dwLength,
//        DWORD *pdwAcceptableValueTypes, DWORD cAcceptableValueTypes)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CRdnAttributeOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    // dwLength - видимо, пока всегда нулевая
    // pdwAcceptableValueTypes - указывает на массив DWORD-ов
    // cAcceptableValueTypes - число элементов (DWORD-ов)
    // в масиве pdwAcceptableValueTypes,
    // последний нулевой элемент не учитывается 
    virtual bool OnRdnAttr(LPCSTR pszOID, LPCWSTR pwszName, DWORD dwLength,
	DWORD *pdwAcceptableValueTypes, DWORD cAcceptableValueTypes) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_EXT_OR_ATTR_OID_GROUP_ID,
// CRYPT_ENHKEY_USAGE_OID_GROUP_ID, CRYPT_POLICY_OID_GROUP_ID or
// CRYPT_TEMPLATE_OID_GROUP_ID don't have a dwValue.

// CExtensionOrAttributeOidEnum - класс для перечисления всех OID-ов
// расширений или атрибутов
//
// Пользоваться классом CExtensionOrAttributeOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnExtOrAttr.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CExtensionOrAttributeOidEnum {
// public:
//    virtual bool OnExtOrAttr(LPCSTR pszOID, LPCWSTR pwszName)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CExtensionOrAttributeOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    virtual bool OnExtOrAttr(LPCSTR pszOID, LPCWSTR pwszName) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_EXT_OR_ATTR_OID_GROUP_ID,
// CRYPT_ENHKEY_USAGE_OID_GROUP_ID, CRYPT_POLICY_OID_GROUP_ID or
// CRYPT_TEMPLATE_OID_GROUP_ID don't have a dwValue.

// CEnhancedKeyUsageOidEnum - класс для перечисления всех OID-ов EKU
//
// Пользоваться классом CEnhancedKeyUsageOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnEnhKeyUsage.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CEnhancedKeyUsageOidEnum {
// public:
//    virtual bool OnEnhKeyUsage(LPCSTR pszOID, LPCWSTR pwszName)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CEnhancedKeyUsageOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    virtual bool OnEnhKeyUsage(LPCSTR pszOID, LPCWSTR pwszName) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_EXT_OR_ATTR_OID_GROUP_ID,
// CRYPT_ENHKEY_USAGE_OID_GROUP_ID, CRYPT_POLICY_OID_GROUP_ID or
// CRYPT_TEMPLATE_OID_GROUP_ID don't have a dwValue.

// CPolicyOidEnum - класс для перечисления всех OID-ов политик
//
// Пользоваться классом CPolicyOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnPolicy.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CPolicyOidEnum {
// public:
//    virtual bool OnPolicy(LPCSTR pszOID, LPCWSTR pwszName)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CPolicyOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    virtual bool OnPolicy(LPCSTR pszOID, LPCWSTR pwszName) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// The CRYPT_EXT_OR_ATTR_OID_GROUP_ID,
// CRYPT_ENHKEY_USAGE_OID_GROUP_ID, CRYPT_POLICY_OID_GROUP_ID or
// CRYPT_TEMPLATE_OID_GROUP_ID don't have a dwValue.

// CTemplateOidEnum - класс для перечисления всех OID-ов шаблонов
//
// Пользоваться классом CTemplateOidEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnTemplate.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CTemplateOidEnum {
// public:
//    virtual bool OnTemplate(LPCSTR pszOID, LPCWSTR pwszName)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения OID-ов,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CTemplateOidEnum: public COidInfoEnum
{
public:
    HRESULT Enum();
    virtual bool OnTemplate(LPCSTR pszOID, LPCWSTR pwszName) = 0;
protected:
    virtual bool OnOidInfo(PCCRYPT_OID_INFO pInfo);
};

// Класс CFindSignatureAlgPubKeyEnum предназначен для поиска алгоритма
// подписи по заданному алгоритму хэша (в виде ALG_ID) и алгоритму ключа
// (в виде OID).
// Пользоваться классом можно следующим образом:
//
// PCCRYPT_OID_INFO pOidInfo = 0;
// CFindSignatureAlgPubKeyEnum findEnum( hashAlgid, szPubKeyAlgOID );
// HRESULT hr = findEnum.FindSignatureAlgorithmOIDInfo(&pOidInfo);
// if(FAILED(hr))
//    return hr;
//
class CFindSignatureAlgPubKeyEnum: private CPublicKeyAlgorithmOidEnum
{
public:
    CFindSignatureAlgPubKeyEnum( ALG_ID hashAlg, const char* szPubKeyAlgOID)
	: m_hashAlg(hashAlg), m_sPubKeyAlgOID(szPubKeyAlgOID),
	m_pOidInfo(0) { }
    HRESULT FindSignatureAlgorithmOIDInfo( PCCRYPT_OID_INFO *ppOidInfo);
private:
    bool OnPubKeyAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid, DWORD dwFlags);

    ALG_ID m_hashAlg;
    ATL::CAtlStringA m_sPubKeyAlgOID;
    PCCRYPT_OID_INFO m_pOidInfo;
};

// Класс CFindSignatureAlgorithmEnum предназначен для поиска алгоритма
// подписи по заданному алгоритму хэша (в виде OID) и алгоритму ключа
// (в виде OID).
// Пользоваться классом можно следующим образом:
//
// PCCRYPT_OID_INFO pOidInfo = 0;
// CFindSignatureAlgorithmEnum findEnum( szHashAlgOID, szPubKeyAlgOID );
// HRESULT hr = findEnum.FindSignatureAlgorithmOIDInfo(&pOidInfo);
// if(FAILED(hr))
//    return hr;
//
class CFindSignatureAlgorithmEnum: private CHashAlgorithmOidEnum
{
public:
    CFindSignatureAlgorithmEnum(const char* szHashAlgOID, const char* szPubKeyAlgOID)
	: m_sHashAlgOID(szHashAlgOID), m_sPubKeyAlgOID(szPubKeyAlgOID),
	m_pOidInfo(0), m_hr(CRYPT_E_NOT_FOUND) {}
    HRESULT FindSignatureAlgorithmOIDInfo( PCCRYPT_OID_INFO *ppOidInfo);
private:
    bool OnHashAlg(LPCSTR pszOID, LPCWSTR pwszName, ALG_ID Algid);

    ATL::CAtlStringA m_sHashAlgOID;
    ATL::CAtlStringA m_sPubKeyAlgOID;
    PCCRYPT_OID_INFO m_pOidInfo;
    HRESULT m_hr;
};


template <typename T> BOOL WINAPI CryptEnumProviderTypesT( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, T *szTypeName,
    DWORD *pcbTypeName);
template <> BOOL WINAPI CryptEnumProviderTypesT<char>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, char *szTypeName,
    DWORD *pcbTypeName);
template <> BOOL WINAPI CryptEnumProviderTypesT<wchar_t>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, wchar_t *szTypeName,
    DWORD *pcbTypeName);

template <typename T> BOOL WINAPI CryptEnumProvidersT( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, T *szProvName,
    DWORD *pcbProvName);
template <> BOOL WINAPI CryptEnumProvidersT<char>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, char *szProvName,
    DWORD *pcbProvName);
template <> BOOL WINAPI CryptEnumProvidersT<wchar_t>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, wchar_t *szProvName,
    DWORD *pcbProvName);

// CCryptProvidersEnum - класс для перечисления всех провайдеров
//
// Пользоваться классом CCryptProvidersEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnProv.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CCryptProvidersEnum {
// public:
//    virtual bool OnProv(LPCTSTR szProvName, DWORD dwProvType)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения провайдеров,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
template <typename T>
class CCryptProvidersEnumT
{
    enum { TempBufferLen = 256, TempBufferByteLen = TempBufferLen*sizeof(T) };
    typedef CTempBufferEx<T, TempBufferByteLen> CTempBuf;
public:
    explicit CCryptProvidersEnumT(ATL::IAtlMemMgr *pMemMgr = &CTempBuf::m_crtHeap);
    HRESULT Enum();
    virtual bool OnProv(const T *szProvName, DWORD dwProvType) = 0;
    virtual ~CCryptProvidersEnumT() { }
private:
    CTempBuf m_TempBuf;
};
typedef CCryptProvidersEnumT<wchar_t> CCryptProvidersEnumW;
typedef CCryptProvidersEnumT<char> CCryptProvidersEnumA;
#ifdef UNICODE
typedef CCryptProvidersEnumW CCryptProvidersEnum;
#else // !UNICODE
typedef CCryptProvidersEnumA CCryptProvidersEnum;
#endif // !UNICODE

// CCryptProviderTypesEnum - класс для перечисления всех провайдеров
//
// Пользоваться классом CCryptProviderTypesEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnType.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CCryptProvidersEnum {
// public:
//    virtual bool OnType(DWORD dwProvType, LPCTSTR szProvName)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения типов провайдеров,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
template <typename T>
class CCryptProviderTypesEnumT
{
    enum { TempBufferLen = 128, TempBufferByteLen = TempBufferLen*sizeof(T) };
    typedef CTempBufferEx<T, TempBufferByteLen> CTempBuf;
public:
    explicit CCryptProviderTypesEnumT(ATL::IAtlMemMgr *pMemMgr = &CTempBuf::m_crtHeap);
    HRESULT Enum();
    virtual bool OnType(DWORD dwProvType, const T *szTypeName) = 0;
    virtual ~CCryptProviderTypesEnumT() { }
private:
    CTempBuf m_TempBuf;
};
typedef CCryptProviderTypesEnumT<wchar_t> CCryptProviderTypesEnumW;
typedef CCryptProviderTypesEnumT<char> CCryptProviderTypesEnumA;
#ifdef UNICODE
typedef CCryptProviderTypesEnumW CCryptProviderTypesEnum;
#else // !UNICODE
typedef CCryptProviderTypesEnumA CCryptProviderTypesEnum;
#endif // !UNICODE


template <typename T> HRESULT CryptGetProvParamAnsiStringT(
    const ATL::CCryptProv &prov, DWORD dwParam, T *szData,
    DWORD *pcbDataLen, DWORD dwFlags);
template <> HRESULT CryptGetProvParamAnsiStringT<char>(
    const ATL::CCryptProv &prov, DWORD dwParam, char *szData,
    DWORD *pcbDataLen, DWORD dwFlags);
template <> HRESULT CryptGetProvParamAnsiStringT<wchar_t>(
    const ATL::CCryptProv &prov, DWORD dwParam, wchar_t *wszData,
    DWORD *pcbDataLen, DWORD dwFlags);
template <typename T> HRESULT CryptGetProvParamUnicodeStringT(
    const ATL::CCryptProv &prov, DWORD dwParam, T *szData,
    DWORD *pcbDataLen, DWORD dwFlags);
template <> HRESULT CryptGetProvParamUnicodeStringT<char>(
    const ATL::CCryptProv &prov, DWORD dwParam, char *szData,
    DWORD *pcbDataLen, DWORD dwFlags);
template <> HRESULT CryptGetProvParamUnicodeStringT<wchar_t>(
    const ATL::CCryptProv &prov, DWORD dwParam, wchar_t *wszData,
    DWORD *pcbDataLen, DWORD dwFlags);
template <typename T> HRESULT CryptGetProvParamStringT(
    const ATL::CCryptProv &prov, DWORD dwParam, T *szData,
    DWORD *pcbDataLen, DWORD dwFlags);

template <typename T>
class CCryptProvContainersEnumT
{
    enum { TempBufferLen = 1024, TempBufferByteLen = TempBufferLen*sizeof(T) };
    typedef CTempBufferEx<T, TempBufferByteLen> CTempBuf;
public:
    explicit CCryptProvContainersEnumT(ATL::IAtlMemMgr *pMemMgr = &CTempBuf::m_crtHeap);
    HRESULT Enum(const ATL::CCryptProv &prov);
    virtual bool OnContainer(const T *szContName) = 0;
#ifdef WINCRYPTEX
    HRESULT EnumFqcn(const ATL::CCryptProv &prov);
    HRESULT EnumUnique(const ATL::CCryptProv &prov);
    virtual bool OnUniqueContainer(const T *szUniqueContName, const T *szContName);
#endif //WINCRYPTEX
private:
    HRESULT Enum(const ATL::CCryptProv &prov, DWORD dwFlags);
    CTempBuf m_TempBuf;
};
typedef CCryptProvContainersEnumT<wchar_t> CCryptProvContainersEnumW;
typedef CCryptProvContainersEnumT<char> CCryptProvContainersEnumA;
#ifdef UNICODE
typedef CCryptProvContainersEnumW CCryptProvContainersEnum;
#else // !UNICODE
typedef CCryptProvContainersEnumA CCryptProvContainersEnum;
#endif // !UNICODE

#ifdef WINCRYPTEX
// CContainerExtensionEnum - класс для перечисления расширений контейнера
//
// Пользоваться классом CContainerExtensionEnum довольно просто:
// 1. Надо отнаследоваться, создать класс-наследник MyEnum,
//    и реализовать в нем отсутствующую виртуальную функцию OnContainerExtension.
// 2. Надо создать экземпляр этого нового класса MyEnum.
// 3. Надо вызвать метод Enum.
// Пример:
//
//class MyEnum: public CContainerExtensionEnum {
// public:
//    virtual bool OnContainerExtension(LPCSTR szOid, const BYTE* pbValue,
//	DWORD cbValue, BOOL bCritical)
//    {
//      //Здесь делаем, все, что надо, сохраняем значения расширений,
//      // или, что лучше, просто посылаем сообщения другим компонентам
//      // системы (CB_ADDSTRING).
//	return true; // true означает продолжение процесса перечисления
//    }
//};
//MyEnum myenum;
//HRESULT hr = myenum.Enum();
class CContainerExtensionEnum {
public:
    HRESULT Enum(const ATL::CCryptProv& prov );
    virtual bool OnContainerExtension(const CONTAINER_EXTENSION *pValue,
	DWORD cbValue) = 0;
    virtual bool OnContainerExtension(LPCSTR szOid, const BYTE* pbValue,
	DWORD cbValue, BOOL bCritical) = 0;
    virtual ~CContainerExtensionEnum() {}
};
#endif // WINCRYPTEX

#ifdef READPKIOBJECT
//   Группа функций CertReadPKIObject() - функции для зачитывания объектов
// PKI (сертификатов, CRL, PKCS10, PKCS7, штампов времени, и т.д.)
// из файлов и буферов памяти. Задача этих функций - распознать
// формат, в котором храниться объект PKI (DER, base64, base64 с заголовком),
// кодировку (ASCII, UTF8, UTF16LE, UTF16BE) и вернуть объект в бинарном 
// (DER) формате.
//   Функции не проверяют соответствие заголовка в случае, если исходные
// данные находятся в формате base64, а игнорируют его.
// При работе c base64 данные функции исходят из предположения, что входной
// буфер содержит только сообщение в base64 с заголовком или без и не
// содержит никакого дополнительного текста и символов, кроме
// словаря кодировки base64 и переносов строки.

HRESULT CertReadPKIObject( const BYTE *pbSourceData, DWORD cbSourceData,
    BYTE *pbResultData, DWORD *pcbResultData);
HRESULT CertReadPKIObject( const BYTE *pbSourceData, DWORD cbSourceData,
    ATL::CAtlStringA& buffer);

HRESULT CertReadPKIObjectFileA( LPCSTR szFilePath, ATL::CAtlStringA& buffer );
HRESULT CertReadPKIObjectFileW( LPCWSTR wszFilePath, ATL::CAtlStringA& buffer );

#ifdef _UNICODE
#define CertReadPKIObjectFile CertReadPKIObjectFileW
#else
#define CertReadPKIObjectFile CertReadPKIObjectFileA
#endif // _UNICODE

HRESULT CertReadCertificate( const BYTE *pbSourceData, DWORD cbSourceData,
    PCCERT_CONTEXT *ppCertContext);

HRESULT CertReadCertificateFileA( LPCSTR szFilePath,
        PCCERT_CONTEXT *ppCertContext);
HRESULT CertReadCertificateFileW( LPCWSTR wszFilePath,
	PCCERT_CONTEXT *ppCertContext);
HRESULT CertReadCertificateFileA( LPCSTR szFilePath,
        CCertContext& cert);
HRESULT CertReadCertificateFileW( LPCWSTR szFilePath,
        CCertContext& cert);

#ifdef _UNICODE
#define CertReadCertificateFile CertReadCertificateFileW
#else
#define CertReadCertificateFile CertReadCertificateFileA
#endif // _UNICODE

HRESULT CertReadCRL( const BYTE *pbSourceData, DWORD cbSourceData,
    PCCRL_CONTEXT *ppCrlContext);

HRESULT CertReadCRLFileA( LPCSTR szFilePath,
    PCCRL_CONTEXT *ppCrlContext);
HRESULT CertReadCRLFileW( LPCWSTR wszFilePath,
    PCCRL_CONTEXT *ppCrlContext);

#ifdef _UNICODE
#define CertReadCRLFile CertReadCRLFileW
#else
#define CertReadCRLFile CertReadCRLFileA
#endif // _UNICODE
#endif // READPKIOBJECT

// Простой класс - не забывает вернуть hWnd взад
class CAutoCryptProvWnd
{
    // HWND m_hWnd;
public:
    CAutoCryptProvWnd(HWND hWnd);
    ~CAutoCryptProvWnd();
};

class CCryptKeyProvInfo
{
    CRYPT_KEY_PROV_INFO m_KeyProvInfo;
    ATL::CAtlStringW m_ContainerName;
    ATL::CAtlStringW m_ProvName;
    ATL::CSimpleArray<CRYPT_KEY_PROV_PARAM> m_CryptKeyProvParams;
    ATL::CSimpleArray<CStringBlob> m_Params;
public:
    CCryptKeyProvInfo();
    ~CCryptKeyProvInfo();
    const CRYPT_KEY_PROV_INFO* GetHandle() const throw();
    operator const CRYPT_KEY_PROV_INFO*() throw();
    void Uninitialize ();
    bool IsEmpty() throw();

    HRESULT Initialize (__in DWORD dwProvType,
	__in LPWSTR pwszProvName, __in LPCWSTR pwszContainerName,
	__in DWORD dwKeySpec, __in_opt DWORD dwFlags = 0);
    HRESULT Initialize (__in const CRYPT_KEY_PROV_INFO *pInfo);

    // Эта функция по ключу key и провайдеру prov заполняет
    // структуру CRYPT_KEY_PROV_INFO (после чего её можно
    // передавать в CCryptStore::SetKeyProvInfo
    HRESULT Initialize (
	__in const ATL::CCryptProv& prov, __in const ATL::CCryptKey& key,
	__in DWORD dwFlags = 0, __in_opt DWORD dwKeySpec = 0);

    HRESULT AddKeyProvParam(
	__in DWORD dwParam, __in_bcount(cbData) BYTE *pbData,
	__in DWORD cbData, __reserved DWORD dwFlags = 0); //throw(...);
};

class CCryptMsg
{
protected:
    HCRYPTMSG m_hMsg;
public:
    CCryptMsg() throw();
    CCryptMsg(const CCryptMsg& msg) throw();
    CCryptMsg &operator=(const CCryptMsg& msg) throw();
    explicit CCryptMsg(HCRYPTMSG hMsg,
	BOOL bTakeOwnership = FALSE) throw();
    ~CCryptMsg() throw();

    void Attach(HCRYPTMSG hMsg, BOOL bTakeOwnership = FALSE) throw();
    void Destroy() throw();
    HCRYPTMSG Detach() throw();
    HCRYPTMSG Duplicate() const throw();

    HRESULT Uninitialize() throw();

    HRESULT OpenToEncode(DWORD dwMsgType, const void* pvMsgEncodeInfo,
	DWORD dwFlags = 0, PCMSG_STREAM_INFO pStreamInfo = NULL,
	DWORD dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	LPSTR pszInnerContentObjID = NULL) throw();
    HRESULT OpenToDecode(DWORD dwMsgType = 0, DWORD dwFlags = 0,
	PCMSG_STREAM_INFO pStreamInfo = NULL,
	DWORD dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	HCRYPTPROV hCryptProv = 0, /* XXX dim: в этом месте gcc-3.4.6 не понимает NULL */
	PCERT_INFO pRecipientInfo = NULL) throw();

    HRESULT Update(const BYTE *pbData, DWORD cbData,
	BOOL fFinal = TRUE) throw();
    HRESULT Update(const CStringBlob& Data, BOOL fFinal = TRUE); // throw(...);

    HRESULT Countersign(__in DWORD dwIndex, __in DWORD cCountersigners,
	__in_ecount(cCountersigners) PCMSG_SIGNER_ENCODE_INFO rgCountersigners)
	throw();

    HRESULT Control(__in DWORD dwCtrlType, __in const void* pvCtrlPara,
	__in DWORD dwFlags = 0) throw();

    // CMSG_CTRL_ADD_SIGNER
    HRESULT AddSigner(
	__in PCMSG_SIGNER_ENCODE_INFO para, __in DWORD dwFlags = 0) throw();

    // CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR
    HRESULT AddSignerUnauthAttr(
	__in PCMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA para) throw();

    HRESULT GetParam(__in DWORD dwParamType, __in DWORD dwIndex,
	__out_bcount_part_opt(*pcbData, *pcbData) void *pvData,
	__inout DWORD *pcbData) throw();
    HRESULT GetParam(__in DWORD dwParamType, __in DWORD dwIndex,
	__out CStringBlob &Result); //throw(...);

    HRESULT GetDwordParam(__in DWORD dwParamType, __in DWORD dwIndex,
	__out DWORD &dwResult) throw();
    HRESULT GetAttrParam(__in DWORD dwParamType, __in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData, __out PCCRYPT_ATTRIBUTES& pcAttrs) throw();
    HRESULT GetAttrParam(__in DWORD dwParamType, __in DWORD dwIndex,
	__out CStringBlob& Result,
	__out PCCRYPT_ATTRIBUTES& pcAttrs); //throw(...);

    // CMSG_CONTENT_PARAM
    HRESULT GetContent(
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData) throw();
    HRESULT GetContent(__out CStringBlob& Result); //throw(...);

    // CMSG_ENCODED_MESSAGE
    HRESULT GetEncodedMessage(
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData) throw();
    HRESULT GetEncodedMessage(__out CStringBlob& Result); //throw(...);

    // CMSG_ENCODED_SIGNER
    HRESULT GetEncodedSigner(__in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData) throw();
    HRESULT GetEncodedSigner(__in DWORD dwIndex,
	__out CStringBlob& Result); //throw(...);

    // CMSG_ENCRYPTED_DIGEST
    HRESULT GetEncryptedDigest(__in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData) throw();
    HRESULT GetEncryptedDigest(__in DWORD dwIndex,
	__out CStringBlob& Result); //throw(...);

    // CMSG_CMS_SIGNER_INFO_PARAM
    HRESULT GetCmsSignerInfo(__in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData,
	__out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo) throw();
    HRESULT GetCmsSignerInfo(__in DWORD dwIndex, __out CStringBlob& Result,
	__out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo); //throw(...);

#ifndef _WIN32_WCE
// В wincrypt.h для CE CryptDe(En)codeObject - это макрос, заменяющий вызов
// на CryptDe(En)codeObjectEx, поэтому под CE не вводим дополнительные функции
// CryptDe(En)codeObject. При желании можно сделать и для CE, но пока не
// было необходимости.

    // CMSG_ENCODED_SIGNER + Decode = CMSG_CMS_SIGNER_INFO_PARAM
    HRESULT GetAndDecodeCmsSignerInfo(__in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData,
	__out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo,
	__in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
	__in DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); //throw(...);
    HRESULT GetAndDecodeCmsSignerInfo(__in DWORD dwIndex,
	__out CStringBlob& Result,
	__out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo,
	__in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
	__in DWORD dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); //throw(...);

#endif // !_WIN32_WCE

    // CMSG_SIGNER_AUTH_ATTR_PARAM
    HRESULT GetAuthAttr(__in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData, __out PCCRYPT_ATTRIBUTES& pcAttrs) throw();
    HRESULT GetAuthAttr( __in DWORD dwIndex, __out CStringBlob& Result,
	__out PCCRYPT_ATTRIBUTES& pcAttrs); //throw(...);

    // CMSG_SIGNER_UNAUTH_ATTR_PARAM
    HRESULT GetUnauthAttr(__in DWORD dwIndex,
	__out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
	__inout DWORD *pcbData, __out PCCRYPT_ATTRIBUTES& pcAttrs) throw();
    HRESULT GetUnauthAttr( __in DWORD dwIndex, __out CStringBlob& Result,
	__out PCCRYPT_ATTRIBUTES& pcAttrs); //throw(...);

    // CMSG_SIGNER_COUNT_PARAM
    HRESULT GetSignerCount(__out DWORD &dwCount) throw();

    // CMSG_TYPE_PARAM
    HRESULT GetType(__out DWORD &dwMsgType) throw();

    HCRYPTMSG GetHandle() const throw();
};

#ifndef _WIN32_WCE
// В wincrypt.h для CE CryptDe(En)codeObject - это макрос, заменяющий вызов
// на CryptDe(En)codeObjectEx, поэтому под CE не вводим дополнительные функции
// CryptDe(En)codeObject. При желании можно сделать и для CE, но пока не
// было необходимости.

HRESULT CryptDecodeObject(__in LPCSTR lpszStructType,
    __in_bcount(cbEncoded) const BYTE *pbEncoded, __in DWORD cbEncoded,
    __out CStringBlob& Result,
    __in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptDecodeObject(__in LPCSTR lpszStructType,
    __in const CStringBlob& Data,
    __out_bcount_part_opt(*pcbStructInfo, *pcbStructInfo) void *pvStructInfo,
    __inout DWORD *pcbStructInfo,
    __in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptDecodeObject(__in LPCSTR lpszStructType,
    __in const CStringBlob& Data, __out CStringBlob& Result,
    __in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptDecodeCmsSignerInfo(
    __in_bcount(cbEncoded) const BYTE *pbEncoded, __in DWORD cbEncoded,
    __out CStringBlob& Result, __out PCCMSG_CMS_SIGNER_INFO& pSignerInfo,
    __in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);

HRESULT CryptDecodeCmsSignerInfo(__in const CStringBlob& Data,
    __out_bcount_part_opt(*pcbStructInfo, *pcbStructInfo) void *pvStructInfo,
    __inout DWORD *pcbStructInfo,
    __out PCCMSG_CMS_SIGNER_INFO& pSignerInfo,
    __in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptDecodeCmsSignerInfo(__in const CStringBlob& Data,
    __out CStringBlob& Result, __out PCCMSG_CMS_SIGNER_INFO& pSignerInfo,
    __in DWORD dwFlags = CRYPT_DECODE_SHARE_OID_STRING_FLAG,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptEncodeObject(__in LPCSTR lpszStructType,
    __in const void* pvStructInfo, __out CStringBlob& Result,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptEncodeAttribute(__in PCCRYPT_ATTRIBUTE pcAttr,
    __out CStringBlob& Result,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
    //throw(...);

HRESULT CryptBinaryToStringW(__in const BYTE *pbBinary,
    __in DWORD cbBinary, __in DWORD dwFlags, __out ATL::CAtlStringW& result);
    //throw(...) 

HRESULT CryptBinaryToStringW(__in CStringBlob& binary,
    __in DWORD dwFlags, __out ATL::CAtlStringW& result);
    //throw(...) 

HRESULT CryptStringToBinaryW(
    __in LPCWSTR pszString,
    __in DWORD cchString,
    __in DWORD dwFlags, __out CStringBlob& result);
    //throw(...) 

HRESULT CryptStringToBinaryW(__in const ATL::CAtlStringW& str,
    __in DWORD dwFlags, __out CStringBlob& result);
    //throw(...) 

HRESULT CryptBinaryToStringA(__in const BYTE *pbBinary,
    __in DWORD cbBinary, __in DWORD dwFlags, __out CStringBlob& result);
    //throw(...) 

HRESULT CryptBinaryToStringA(__in CStringBlob& binary,
    __in DWORD dwFlags, __out CStringBlob& result);
    //throw(...) 

HRESULT CryptStringToBinaryA(
    __in LPCSTR pszString,
    __in DWORD cchString,
    __in DWORD dwFlags,
    __out CStringBlob& result);
    //throw(...) 

HRESULT CryptStringToBinaryA(__in const CStringBlob& str,
    __in DWORD dwFlags, __out CStringBlob& result);
    //throw(...) 

HRESULT CertStrToNameW(__in const ATL::CAtlStringW& str,
    __in DWORD dwStrType, __out CStringBlob& result,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);

HRESULT CertStrToNameA(__in const CStringBlob& str,
    __in DWORD dwStrType, __out CStringBlob& result,
    __in DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);

#endif // !_WIN32_WCE

#ifdef _WIN32

class CCryptOIDFuncAddr
{
protected:
    HCRYPTOIDFUNCADDR m_hCryptOIDFuncAddr;
    void *m_pfn;

public:
    CCryptOIDFuncAddr() throw();
    ~CCryptOIDFuncAddr() throw();

    HRESULT Initialize(__in_z LPCSTR pszFuncName, __in DWORD dwEncodingType,
	__in_z LPCSTR pszOID, __in DWORD dwFlags = 0) throw();
    void Destroy() throw();

private:
    // Запрещаем копирование
    CCryptOIDFuncAddr(const CCryptOIDFuncAddr& addr);
    CCryptOIDFuncAddr &operator=(const CCryptOIDFuncAddr& addr);
};

class COIDFuncEncodePublicKeyAndParameters: public CCryptOIDFuncAddr
{
    // Функция CryptEncodePublicKeyAndParameters не документирована и не
    // экспортируется из crypt32.dll. Тем не менее, эта OID-функция
    // зарегистрирована в реестре для типов ключей КриптоПро CSP, и без
    // её реализации представить себе CSP с собственными алгоритмами ключей
    // невозможно. Прототип взят из исходников КриптоПро CSP.
    // Функция сама выделяет память под возвращаемые буферы с помощью
    // LocalAlloc() (по крайней мере, должна с помощью LocalAlloc(), а на
    // самом деле в КриптоПро CSP не так, но освобождать надо с помощью
    // LocalFree(), так же как и в CryptoAPI).

    typedef BOOL WINAPI CryptEncodePublicKeyAndParameters_t(
	__in DWORD dwCertEncodingType,
	__in_z LPCSTR lpszStructType,
	__in_bcount(cbStructInfo) const void *pvStructInfo,
	__in DWORD cbStructInfo,
	__in DWORD dwFlags,
	__in_opt void *pvAuxInfo,
	__deref_out_bcount_full_opt(*pcbEncodedPublicKey)
	    LPBYTE *ppbEncodedPublicKey,
	__deref_out DWORD *pcbEncodedPublicKey,
	__deref_out_bcount_full_opt(*pcbParameters)
	    LPBYTE *ppbParameters,
	__deref_out DWORD *pcbParameters);

    static const DWORD m_dwCertEncodingType
	= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;

protected:
    LPCSTR m_pszOID;

public:
    COIDFuncEncodePublicKeyAndParameters() throw();

    HRESULT Initialize(__in_z LPCSTR pszOID, __in DWORD dwFlags = 0) throw();

    CryptEncodePublicKeyAndParameters_t Invoke;
    BOOL Invoke(
	__in_bcount(cbStructInfo) const void *pvStructInfo,
	__in DWORD cbStructInfo,
	__out CStringBlob& EncodedPublicKey,
	__out CStringBlob& EncodedParameters,
	__in DWORD dwFlags = 0,
	__in_opt void *pvAuxInfo = NULL); //throw(...);

private:
    // Запрещаем копирование
    COIDFuncEncodePublicKeyAndParameters(
	const COIDFuncEncodePublicKeyAndParameters& addr);
    COIDFuncEncodePublicKeyAndParameters &operator=(
	const COIDFuncEncodePublicKeyAndParameters& addr);
};

#endif //_WIN32

} // namespace ATL2

#include "atlcrypt2.inl"

#ifndef _ATL2_NO_AUTOMATIC_NAMESPACE
using namespace ATL2;
#endif //!_ATL2_NO_AUTOMATIC_NAMESPACE

#endif // _ATLCRYPT2_H_INCLUDED
