#include "stdafx.h"

#include <windows.h>
#include <wincrypt.h>
#include <unicode.h>
#define SECURITY_WIN32
#include <security.h>
#include <aclapi.h>
#include <pvk.h>
#include <wintrust.h>
#include <xasn.h>
#include <autoenr.h>
#include <sddl.h>

#include "xenroll.h"
#include "cenroll.h"
#include "xelib.h"
#include "sfscript.h"

#define NO_OSS_DEBUG
#include <dbgdef.h>

#include <string.h>

#include <assert.h>

static LPVOID (* MyCoTaskMemAlloc)(ULONG) = NULL;
static LPVOID (* MyCoTaskMemRealloc)(LPVOID, ULONG) = NULL;
static void (* MyCoTaskMemFree)(LPVOID) = NULL;

#define MY_HRESULT_FROM_WIN32(a) ((a >= 0x80000000) ? a : HRESULT_FROM_WIN32(a))

#ifndef NTE_TOKEN_KEYSET_STORAGE_FULL
#define NTE_TOKEN_KEYSET_STORAGE_FULL _HRESULT_TYPEDEF_(0x80090023L)
#endif

#define CEnrollLocalScope(ScopeName) struct ScopeName##TheLocalScope { public
#define CEnrollEndLocalScope } local

#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))

#pragma warning(disable:4213) 

static LPSTR MBFromWide(LPCWSTR wsz) {

    LPSTR   sz = NULL;
    DWORD   cb = 0;

    assert(wsz != NULL);
    if(wsz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    if( (cb = WideCharToMultiByte(0, 0, wsz, -1, NULL, 0, NULL, NULL)) == 0   ||
        (sz = (char *) MyCoTaskMemAlloc(cb)) == NULL  ||
        (cb = WideCharToMultiByte(0, 0, wsz, -1, sz, cb, NULL, NULL)) == 0 ) {

        if(GetLastError() == ERROR_SUCCESS)
            SetLastError(ERROR_OUTOFMEMORY);

        return(NULL);
    }

    return(sz);
}

static LPWSTR WideFromMB(LPCSTR sz) {

    DWORD   cch     = 0;
    LPWSTR  wsz     = NULL;

    assert(sz != NULL);
    if(sz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    if( (cch = MultiByteToWideChar(0, 0, sz, -1, NULL, 0)) == 0   ||
        (wsz = (WCHAR *) MyCoTaskMemAlloc(cch * sizeof(WCHAR))) == NULL  ||
        (cch = MultiByteToWideChar(0, 0, sz, -1, wsz, cch)) == 0) {

        if(GetLastError() == ERROR_SUCCESS)
            SetLastError(ERROR_OUTOFMEMORY);

        return(NULL);
    }

    return(wsz);
}

static BSTR
BSTRFromMB(LPCSTR sz)
{
    BSTR    bstr    = NULL;
    DWORD   cch     = 0;
    WCHAR  *pwsz     = NULL;
    BOOL    fFail = FALSE;

    assert(sz != NULL);
    if(sz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    while (TRUE)
    {
        if(0 == (cch = MultiByteToWideChar(0, 0, sz, -1, pwsz, cch)))
        {

            fFail = TRUE;
            break;
        }
        if (NULL != pwsz)
        {

            break;
        }
        pwsz = (WCHAR *)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
        if (NULL == pwsz)
        {

            if(GetLastError() == ERROR_SUCCESS)
                SetLastError(ERROR_OUTOFMEMORY);
            break;
        }
    }

    if (!fFail && NULL != pwsz)
    {
        bstr = SysAllocString(pwsz);
        if (NULL == bstr)
        {
            if(GetLastError() == ERROR_SUCCESS)
                SetLastError(ERROR_OUTOFMEMORY);
        }
    }

    if (NULL != pwsz)
    {
        LocalFree(pwsz);
    }
    return(bstr);
}

static LPWSTR CopyWideString(LPCWSTR wsz) {

    size_t  cch     = 0;
    LPWSTR  wszOut  = NULL;

    assert(wsz != NULL);
    if(wsz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    cch = wcslen(wsz) + 1;
    if (cch*sizeof(WCHAR) > (ULONG)-1) { 

        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    if( (wszOut = (LPWSTR) MyCoTaskMemAlloc((ULONG)(sizeof(WCHAR) * cch))) == NULL ) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(NULL);
    }

    wcscpy(wszOut, wsz);

    return(wszOut);
}

static LPSTR CopyAsciiString(LPCSTR sz) {

    size_t  cch     = 0;
    LPSTR   szOut   = NULL;

    assert(sz != NULL);
    if(sz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    cch = strlen(sz) + 1;
    if (cch > (ULONG)-1) { 

        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    if( (szOut = (LPSTR) MyCoTaskMemAlloc((ULONG)cch)) == NULL ) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(NULL);
    }

    strcpy(szOut, sz);

    return(szOut);
}

static DWORD KeyLocationFromStoreLocation(DWORD dwStoreFlags) {

    if(
        ((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_CURRENT_USER) ||
        ((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_USERS) ||
        ((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)
      ) {
        return(0);
    }

    return(CRYPT_MACHINE_KEYSET);
}

HRESULT
xeLoadRCString(
    IN HINSTANCE   hInstance,
    IN int         iRCId,
    OUT WCHAR    **ppwsz)
{
#define REALLOCATEBLOCK 512
    HRESULT   hr;
    WCHAR    *pwszTemp = NULL;
    int       sizeTemp;
    int       size = 0;
    int       cBlocks = 1;

    *ppwsz = NULL;

    while (NULL == pwszTemp)
    {
        sizeTemp = cBlocks * REALLOCATEBLOCK;
        pwszTemp = (WCHAR*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
                        sizeTemp * sizeof(WCHAR));
        if (NULL == pwszTemp)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }

        size = LoadStringU(
                   hInstance,
                   iRCId,
                   pwszTemp,
                   sizeTemp);
        if (0 == size)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto LoadStringError;
        }

        if (size < sizeTemp - 1)
        {

            break;
        }
        ++cBlocks;
        LocalFree(pwszTemp);
        pwszTemp = NULL;
    }

    *ppwsz = (WCHAR*) LocalAlloc(LPTR, (size+1) * sizeof(WCHAR));
    if (NULL == *ppwsz)
    {
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    }

    wcscpy(*ppwsz, pwszTemp);

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszTemp)
    {
        LocalFree(pwszTemp);
    }
    return hr;

TRACE_ERROR(LocalAllocError)
TRACE_ERROR(LoadStringError)
}

HANDLE CCEnroll::CreateOpenFileSafely2(
    LPCWSTR pwszFileName,
    DWORD   idsCreate,
    DWORD   idsOverwrite)
{
    HANDLE      hFile = NULL;
    WCHAR      *pwszMsg = NULL;
    WCHAR      *pwszFormat = NULL;
    WCHAR      *pwszTitle = NULL;
    WCHAR      *pwszSafety = NULL;
    DWORD       dwAttribs = 0;
    BOOL        fNotProperFile;

    LPCWSTR     apwszInsertArray[2];
    BOOL        fNo;
    BOOL        fMsgBox;
    int         idPrefix = IDS_NOTSAFE_WRITE_PREFIX; 
    BOOL        fCreate = (0xFFFFFFFF != idsCreate) &&
                          (0xFFFFFFFF != idsOverwrite);
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);

    fMsgBox = (m_dwEnabledSafteyOptions != 0);
    dwAttribs = GetFileAttributesU(pwszFileName);

    if(0xFFFFFFFF == dwAttribs)
    {

        if (!fCreate)
        {

            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }

        if (fMsgBox)
        {
            hr = xeLoadRCString(hInstanceXEnroll, idsCreate, &pwszFormat);
            if (S_OK != hr)
            {
                goto xeLoadRCStringError;
            }
        }
    }
    else
    {

        fNotProperFile = 
              (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
              (dwAttribs & FILE_ATTRIBUTE_HIDDEN)    ||
              (dwAttribs & FILE_ATTRIBUTE_SYSTEM);

        if (!fNotProperFile)
        {

            if (fCreate)
            {

                if (0x0 != (dwAttribs & FILE_ATTRIBUTE_READONLY))
                {

                    fNotProperFile = TRUE;
                }
                else
                {

                    hr = xeLoadRCString(hInstanceXEnroll, idsOverwrite, &pwszFormat);
                    if (S_OK != hr)
                    {
                        goto xeLoadRCStringError;
                    }

                    fMsgBox = TRUE;
                }
            }
            else
            {

		fMsgBox = TRUE;
		hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_OPEN, &pwszFormat);
		if (S_OK != hr)
                {
		    goto xeLoadRCStringError;
		}
		idPrefix = IDS_NOTSAFE_OPEN_PREFIX;
            }
        }

        if (fNotProperFile)
        {

            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
    }

    if (fMsgBox)
    {
        hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }
        hr = xeLoadRCString(hInstanceXEnroll, idPrefix, &pwszSafety);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }

        apwszInsertArray[0] = pwszSafety;
        apwszInsertArray[1] = pwszFileName;

        if (!FormatMessageU(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_STRING |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pwszFormat,
            0,
            0,
            (LPWSTR) &pwszMsg,
            0,
            (va_list *)apwszInsertArray)) 
	{ 
	    goto FormatMessageError; 
	}

        fNo = (MessageBoxU(
                    NULL,
                    pwszMsg,
                    pwszTitle,
                    MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES);

        if(fNo)
        {
            SetLastError(ERROR_CANCELLED);
            goto CancelError;
        }
    }

    hFile = CreateFileU(
            pwszFileName,
            fCreate ? GENERIC_WRITE : GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

    if (hFile == INVALID_HANDLE_VALUE  ||  hFile == NULL)
    {

        SetLastError(ERROR_ACCESS_DENIED);
        hFile = NULL;
        goto CreateFileUError;
    }

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);

    if(NULL != pwszMsg)
    {
        LocalFree(pwszMsg);
    }
    if(NULL != pwszFormat)
    {
        LocalFree(pwszFormat);
    }
    if(NULL != pwszTitle)
    {
        LocalFree(pwszTitle);
    }
    if(NULL != pwszSafety)
    {
        LocalFree(pwszSafety);
    }
    return(hFile);

TRACE_ERROR(CreateFileUError)
TRACE_ERROR(FormatMessageError);
TRACE_ERROR(CancelError)
TRACE_ERROR(InvalidFileError)
TRACE_ERROR(xeLoadRCStringError)
}

HANDLE CCEnroll::CreateOpenFileSafely(
    LPCWSTR pwszFileName,
    BOOL    fCreate)
{
    HANDLE      hFile = NULL;
    WCHAR      *pwszMsg = NULL;
    DWORD       dwAttribs = 0;
    BOOL        fNotProperFile;

    WCHAR      *pwszFormat = NULL;
    WCHAR      *pwszTitle = NULL;
    LPCWSTR     apwszInsertArray[] = {pwszFileName};
    BOOL        fNo;
    BOOL        fMsgBox = 0 != m_dwEnabledSafteyOptions;
    BOOL        fOverWrite = FALSE;
    HRESULT     hr;

    EnterCriticalSection(&m_csXEnroll);

    dwAttribs = GetFileAttributesU(pwszFileName);
    if(0xFFFFFFFF == dwAttribs)
    {

        if (!fCreate)
        {

            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
    }
    else
    {

        fNotProperFile = 
              (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
              (dwAttribs & FILE_ATTRIBUTE_HIDDEN)    ||
              (dwAttribs & FILE_ATTRIBUTE_SYSTEM);

        if (!fNotProperFile)
        {

            if (fCreate)
            {

                if (0x0 != (dwAttribs & FILE_ATTRIBUTE_READONLY))
                {

                    fNotProperFile = TRUE;
                }
                else
                {

                    fOverWrite = TRUE;
                }
            }
        }

        if (fNotProperFile)
        {

            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
    }

    if (fMsgBox)
    {
        hr = xeLoadRCString(hInstanceXEnroll, IDS_CERTENROLL, &pwszTitle);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }
        hr = xeLoadRCString(
                hInstanceXEnroll,
                fCreate ? IDS_NOTSAFE_WRITE_FORMAT : IDS_NOTSAFE_OPEN_FORMAT,
                &pwszFormat);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }

        if (!FormatMessageU(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_STRING |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pwszFormat,
            0,
            0,
            (LPWSTR) &pwszMsg,
            0,
            (va_list *)apwszInsertArray))
	{
	    goto FormatMessageUError;
	}

        fNo = (MessageBoxU(
                    NULL,
                    pwszMsg,
                    pwszTitle,
                    MB_DEFBUTTON2 | MB_YESNO | MB_ICONWARNING) == IDNO);
        if(fNo)
        {
            SetLastError(ERROR_CANCELLED);
            goto CancelError;
        }
    }

    if (fCreate && fOverWrite)
    {
        if (!fMsgBox)
        {
            hr = xeLoadRCString(hInstanceXEnroll, IDS_CERTENROLL, &pwszTitle);
            if (S_OK != hr)
            {
                goto xeLoadRCStringError;
            }
        }

        hr = xeLoadRCString(hInstanceXEnroll, IDS_OVERWRITE_FORMAT, &pwszFormat);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }

        if (NULL != pwszMsg)
        {
            LocalFree(pwszMsg);
            pwszMsg = NULL;
        }
        FormatMessageU(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_STRING |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pwszFormat,
            0,
            0,
            (LPWSTR) &pwszMsg,
            0,
            (va_list *)apwszInsertArray);

        fNo = (MessageBoxU(
                    NULL,
                    pwszMsg,
                    pwszTitle,
                    MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES);
        if(fNo)
        {
            SetLastError(ERROR_CANCELLED);
            goto CancelError;
        }
    }

    hFile = CreateFileU(
            pwszFileName,
            fCreate ? GENERIC_WRITE : GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

    if (hFile == INVALID_HANDLE_VALUE  ||  hFile == NULL)
    {

        SetLastError(ERROR_ACCESS_DENIED);
        hFile = NULL;
        goto CreateFileUError;
    }

ErrorReturn:
    if(NULL != pwszMsg)
    {
        LocalFree(pwszMsg);
    }
    if(NULL != pwszTitle)
    {
        LocalFree(pwszTitle);
    }
    if(NULL != pwszFormat)
    {
        LocalFree(pwszFormat);
    }
    LeaveCriticalSection(&m_csXEnroll);

    return(hFile);

TRACE_ERROR(CreateFileUError)
TRACE_ERROR(CancelError)
TRACE_ERROR(FormatMessageUError);
TRACE_ERROR(InvalidFileError)
TRACE_ERROR(xeLoadRCStringError)
}

HANDLE CCEnroll::CreateFileSafely(
    LPCWSTR pwszFileName)
{
    return CreateOpenFileSafely(pwszFileName, TRUE); 
}

HANDLE CCEnroll::OpenFileSafely(
    LPCWSTR pwszFileName)
{
    return CreateOpenFileSafely(pwszFileName, FALSE); 
}

void DwordToWide(DWORD dw, LPWSTR lpwstr) {

    DWORD   i = 0;
    DWORD   j;
    WCHAR   wch;

    while(dw > 0) {
        j = dw % 10;
        dw /= 10;
        lpwstr[i++] = (WCHAR) (j + L'\0');
    }

    if( i == 0 )
        lpwstr[i++] = L'\0';

    lpwstr[i] = 0;

    for(j=0, i--; i > j; i--, j++) {
        wch = lpwstr[i];
        lpwstr[i] = lpwstr[j];
        lpwstr[j] = wch;
    }
}

HRESULT
xeEncodeNameValuePair(
    IN PCRYPT_ENROLLMENT_NAME_VALUE_PAIR pNameValuePair,
    OUT BYTE                           **ppbData,
    OUT DWORD                           *pcbData)
{
    HRESULT hr = S_OK;

    *ppbData = NULL;
    *pcbData = 0;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                szOID_ENROLLMENT_NAME_VALUE_PAIR,
                pNameValuePair,
                *ppbData,
                pcbData))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto error;
        }

        if (NULL != *ppbData)
        {
            break;
        }

        *ppbData = (BYTE*)MyCoTaskMemAlloc(*pcbData);
        if (NULL == *ppbData)
        {
            hr = E_OUTOFMEMORY;
            goto error;
        }
    }

error:
    if (S_OK != hr && NULL != *ppbData)
    {
        MyCoTaskMemFree(*ppbData);
	*ppbData = NULL; 
    }
    return hr;
}

HRESULT
xeWSZToSZ(
    IN LPCWSTR    pwsz,
    OUT LPSTR    *ppsz)
{
    HRESULT hr = S_OK;
    LONG    cc = 0;

    *ppsz = NULL;

    while (TRUE)
    {
        cc = WideCharToMultiByte(
                    GetACP(),
                    0,
                    pwsz,
                    -1,
                    *ppsz,
                    cc,
                    NULL,
                    NULL);
        if (0 >= cc)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto error;
        }

        if (NULL != *ppsz)
        {
            break;
        }
        *ppsz= (CHAR*)MyCoTaskMemAlloc(cc);
        if (NULL == *ppsz)
        {
            hr = E_OUTOFMEMORY;
            goto error;
        }
    }
error:
    if (S_OK != hr && NULL != *ppsz)
    {
        MyCoTaskMemFree(*ppsz);
	*ppsz = NULL; 
    }
    return hr;
}

HRESULT
CCEnroll::xeStringToBinaryFromFile(
    IN  WCHAR const *pwszfn,
    OUT BYTE       **ppbOut,
    OUT DWORD       *pcbOut,
    IN  DWORD        Flags)
{
    HANDLE hFile;
    HRESULT hr;
    CHAR *pchFile = NULL;
    BYTE *pbOut = NULL;
    DWORD cchFile;
    DWORD cbRead;
    DWORD cbOut = 0;

    hFile = OpenFileSafely(pwszfn);
    if (NULL == hFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto OpenFileSafelyError;
    }

    cchFile = GetFileSize(hFile, NULL);
    if ((DWORD) -1 == cchFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetFileSizeError;
    }

    pchFile = (CHAR *) LocalAlloc(LMEM_FIXED, cchFile);
    if (NULL == pchFile)
    {
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    }

    if (!ReadFile(hFile, pchFile, cchFile, &cbRead, NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto ReadFileError;
    }

    assert(cbRead <= cchFile);
    if (cbRead != cchFile)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        goto ReadFileError;
    }

    if (CRYPT_STRING_BINARY == Flags)
    {
        pbOut = (BYTE *) pchFile;
        cbOut = cchFile;
        pchFile = NULL;
    }
    else
    {

        while (TRUE)
        {
            if (!MyCryptStringToBinaryA(
                        pchFile,
                        cchFile,
                        Flags,
                        pbOut,
                        &cbOut,
                        NULL,
                        NULL))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto CyrptStringToBinaryError;
            }
            if (NULL != pbOut)
            {

                break;
            }
            pbOut = (BYTE*)LocalAlloc(LMEM_FIXED, cbOut);
            if (NULL == pbOut)
            {
                hr = E_OUTOFMEMORY;
                goto LocalAllocError;
            }
        }
    }
    *pcbOut = cbOut;
    *ppbOut = pbOut;
    pbOut = NULL;

    hr = S_OK;
error:
    if (INVALID_HANDLE_VALUE != hFile)
    {
        CloseHandle(hFile);
    }
    if (NULL != pchFile)
    {
        LocalFree(pchFile);
    }
    if (NULL != pbOut)
    {
        LocalFree(pbOut);
    }
    return(hr);

ErrorReturn:
    goto error;

TRACE_ERROR(CyrptStringToBinaryError)
TRACE_ERROR(ReadFileError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(GetFileSizeError)
TRACE_ERROR(OpenFileSafelyError)
}

typedef VOID
(WINAPI * PFNCertFreeCertificateChain)
   (IN PCCERT_CHAIN_CONTEXT pChainContext);

typedef BOOL
(WINAPI * PFNCertGetCertificateChain)
   (IN OPTIONAL HCERTCHAINENGINE hChainEngine,
    IN PCCERT_CONTEXT pCertContext,
    IN OPTIONAL LPFILETIME pTime,
    IN OPTIONAL HCERTSTORE hAdditionalStore,
    IN PCERT_CHAIN_PARA pChainPara,
    IN DWORD dwFlags,
    IN LPVOID pvReserved,
    OUT PCCERT_CHAIN_CONTEXT* ppChainContext);

typedef BOOL (WINAPI *PFNCertVerifyCertificateChainPolicy) (
  LPCSTR pszPolicyOID,
  PCCERT_CHAIN_CONTEXT pChainContext,
  PCERT_CHAIN_POLICY_PARA pPolicyPara,
  PCERT_CHAIN_POLICY_STATUS pPolicyStatus
);

typedef BOOL (*PFNCheckTokenMembership) (
  HANDLE TokenHandle,  
  PSID SidToCheck,     
  PBOOL IsMember       
);

typedef BOOL (*PFNSetSecurityDescriptorControl) (
  PSECURITY_DESCRIPTOR pSecurityDescriptor,          
  SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, 
  SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet       
);

VOID
MyCertFreeCertificateChain (
    IN PCCERT_CHAIN_CONTEXT pChainContext
    )
{
    PFNCertFreeCertificateChain pfnCertFreeCertificateChain = NULL;
    HMODULE  hModule = NULL;

    hModule = GetModuleHandle("crypt32.dll");
    if (NULL != hModule)
    {
        pfnCertFreeCertificateChain = (PFNCertFreeCertificateChain)
                GetProcAddress(hModule,
                               "CertFreeCertificateChain");
        if (NULL != pfnCertFreeCertificateChain)
        {
            pfnCertFreeCertificateChain(pChainContext);
        }
    }
}

BOOL
MyCertGetCertificateChain (
    IN OPTIONAL HCERTCHAINENGINE hChainEngine,
    IN PCCERT_CONTEXT pCertContext,
    IN OPTIONAL LPFILETIME pTime,
    IN OPTIONAL HCERTSTORE hAdditionalStore,
    IN PCERT_CHAIN_PARA pChainPara,
    IN DWORD dwFlags,
    IN LPVOID pvReserved,
    OUT PCCERT_CHAIN_CONTEXT* ppChainContext
    )
{
    PFNCertGetCertificateChain pfnCertGetCertificateChain = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("crypt32.dll");
    if (NULL != hModule)
    {
        pfnCertGetCertificateChain = (PFNCertGetCertificateChain)
                GetProcAddress(hModule,
                               "CertGetCertificateChain");
        if (NULL != pfnCertGetCertificateChain)
        {
            return pfnCertGetCertificateChain(
                hChainEngine,
                pCertContext,
                pTime,
                hAdditionalStore,
                pChainPara,
                dwFlags,
                pvReserved,
                ppChainContext);
        }
    }
    return FALSE;
}

BOOL
MyCertVerifyCertificateChainPolicy(
  LPCSTR pszPolicyOID,
  PCCERT_CHAIN_CONTEXT pChainContext,
  PCERT_CHAIN_POLICY_PARA pPolicyPara,
  PCERT_CHAIN_POLICY_STATUS pPolicyStatus
)
{
    PFNCertVerifyCertificateChainPolicy pfnCertVerifyCertificateChainPolicy = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("crypt32.dll");
    if (NULL != hModule)
    {
        pfnCertVerifyCertificateChainPolicy = (PFNCertVerifyCertificateChainPolicy)
                GetProcAddress(hModule,
                               "CertVerifyCertificateChainPolicy");
        if (NULL != pfnCertVerifyCertificateChainPolicy)
        {
            return pfnCertVerifyCertificateChainPolicy(
                            pszPolicyOID,
                            pChainContext,
                            pPolicyPara,
                            pPolicyStatus);
        }
    }
    return FALSE;
}

BOOL
MyCheckTokenMembership(
  HANDLE TokenHandle,  
  PSID SidToCheck,     
  PBOOL IsMember       
)
{
    PFNCheckTokenMembership pfnCheckTokenMembership = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("advapi32.dll");
    if (NULL != hModule)
    {
        pfnCheckTokenMembership = (PFNCheckTokenMembership)
                GetProcAddress(hModule,
                               "CheckTokenMembership");
        if (NULL != pfnCheckTokenMembership)
        {
            return pfnCheckTokenMembership(
                        TokenHandle,
                        SidToCheck,
                        IsMember);
        }
    }
    return FALSE;
}

BOOL
MySetSecurityDescriptorControl(
  PSECURITY_DESCRIPTOR pSecurityDescriptor,          
  SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, 
  SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet       
)
{
    PFNSetSecurityDescriptorControl pfnSetSecurityDescriptorControl = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("advapi32.dll");
    if (NULL != hModule)
    {
        pfnSetSecurityDescriptorControl = (PFNSetSecurityDescriptorControl)
                GetProcAddress(hModule,
                               "SetSecurityDescriptorControl");
        if (NULL != pfnSetSecurityDescriptorControl)
        {
            return pfnSetSecurityDescriptorControl(
                        pSecurityDescriptor,
                        ControlBitsOfInterest,
                        ControlBitsToSet);
        }
    }
    return FALSE;
}

HRESULT __stdcall CCEnroll::GetInterfaceSafetyOptions( 
             REFIID riid,
             DWORD __RPC_FAR *pdwSupportedOptions,
             DWORD __RPC_FAR *pdwEnabledOptions) {

    RPC_STATUS rpcStatus;          

    if(0 != UuidCompare((GUID *) &riid, (GUID *) &IID_IDispatch, &rpcStatus) )
        return(E_NOINTERFACE);

    *pdwEnabledOptions   = m_dwEnabledSafteyOptions;
    *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;

    return(S_OK);
}

HRESULT __stdcall CCEnroll::SetInterfaceSafetyOptions( 
             REFIID riid,
             DWORD dwOptionSetMask,
             DWORD dwEnabledOptions) {

    RPC_STATUS rpcStatus;          
    DWORD dwSupport = 0;            

    if(0 != UuidCompare((GUID *) &riid, (GUID *) &IID_IDispatch, &rpcStatus) )
        return(E_NOINTERFACE);

    dwSupport = dwOptionSetMask & ~(INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA);        
    if(dwSupport != 0)
        return(E_FAIL);

    (DWORD)m_dwEnabledSafteyOptions &= ~dwOptionSetMask;
    (DWORD)m_dwEnabledSafteyOptions |= dwEnabledOptions; 

return(S_OK);
}

HRESULT
CCEnroll::GetVerifyProv()
{
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == m_hVerifyProv)
    {
        if (!CryptAcquireContextU(
                    &m_hVerifyProv,
                    NULL,
                    m_keyProvInfo.pwszProvName,
                    m_keyProvInfo.dwProvType,
                    CRYPT_VERIFYCONTEXT))
        {
#if DBG
            assert(NULL == m_hVerifyProv);
#endif 
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptAcquireContextUError;
        }
    }

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(CryptAcquireContextUError)
}

BOOL CCEnroll::GetCapiHashAndSigAlgId(ALG_ID rgAlg[2]) {

    DWORD   iHashBest       = 0;
    ALG_ID  arDefaultHash[] = {m_HashAlgId, CALG_SHA1, CALG_MD5};
    DWORD   cDefaultHash    = sizeof(arDefaultHash) / sizeof(DWORD);

    HCRYPTPROV  hProvU      = NULL;

    DWORD       dwFlags     = CRYPT_FIRST;

    DWORD       i;
    PROV_ENUMALGS               enumAlgs;
    DWORD       cb          = sizeof(enumAlgs);

    BOOL        fRet        = TRUE;

    rgAlg[0] = 0;
    rgAlg[1] = 0;

    EnterCriticalSection(&m_csXEnroll);

    if(m_hProv == NULL)
    {
        HRESULT hr;
        hr = GetVerifyProv();
        if (S_OK != hr)
        {
            goto GetVerifyProvError;
        }
        hProvU = m_hVerifyProv;
    }
    else
    {

        hProvU = m_hProv;
    }

    cb = sizeof(enumAlgs);
    while( CryptGetProvParam(
        hProvU,
            PP_ENUMALGS,
        (BYTE *) &enumAlgs,
        &cb,
        dwFlags
        ) ) {

        cb = sizeof(enumAlgs);

        dwFlags = 0;

        if( ALG_CLASS_HASH == GET_ALG_CLASS(enumAlgs.aiAlgid) ) {

            if(rgAlg[0] == 0) {
                rgAlg[0] = enumAlgs.aiAlgid;
                iHashBest = cDefaultHash;
            }

            for(i=0; i<iHashBest; i++) {

                if(arDefaultHash[i] == enumAlgs.aiAlgid) {
                    rgAlg[0] = enumAlgs.aiAlgid;
                    iHashBest   = i;
                    break;
                }
            }
        }

        else if( ALG_CLASS_SIGNATURE == GET_ALG_CLASS(enumAlgs.aiAlgid) ) {

            if(rgAlg[1] == 0)
                rgAlg[1] = enumAlgs.aiAlgid;
        }
    }

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);

    if(rgAlg[0] == 0)
        rgAlg[0] = CALG_SHA1;

    if(rgAlg[1] == 0)
        rgAlg[1] = CALG_RSA_SIGN;

#if 0
    if(rgAlg[0] == 0 || rgAlg[1] == 0) {
        SetLastError((DWORD)NTE_BAD_ALGID);
        fRet = FALSE;
    }
#endif

    return(fRet);

TRACE_ERROR(GetVerifyProvError)
}

BOOL CreatePvkProperty(
    CRYPT_KEY_PROV_INFO *pKeyProvInfo,
    PCRYPT_DATA_BLOB    pBlob)
{
    WCHAR   wszKeySpec[11];
    WCHAR   wszProvType[11];
    DWORD   cbContainer;
    DWORD   cbKeySpec;
    DWORD   cbProvType;
    DWORD   cbProvName;

    assert(pBlob != NULL);
    assert(pKeyProvInfo != NULL);

    DwordToWide(pKeyProvInfo->dwKeySpec, wszKeySpec);
    DwordToWide(pKeyProvInfo->dwProvType, wszProvType);

    cbContainer = (DWORD)(wcslen(pKeyProvInfo->pwszContainerName) + 1) * sizeof(WCHAR);
    cbKeySpec   = (DWORD)(wcslen(wszKeySpec) + 1) * sizeof(WCHAR);
    cbProvType  = (DWORD)(wcslen(wszProvType) + 1) * sizeof(WCHAR);

    cbProvName  = (DWORD)(wcslen(pKeyProvInfo->pwszProvName) + 1) * sizeof(WCHAR);

    pBlob->cbData =
        cbContainer +
        cbKeySpec   +
        cbProvType  +
        cbProvName  +
        sizeof(WCHAR);

    if( (pBlob->pbData = (BYTE *) MyCoTaskMemAlloc(pBlob->cbData)) == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(FALSE);
    }

    memset(pBlob->pbData, 0, pBlob->cbData);
    memcpy(pBlob->pbData, pKeyProvInfo->pwszContainerName, cbContainer);
    memcpy(&pBlob->pbData[cbContainer], wszKeySpec, cbKeySpec);
    memcpy(&pBlob->pbData[cbContainer + cbKeySpec], wszProvType, cbProvType);
    memcpy(&pBlob->pbData[cbContainer + cbKeySpec + cbProvType], pKeyProvInfo->pwszProvName, cbProvName);

    return(TRUE);
}

static LPWSTR wszEmpty      = L"";
static LPWSTR wszMY         = L"MY";
static LPWSTR wszCA         = L"CA";
static LPWSTR wszROOT       = L"ROOT";
static LPWSTR wszREQUEST    = L"REQUEST";
static LPSTR  szSystemStore = sz_CERT_STORE_PROV_SYSTEM;

CCEnroll::~CCEnroll(void) {
    Destruct();
    DeleteCriticalSection(&m_csXEnroll);
}

void CCEnroll::Destruct(void) {

    if(NULL != m_PrivateKeyArchiveCertificate)
    {
        CertFreeCertificateContext(m_PrivateKeyArchiveCertificate);
    }
    if(NULL != m_pCertContextSigner)
    {
        CertFreeCertificateContext(m_pCertContextSigner);
    }
    if(m_pCertContextRenewal != NULL)
        CertFreeCertificateContext(m_pCertContextRenewal);
    if(m_pCertContextStatic != NULL)
        CertFreeCertificateContext(m_pCertContextStatic);

    if(m_keyProvInfo.pwszContainerName != wszEmpty)
        MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);

    if(m_keyProvInfo.pwszProvName != wszEmpty)
        MyCoTaskMemFree(m_keyProvInfo.pwszProvName);

    if(m_MyStore.wszName != wszMY)
        MyCoTaskMemFree(m_MyStore.wszName);

    if(m_CAStore.wszName != wszCA)
        MyCoTaskMemFree(m_CAStore.wszName);

    if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
        MyCoTaskMemFree(m_RootStore.wszName);

    if(m_RequestStore.wszName != wszREQUEST)
        MyCoTaskMemFree(m_RequestStore.wszName);

    if(m_MyStore.szType  != szSystemStore)
        MyCoTaskMemFree(m_MyStore.szType);

    if(m_CAStore.szType != szSystemStore)
        MyCoTaskMemFree(m_CAStore.szType);

    if(m_RootStore.szType != szSystemStore)
        MyCoTaskMemFree(m_RootStore.szType);

    if(m_RequestStore.szType != szSystemStore)
        MyCoTaskMemFree(m_RequestStore.szType);

    if(m_wszSPCFileName != wszEmpty)
        MyCoTaskMemFree(m_wszSPCFileName);

    if(m_wszPVKFileName != wszEmpty)
        MyCoTaskMemFree(m_wszPVKFileName);

    if (NULL != m_pCertContextPendingRequest)
        CertFreeCertificateContext(m_pCertContextPendingRequest);

    if (NULL != m_pPendingRequestTable)
        delete m_pPendingRequestTable; 

    if(m_RootStore.hStore != NULL)
        CertCloseStore(m_RootStore.hStore, 0);
    m_RootStore.hStore = NULL;

    if(m_CAStore.hStore != NULL)
        CertCloseStore(m_CAStore.hStore, 0);
    m_CAStore.hStore = NULL;

    if(m_MyStore.hStore != NULL)
        CertCloseStore(m_MyStore.hStore, 0);
    m_MyStore.hStore = NULL;

    if(m_RequestStore.hStore != NULL)
        CertCloseStore(m_RequestStore.hStore, 0);
    m_RequestStore.hStore = NULL;

    if(m_hProv != NULL)
        CryptReleaseContext(m_hProv, 0);
    m_hProv = NULL;

    if(m_hVerifyProv != NULL)
        CryptReleaseContext(m_hVerifyProv, 0);
    m_hVerifyProv = NULL;

    if (NULL != m_hCachedKey)
    {

        CryptDestroyKey(m_hCachedKey);
    }

    if (NULL != m_pPublicKeyInfo)
    {
        LocalFree(m_pPublicKeyInfo);
        m_pPublicKeyInfo = NULL;
    }

    FreeAllStackExtension();
    FreeAllStackAttribute();
    resetBlobProperties();
}

static LPVOID CoTaskMemAllocTrap(ULONG cb) {

    __try {
        return(CoTaskMemAlloc(cb));

    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_DLL_NOT_FOUND);
        return(NULL);
    }
}

static LPVOID CoTaskMemReallocTrap(LPVOID ptr, ULONG cb) {
    __try {
        return(CoTaskMemRealloc(ptr, cb));
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_DLL_NOT_FOUND);
        return(NULL);
    }
}

static void CoTaskMemFreeTrap(LPVOID ptr) {
    __try {
        CoTaskMemFree(ptr);
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_DLL_NOT_FOUND);
    }
    return;
}

CCEnroll::CCEnroll(void) : m_dwEnabledSafteyOptions(0) {
    __try
    {
        InitializeCriticalSection(&m_csXEnroll);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    Init();
}

HRESULT
CCEnroll::Init(void)
{
    HRESULT hr;
    GUID    guidContainerName;
    char *  sz = NULL;
    RPC_STATUS  rpc_status;

    if(MyCoTaskMemAlloc == NULL)
    {

        MyCoTaskMemAlloc    = CoTaskMemAllocTrap;
        MyCoTaskMemRealloc  = CoTaskMemReallocTrap;
        MyCoTaskMemFree     = CoTaskMemFreeTrap;
    }

    rpc_status = UuidCreate(&guidContainerName);
    if (RPC_S_OK != rpc_status && RPC_S_UUID_LOCAL_ONLY != rpc_status)
    {
        hr = rpc_status;
        goto UuidCreateError;
    }
    rpc_status = UuidToStringA(&guidContainerName, (unsigned char **) &sz);
    if (RPC_S_OK != rpc_status)
    {
        hr = rpc_status;
        goto UuidToStringAError;
    }
    assert(sz != NULL);
    m_keyProvInfo.pwszContainerName = WideFromMB(sz);
    RpcStringFree((unsigned char **) &sz);

    m_keyProvInfo.pwszProvName        = wszEmpty;
    m_keyProvInfo.dwProvType          = PROV_RSA_FULL;
    m_keyProvInfo.dwFlags             = 0;
    m_keyProvInfo.cProvParam          = 0;
    m_keyProvInfo.rgProvParam         = NULL;
    m_keyProvInfo.dwKeySpec           = AT_SIGNATURE;
    m_fEnableSMIMECapabilities =
                    (m_keyProvInfo.dwKeySpec == AT_KEYEXCHANGE);
    m_fSMIMESetByClient               = FALSE;
    m_fKeySpecSetByClient             = FALSE;
    m_hProv                           = NULL;
    m_hVerifyProv                     = NULL;

    m_fDeleteRequestCert              = TRUE;
    m_fUseExistingKey                 = FALSE;
    m_fWriteCertToCSPModified         = FALSE;
    m_fWriteCertToCSP                 = TRUE;     
    m_fWriteCertToUserDSModified      = FALSE;
    m_fWriteCertToUserDS              = FALSE;
    m_fReuseHardwareKeyIfUnableToGenNew = TRUE;
    m_fLimitExchangeKeyToEncipherment = FALSE;
    m_dwT61DNEncoding                 = 0;        
    m_dwGenKeyFlags                   = 0;
    m_wszSPCFileName                  = wszEmpty;
    m_wszPVKFileName                  = wszEmpty;
    m_HashAlgId                       = 0;

    m_fMyStoreOpenFlagsModified   = FALSE;
    m_MyStore.wszName             = wszMY;
    m_MyStore.szType              = szSystemStore;
    m_MyStore.dwFlags             = CERT_SYSTEM_STORE_CURRENT_USER;
    m_MyStore.hStore              = NULL;

    m_fCAStoreOpenFlagsModified   = FALSE;
    m_CAStore.wszName             = wszCA;
    m_CAStore.szType              = szSystemStore;
    m_CAStore.dwFlags             = CERT_SYSTEM_STORE_CURRENT_USER;
    m_CAStore.hStore              = NULL;

    m_fRootStoreOpenFlagsModified = FALSE;
    m_RootStore.wszName           = wszROOT;
    m_RootStore.szType            = szSystemStore;
    m_RootStore.dwFlags           = CERT_SYSTEM_STORE_CURRENT_USER;
    m_RootStore.hStore            = NULL;

    m_fRequestStoreOpenFlagsModified = FALSE;
    m_RequestStore.wszName        = wszREQUEST ;
    m_RequestStore.szType         = szSystemStore;
    m_RequestStore.dwFlags        = CERT_SYSTEM_STORE_CURRENT_USER;
    m_RequestStore.hStore         = NULL;

    m_PrivateKeyArchiveCertificate= NULL;
    m_pCertContextRenewal         = NULL;
    m_pCertContextSigner         = NULL;
    m_pCertContextStatic          = NULL;
    memset(m_arHashBytesNewCert, 0, sizeof(m_arHashBytesNewCert));
    memset(m_arHashBytesOldCert, 0, sizeof(m_arHashBytesOldCert));
    m_fArchiveOldCert             = FALSE;

    m_pExtStack                 = NULL;
    m_cExtStack                 = 0;

    m_pAttrStack                = NULL;
    m_cAttrStack                = 0;

    m_pExtStackNew              = NULL;
    m_cExtStackNew              = 0;

    m_pAttrStackNew             = NULL;
    m_cAttrStackNew             = 0;

    m_pPropStack                = NULL;
    m_cPropStack                = 0;

    m_fNewRequestMethod         = FALSE;
    m_fCMCFormat                = FALSE;
    m_fHonorRenew               = TRUE; 
    m_fOID_V2                   = FALSE; 
    m_hCachedKey                = NULL;
    m_fUseClientKeyUsage        = FALSE;
    m_lClientId                 = XECI_XENROLL;
    m_dwLastAlgIndex            = MAXDWORD;
    m_fIncludeSubjectKeyID      = TRUE;
    m_fHonorIncludeSubjectKeyID = FALSE;
    m_pPublicKeyInfo            = NULL;

    m_dwSigKeyLenMax = 0;
    m_dwSigKeyLenMin = 0;
    m_dwSigKeyLenDef = 0;
    m_dwSigKeyLenInc = 0;
    m_dwXhgKeyLenMax = 0;
    m_dwXhgKeyLenMin = 0;
    m_dwXhgKeyLenDef = 0;
    m_dwXhgKeyLenInc = 0;

    m_pCertContextPendingRequest       = NULL;
    m_pCertContextLastEnumerated       = NULL;
    m_dwCurrentPendingRequestIndex     = 0; 
    m_pPendingRequestTable             = NULL; 
    memset(&m_hashBlobPendingRequest, 0, sizeof(CRYPT_DATA_BLOB)); 
    ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));

    hr = S_OK;
ErrorReturn:
    return hr;
TRACE_ERROR(UuidToStringAError)
TRACE_ERROR(UuidCreateError)
}

void CCEnroll::FlushStore(StoreType storeType) {
    PSTOREINFO   pStoreInfo = NULL;

    switch(storeType) {

        case StoreMY:
            pStoreInfo = &m_MyStore;
            break;

        case StoreCA:
            pStoreInfo = &m_CAStore;
            break;

        case StoreROOT:
            pStoreInfo = &m_RootStore;
            break;

        case StoreREQUEST:
            pStoreInfo = &m_RequestStore;
            break;
    }

    EnterCriticalSection(&m_csXEnroll);

    if(pStoreInfo->hStore != NULL) {

        CertCloseStore(pStoreInfo->hStore, 0);
        pStoreInfo->hStore = NULL;
    }

    LeaveCriticalSection(&m_csXEnroll);
}

HCERTSTORE CCEnroll::GetStore(StoreType storeType) {

    PSTOREINFO   pStoreInfo = NULL;
    HCERTSTORE   hStore     = NULL;

    switch(storeType) {

        case StoreMY:
            pStoreInfo = &m_MyStore;
            break;

        case StoreCA:
            pStoreInfo = &m_CAStore;
            break;

        case StoreROOT:
            pStoreInfo = &m_RootStore;
            break;

        case StoreREQUEST:
            pStoreInfo = &m_RequestStore;
            break;

        default:
            SetLastError(ERROR_BAD_ARGUMENTS);
            return(NULL);
            break;
    }

    EnterCriticalSection(&m_csXEnroll);

    if(pStoreInfo->hStore == NULL) {

        pStoreInfo->hStore = CertOpenStore(
                pStoreInfo->szType,
                PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                NULL,
                pStoreInfo->dwFlags,
                pStoreInfo->wszName);
    }

    hStore = pStoreInfo->hStore;
    LeaveCriticalSection(&m_csXEnroll);

    return(hStore);
}

HCRYPTPROV CCEnroll::GetProv(DWORD dwFlags) {

    HCRYPTPROV  hProvT          = NULL;
    DWORD       cb              = 0;
    char *      pszProvName     = NULL;
    char *      pszContainerName = NULL;

    EnterCriticalSection(&m_csXEnroll);
    DWORD       dwProvType      = m_keyProvInfo.dwProvType;

    switch(dwFlags) {

        case CRYPT_NEWKEYSET:
            dwFlags = dwFlags | m_keyProvInfo.dwFlags;
            break;

        case CRYPT_DELETEKEYSET:
            if( m_hProv != NULL ) {

                CryptReleaseContext(m_hProv, 0);
                m_hProv = NULL;

                CryptAcquireContextU(&m_hProv,
                     m_keyProvInfo.pwszContainerName,
                     m_keyProvInfo.pwszProvName,
                     m_keyProvInfo.dwProvType,
                     CRYPT_DELETEKEYSET);
            }
            m_hProv = NULL;
            goto CommonReturn;
            break;

        default:
            dwFlags = m_keyProvInfo.dwFlags;
            break;
   }

    if(m_hProv == NULL) {

            if( CryptAcquireContextU(&m_hProv,
             m_keyProvInfo.pwszContainerName,
             m_keyProvInfo.pwszProvName,
             m_keyProvInfo.dwProvType,
             dwFlags) ) {

                pszProvName = NULL;
                while (TRUE)
                {
                    if(!CryptGetProvParam( m_hProv,
                                    PP_NAME,
                                    (BYTE*)pszProvName,
                                    &cb,
                                    0))
                    {
                        break;
                    }
                    if (NULL != pszProvName)
                    {
                        if(m_keyProvInfo.pwszProvName != wszEmpty)
                            MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
                        m_keyProvInfo.pwszProvName = WideFromMB(pszProvName);
                        break;
                    }
                    pszProvName = (char *)LocalAlloc(LMEM_FIXED, cb);
                    if (NULL == pszProvName)
                    {
                        goto CommonReturn;
                    }
                }

                BOOL fTryAnother = FALSE;
                cb = 0;
                pszContainerName = NULL;
                while (TRUE)
                {
                    if(!CryptGetProvParam( m_hProv,
                                    PP_UNIQUE_CONTAINER,
                                    (BYTE*)pszContainerName,
                                    &cb,
                                    0))
                    {
                        if (NULL == pszContainerName)
                        {
			    fTryAnother = TRUE;
                        }
                        else
                        {
			    LocalFree(pszContainerName); 
			    pszContainerName = NULL;
                        }
                        break;
                    }
                    else
                    {
                        if (NULL != pszContainerName)
                        {

                            break;
                        }
                    }
                    pszContainerName = (char *)LocalAlloc(LMEM_FIXED, cb);
                    if (NULL == pszContainerName)
                    {
                        goto CommonReturn;
                    }
                }

                if (fTryAnother)
                {

                    cb = 0;
                    pszContainerName = NULL;
                    while (TRUE)
                    {
                        if(!CryptGetProvParam(m_hProv,
                                    PP_CONTAINER,
                                    (BYTE*)pszContainerName,
                                    &cb,
                                    0))
                        {
                            if (NULL != pszContainerName)
                            {
				LocalFree(pszContainerName); 
                                pszContainerName = NULL;
                            }
                            break;
                        }
                        else
                        {
                            if (NULL != pszContainerName)
                            {

                                break;
                            }
                            pszContainerName = (char *)LocalAlloc(LMEM_FIXED, cb);
                            if (NULL == pszContainerName)
                            {
                                goto CommonReturn;
                            }
                        }
                    }
                }

                if(pszContainerName != NULL) {
                    if( m_keyProvInfo.pwszContainerName != wszEmpty )
                        MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
                    m_keyProvInfo.pwszContainerName = WideFromMB(pszContainerName);
                }

                cb = sizeof(DWORD);
                if(CryptGetProvParam(   m_hProv,
                                        PP_PROVTYPE,
                                        (BYTE *) &dwProvType,
                                        &cb,
                                        0) ) {
                    m_keyProvInfo.dwProvType = dwProvType;
                }

        } else {
            m_hProv = NULL;
        }

    }

CommonReturn:
    hProvT = m_hProv;
    LeaveCriticalSection(&m_csXEnroll);
    if (NULL != pszProvName)
    {
        LocalFree(pszProvName);
    }
    if (NULL != pszContainerName)
    {
        LocalFree(pszContainerName);
    }
    return(hProvT);
}

BOOL CCEnroll::SetKeyParams(
    PCRYPT_KEY_PROV_INFO pKeyProvInfo
) {

    EnterCriticalSection(&m_csXEnroll);

    if(m_hProv != NULL)
        CryptReleaseContext(m_hProv, 0);
    m_hProv = NULL;

    if(m_hVerifyProv != NULL)
        CryptReleaseContext(m_hVerifyProv, 0);
    m_hVerifyProv = NULL;

    put_ContainerNameWStr(pKeyProvInfo->pwszContainerName);
    put_ProviderNameWStr(pKeyProvInfo->pwszProvName);
    put_ProviderFlags(pKeyProvInfo->dwFlags);
    put_KeySpec(pKeyProvInfo->dwKeySpec);
    put_ProviderType(pKeyProvInfo->dwProvType);

    m_keyProvInfo.cProvParam          = 0;
    m_keyProvInfo.rgProvParam         = NULL;

    LeaveCriticalSection(&m_csXEnroll);

    return(TRUE);
}

HRESULT STDMETHODCALLTYPE CCEnroll::createPKCS10(
             BSTR DNName,
             BSTR wszPurpose,
             BSTR __RPC_FAR *pPKCS10) {

    return(createPKCS10WStrBStr(DNName, wszPurpose, pPKCS10));
}

HRESULT  CCEnroll::createPKCS10WStrBStr(
            LPCWSTR DNName,
            LPCWSTR wszPurpose,
            BSTR __RPC_FAR *pPKCS10) {

    HRESULT                     hr              = S_OK;
    CRYPT_DATA_BLOB             blobPKCS10;

    memset(&blobPKCS10, 0, sizeof(CRYPT_DATA_BLOB));

    hr = createPKCS10WStr(DNName, wszPurpose, &blobPKCS10);
    if(S_OK != hr)
    {
        goto createPKCS10Error;
    }

    hr = BlobToBstring(&blobPKCS10, CRYPT_STRING_BASE64, pPKCS10);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

CommonReturn:

    if(NULL != blobPKCS10.pbData)
    {
        MyCoTaskMemFree(blobPKCS10.pbData);
    }
    return(hr);

ErrorReturn:
    if(*pPKCS10 != NULL)
        SysFreeString(*pPKCS10);
    *pPKCS10 = NULL;

    goto CommonReturn;

TRACE_ERROR(createPKCS10Error);
TRACE_ERROR(BlobToBstringError);
}

HRESULT CCEnroll::AddCertsToStores(
    HCERTSTORE    hStoreMsg,
    LONG         *plCertInstalled
    ) {

    HCERTSTORE                  hStoreRoot              = NULL;
    HCERTSTORE                  hStoreCA                = NULL;
    PCCERT_CONTEXT              pCertContext            = NULL;
    PCCERT_CONTEXT              pCertContextLast        = NULL;
    LONG                        lCertInstalled = 0;
    HRESULT hr = S_OK;

    if (NULL != plCertInstalled)
    {
        *plCertInstalled = 0;
    }

    EnterCriticalSection(&m_csXEnroll);

    if( (hStoreCA = GetStore(StoreCA)) == NULL )
        goto ErrorCertOpenCAStore;

    if( (hStoreRoot = GetStore(StoreROOT)) == NULL )
        goto ErrorCertOpenRootStore;

    while( (pCertContext = CertEnumCertificatesInStore(
                        hStoreMsg,
                        pCertContextLast)) != NULL ) {

        if( CertCompareCertificateName(
                CRYPT_ASN_ENCODING,
                &pCertContext->pCertInfo->Subject,
                &pCertContext->pCertInfo->Issuer) ) {

            if (MySafeCertAddCertificateContextToStore(
                    hStoreRoot,
                    pCertContext,
                    CERT_STORE_ADD_USE_EXISTING,
                    NULL, 
		    m_dwEnabledSafteyOptions))
            {
                ++lCertInstalled;
            }
            else
            {
                if (S_OK == hr)
                {

                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hr)
                    {

                        hr = XENROLL_E_CANNOT_ADD_ROOT_CERT;
                    }
                }

            }
        }

        else  {

            if (MySafeCertAddCertificateContextToStore(
                    hStoreCA,
                    pCertContext,
                    CERT_STORE_ADD_USE_EXISTING,
                    NULL, 
		    m_dwEnabledSafteyOptions))
            {

                ++lCertInstalled;
            }
        }

        pCertContextLast = pCertContext;
    }
    pCertContextLast = NULL;
    if (NULL != plCertInstalled)
    {
        *plCertInstalled = lCertInstalled;
    }

CommonReturn:

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);

    goto CommonReturn;

TRACE_ERROR(ErrorCertOpenCAStore);
TRACE_ERROR(ErrorCertOpenRootStore);
}

BOOL
IsDesiredProperty(DWORD  dwPropertyId)
{
    DWORD  DesiredIds[] = {
        CERT_PVK_FILE_PROP_ID,
        CERT_FRIENDLY_NAME_PROP_ID,
        CERT_DESCRIPTION_PROP_ID,
        CERT_RENEWAL_PROP_ID,
    };
    DWORD i;

    for (i = 0; i < ARRAYSIZE(DesiredIds); ++i)
    {
        if (dwPropertyId == DesiredIds[i])
        {
            return TRUE;
        }
    }
    return FALSE;
}

BOOL
IsFilteredOutProperty(DWORD  dwPropertyId)
{
    DWORD  FilteredIds[] = {
        XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
        XENROLL_PASS_THRU_PROP_ID,
        CERT_KEY_PROV_INFO_PROP_ID,
        CERT_ENROLLMENT_PROP_ID, 
    };
    DWORD i;

    for (i = 0; i < ARRAYSIZE(FilteredIds); ++i)
    {
        if (dwPropertyId == FilteredIds[i])
        {
            return TRUE;
        }
    }
    return FALSE;
}

HRESULT CCEnroll::GetEndEntityCert(
    PCRYPT_DATA_BLOB    pBlobPKCS7,
    BOOL                fSaveToStores,
    PCCERT_CONTEXT     *ppCert
    )
{
    HRESULT                     hr = S_OK;
    HCERTSTORE                  hStoreMsg               = NULL;
    HCERTSTORE                  hStoreMy                = NULL;
    HCERTSTORE                  hStoreRequest           = NULL;

    PCCERT_CONTEXT              pCertContextLast        = NULL;
    PCCERT_CONTEXT              pCertContextRequest     = NULL;
    PCCERT_CONTEXT              pCertContextMsg         = NULL;
    PCCERT_CONTEXT              pCertContextArchive     = NULL;

    PCRYPT_KEY_PROV_INFO                pKeyProvInfo            = NULL;
    DWORD                       cb                      = 0;
    CRYPT_DATA_BLOB             blobData;

    CRYPT_HASH_BLOB             blobHash                = {sizeof(m_arHashBytesNewCert), m_arHashBytesNewCert};
    CRYPT_HASH_BLOB             blobHashRenew           = {sizeof(m_arHashBytesOldCert), m_arHashBytesOldCert};

    RequestFlags                requestFlags;
    CRYPT_HASH_BLOB             requestFlagsBlob;

    CRYPT_HASH_BLOB             renewalCertBlob;

    HCRYPTPROV                  hProv=NULL;
    BOOL  fSetting;
    DWORD                       dwPropertyId;
    CRYPT_DATA_BLOB             blobProp;
    BYTE                        *pbArchivedKeyHash = NULL;
    DWORD                        cbArchivedKeyHash = 0;

    EnterCriticalSection(&m_csXEnroll);

    memset(&requestFlags, 0, sizeof(RequestFlags));
    memset(&blobData, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&requestFlagsBlob, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&renewalCertBlob, 0, sizeof(CRYPT_DATA_BLOB));
    ZeroMemory(&blobProp, sizeof(blobProp));

    if (NULL == ppCert)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    *ppCert = NULL;

    if(!MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                       pBlobPKCS7,
                       (CERT_QUERY_CONTENT_FLAG_CERT |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
                       CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED) ,
                       CERT_QUERY_FORMAT_FLAG_ALL,
                       0,
                       NULL,
                       NULL,
                       NULL,
                       &hStoreMsg,
                       NULL,
                       NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto ErrorCryptQueryObject;
    }

    if (m_pCertContextStatic == NULL  ||
        (NULL == (pCertContextMsg = CertFindCertificateInStore(
                                hStoreMsg,
                                X509_ASN_ENCODING,
                                0,
                                CERT_FIND_HASH,
                                &blobHash,
                                NULL))))
    {

        if (NULL == (hStoreRequest = GetStore(StoreREQUEST)))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenRequestStore;
        }

        while (NULL != (pCertContextMsg = CertEnumCertificatesInStore(
                            hStoreMsg,
                            pCertContextLast)))
        {

            if (NULL != (pCertContextRequest = CertFindCertificateInStore(
                    hStoreRequest,
                    CRYPT_ASN_ENCODING,
                    0,
                    CERT_FIND_PUBLIC_KEY,
                    (void *) &pCertContextMsg->pCertInfo->SubjectPublicKeyInfo,
                    NULL)))
            {

                break;
            }

            pCertContextLast = pCertContextMsg;
        }
        pCertContextLast = NULL;

        if (NULL == pCertContextRequest)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorNoCertFound;
        }

        if (fSaveToStores)
        {

        cb = 0;
        while (TRUE)
        {
            if(!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    CERT_ARCHIVED_KEY_HASH_PROP_ID,
                    pbArchivedKeyHash,
                    &cbArchivedKeyHash))
            {
                if (NULL == pbArchivedKeyHash)
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    if (MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND) == hr)
                    {

                        break;
                    }

                    goto ErrorCertGetCertificateContextProperty;
                }
                else
                {

                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertGetCertificateContextProperty;
                }
            }
            if (NULL != pbArchivedKeyHash)
            {

                break;
            }
            pbArchivedKeyHash = (BYTE*)LocalAlloc(
                                    LMEM_FIXED, cbArchivedKeyHash);
            if (NULL == pbArchivedKeyHash)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }

        if (NULL != pbArchivedKeyHash && NULL == m_blobResponseKAHash.pbData)
        {

            hr = XENROLL_E_RESPONSE_KA_HASH_NOT_FOUND;
            goto ResponseKAHashNotFoundError;
        }
        if (NULL == pbArchivedKeyHash && NULL != m_blobResponseKAHash.pbData)
        {

            hr = XENROLL_E_RESPONSE_UNEXPECTED_KA_HASH;
            goto ResponseUnexpectedKAHashError;
        }
        if (NULL != pbArchivedKeyHash && NULL != m_blobResponseKAHash.pbData)
        {

            if (cbArchivedKeyHash != m_blobResponseKAHash.cbData ||
                0 != memcmp(pbArchivedKeyHash,
                            m_blobResponseKAHash.pbData,
                            cbArchivedKeyHash))
            {

                hr = XENROLL_E_RESPONSE_KA_HASH_MISMATCH;

                goto ResponseKAMismatchError;
            }
        }
        }

        fSetting = TRUE;
        cb = 0;
        while (TRUE)
        {
            if(!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    CERT_KEY_PROV_INFO_PROP_ID,
                    pKeyProvInfo,
                    &cb))
            {
                if (NULL == pKeyProvInfo)
                {

                    fSetting = FALSE;
                    break;
                }
                else
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertGetCertificateContextProperty;
                }
            }
            if (NULL != pKeyProvInfo)
            {

                break;
            }
            pKeyProvInfo = (PCRYPT_KEY_PROV_INFO)LocalAlloc(LMEM_FIXED, cb);
            if (NULL == pKeyProvInfo)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        if (fSetting)
        {

            if( !CertSetCertificateContextProperty(
                    pCertContextMsg,
                    CERT_KEY_PROV_INFO_PROP_ID,
                    0,
                    pKeyProvInfo) )
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorSetMyCertPropError;
            }

            SetKeyParams(pKeyProvInfo);
        }

        fSetting = TRUE;
        while (TRUE)
        {
            if(!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    XENROLL_PASS_THRU_PROP_ID,
                    requestFlagsBlob.pbData,
                    &requestFlagsBlob.cbData) )
            {
                if (NULL == requestFlagsBlob.pbData)
                {

                    fSetting = FALSE;
                    break;
                }
                else
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorDecodeRequestFlags;
                }
            }
            if (NULL != requestFlagsBlob.pbData)
            {

                break;
            }
            requestFlagsBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                                requestFlagsBlob.cbData);
            if (NULL == requestFlagsBlob.pbData)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }

        if (fSetting)
        {

            cb = sizeof(requestFlags);

            if (!CryptDecodeObject(
                    CRYPT_ASN_ENCODING,
                    XENROLL_REQUEST_INFO,
                    requestFlagsBlob.pbData,
                    requestFlagsBlob.cbData,
                    0,
                    &requestFlags,
                    &cb))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorDecodeRequestFlags;
            }

            if(!m_fWriteCertToCSPModified)
                m_fWriteCertToCSP     = requestFlags.fWriteToCSP;
            if(!m_fWriteCertToUserDSModified)
                m_fWriteCertToUserDS  = requestFlags.fWriteToDS;
            if(!m_fRequestStoreOpenFlagsModified)
                m_RequestStore.dwFlags = requestFlags.openFlags;
            if(!m_fMyStoreOpenFlagsModified)
                m_MyStore.dwFlags =   (m_MyStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
                                    (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
            if(!m_fCAStoreOpenFlagsModified)
                m_CAStore.dwFlags =   (m_CAStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
                                    (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
            if(!m_fRootStoreOpenFlagsModified) { 

		if (0 != m_dwEnabledSafteyOptions) { 
		    if (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCAL_MACHINE) { 
			m_RootStore.wszName = wszCA; 
		    }
		}
                m_RootStore.dwFlags = (m_RootStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
		    (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
	    }
        }

        m_fArchiveOldCert = FALSE;
        fSetting = TRUE;
        while (TRUE)
        {

            if (!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
                    renewalCertBlob.pbData,
                    &renewalCertBlob.cbData))
            {
                if (NULL == renewalCertBlob.pbData)
                {
                    fSetting = FALSE;
                    break;
                }
                else
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertGetCertificateContextProperty;
                }
            }
            if (NULL != renewalCertBlob.pbData)
            {

                break;
            }
            renewalCertBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                                renewalCertBlob.cbData);
            if (NULL == renewalCertBlob.pbData)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        if (fSetting)
        {

            if (NULL==hProv)
            {
                if(!CryptAcquireContext(
                        &hProv,
                        NULL,
                        MS_DEF_PROV,
                        PROV_RSA_FULL,
                        CRYPT_VERIFYCONTEXT))
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorAcquireContext;
                }
            }

            if (!CryptHashCertificate(
                hProv,  
                0,      
                X509_ASN_ENCODING,      
                renewalCertBlob.pbData,
                renewalCertBlob.cbData,
                blobHashRenew.pbData,
                &blobHashRenew.cbData))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorCryptHashCertificate;
            }
            m_fArchiveOldCert = TRUE;
        }

        dwPropertyId = CertEnumCertificateContextProperties(
                            pCertContextRequest, 0);  
        while (0 != dwPropertyId)
        {

            if (IsDesiredProperty(dwPropertyId))
            {
                fSetting = TRUE;
                while (TRUE)
                {
                    if (!CertGetCertificateContextProperty(
                            pCertContextRequest,
                            dwPropertyId,
                            blobProp.pbData,
                            &blobProp.cbData))
                    {

                        fSetting = FALSE;
                        break;
                    }
                    if (NULL != blobProp.pbData)
                    {

                        break;
                    }
                    blobProp.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, 
                                                        blobProp.cbData);
                    if (NULL == blobProp.pbData)
                    {
                        goto OutOfMemoryError;
                    }
                }
                if (fSetting)
                {

                    if (!CertSetCertificateContextProperty(
                                pCertContextMsg,
                                dwPropertyId,
                                0,
                                &blobProp))
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto ErrorSetMyCertPropError;
                    }
                }
                if (NULL != blobProp.pbData)
                {

                    LocalFree(blobProp.pbData);
                    blobProp.pbData = NULL;
                }
            }
            dwPropertyId = CertEnumCertificateContextProperties(
                                  pCertContextRequest,
                                  dwPropertyId);
        }

        if(m_pCertContextStatic != NULL)
            CertFreeCertificateContext(m_pCertContextStatic);

        m_pCertContextStatic = CertDuplicateCertificateContext(pCertContextMsg);

        if(NULL==hProv)
        {
            if(!CryptAcquireContext(
                &hProv,
                NULL,
                MS_DEF_PROV,
                PROV_RSA_FULL,
                CRYPT_VERIFYCONTEXT))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorAcquireContext;
            }
        }

        if( !CryptHashCertificate(
            hProv,             
            0,
            X509_ASN_ENCODING,
            pCertContextMsg->pbCertEncoded,
            pCertContextMsg->cbCertEncoded,
            blobHash.pbData,
            &blobHash.cbData) )
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCryptHashCertificate;
        }
    }

    assert(pCertContextMsg != NULL);
    CertDeleteCertificateFromStore(pCertContextMsg);
    pCertContextMsg = NULL; 

    assert(m_pCertContextStatic != NULL);
    pCertContextMsg = CertDuplicateCertificateContext(m_pCertContextStatic);

    if(fSaveToStores) {

        if( (hStoreMy = GetStore(StoreMY)) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenMYStore;
        }

        assert(pCertContextMsg != NULL);
        if( !MySafeCertAddCertificateContextToStore(
                hStoreMy,
                pCertContextMsg,
                CERT_STORE_ADD_USE_EXISTING,
                NULL,
		m_dwEnabledSafteyOptions) )
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertAddToMyStore;
        }

        if(m_fArchiveOldCert &&
            ((pCertContextArchive = CertFindCertificateInStore(
                    hStoreMy,
                    X509_ASN_ENCODING,
                    0,
                    CERT_FIND_HASH,
                    &blobHashRenew,
                    NULL)) != NULL) ) {

            CertSetCertificateContextProperty(
                                pCertContextArchive,
                                CERT_ARCHIVED_PROP_ID,
                                0,
                                &blobData);

            CertSetCertificateContextProperty(
                                pCertContextArchive,
                                CERT_RENEWAL_PROP_ID,
                                0,
                                &blobHash);
        }

        hr = AddCertsToStores(hStoreMsg, NULL);

        if (S_OK != hr &&
            MY_HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr &&
            XENROLL_E_CANNOT_ADD_ROOT_CERT != hr)
        {
            goto ErrorAddCertsToStores;
        }
    }

    *ppCert = pCertContextMsg;

CommonReturn:

    if(hProv)
        CryptReleaseContext(hProv, 0);

    if(pCertContextRequest != NULL)
        CertFreeCertificateContext(pCertContextRequest);

    if(pCertContextArchive != NULL)
        CertFreeCertificateContext(pCertContextArchive);

    assert(pCertContextLast == NULL);

    if(hStoreMsg != NULL)
        CertCloseStore(hStoreMsg, 0);

    FlushStore(StoreREQUEST);

    if (NULL != requestFlagsBlob.pbData)
    {
        LocalFree(requestFlagsBlob.pbData);
    }
    if (NULL != renewalCertBlob.pbData)
    {
        LocalFree(renewalCertBlob.pbData);
    }
    if (NULL != blobProp.pbData)
    {
        LocalFree(blobProp.pbData);
    }
    if (NULL != pKeyProvInfo)
    {
        LocalFree(pKeyProvInfo);
    }
    if (NULL != pbArchivedKeyHash)
    {
        LocalFree(pbArchivedKeyHash);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return (hr);

ErrorReturn:
    if(NULL != pCertContextMsg)
    {
        CertFreeCertificateContext(pCertContextMsg);
    }
    goto CommonReturn;

TRACE_ERROR(ErrorCryptHashCertificate);
TRACE_ERROR(ErrorCertOpenMYStore);
TRACE_ERROR(ErrorCertAddToMyStore);
TRACE_ERROR(ErrorCryptQueryObject);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorNoCertFound);
TRACE_ERROR(ErrorCertGetCertificateContextProperty);
TRACE_ERROR(ErrorSetMyCertPropError);
TRACE_ERROR(ErrorDecodeRequestFlags);
TRACE_ERROR(ErrorAcquireContext);      
TRACE_ERROR(ErrorAddCertsToStores);
TRACE_ERROR(OutOfMemoryError);
TRACE_ERROR(InvalidParameterError);
TRACE_ERROR(ResponseKAMismatchError)
TRACE_ERROR(ResponseUnexpectedKAHashError)
TRACE_ERROR(ResponseKAHashNotFoundError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::getCertFromPKCS7(
                         BSTR wszPKCS7,
                         BSTR __RPC_FAR *pbstrCert
) {

    HRESULT     hr;                     
    CRYPT_DATA_BLOB             blobPKCS7;
    CRYPT_DATA_BLOB             blobX509;
    PCCERT_CONTEXT              pCertContextMy          = NULL;

    assert(wszPKCS7 != NULL && pbstrCert != NULL);
    if (NULL == wszPKCS7 || NULL == pbstrCert)
	goto PointerError; 

    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    if( (pCertContextMy = getCertContextFromPKCS7(&blobPKCS7)) == NULL)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto getCertContextFromPKCS7Error;
    }

    blobX509.pbData = pCertContextMy->pbCertEncoded;
    blobX509.cbData = pCertContextMy->cbCertEncoded;

    hr = BlobToBstring(&blobX509, CRYPT_STRING_BASE64, pbstrCert);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if(pCertContextMy != NULL)
        CertFreeCertificateContext(pCertContextMy);

    return(hr);

SET_HRESULT(PointerError, E_POINTER); 
TRACE_ERROR(getCertContextFromPKCS7Error);
TRACE_ERROR(BlobToBstringError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptPKCS7(
                         BSTR wszPKCS7) {

    CRYPT_DATA_BLOB             blobPKCS7;
    HRESULT                     hr; 

    assert(wszPKCS7 != NULL);
    if (NULL == wszPKCS7) 
	goto PointerError; 

    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    hr = acceptPKCS7Blob(&blobPKCS7);
 ErrorReturn:
    return hr; 

SET_HRESULT(PointerError, E_POINTER); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::createFilePKCS10(
     BSTR DNName,
     BSTR Usage,
     BSTR wszPKCS10FileName) {
    return(createFilePKCS10WStr(DNName, Usage, wszPKCS10FileName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::addCertTypeToRequest(
             BSTR CertType) {
    return(AddCertTypeToRequestWStr(CertType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::addCertTypeToRequestEx( 
    IN  LONG            lType,
    IN  BSTR            bstrOIDOrName,
    IN  LONG            lMajorVersion,
    IN  BOOL            fMinorVersion,
    IN  LONG            lMinorVersion)
{
    return AddCertTypeToRequestWStrEx(
                        lType,
                        bstrOIDOrName,
                        lMajorVersion,
                        fMinorVersion,
                        lMinorVersion);

}

HRESULT STDMETHODCALLTYPE CCEnroll::getProviderType( 
    IN  BSTR  strProvName,
    OUT LONG *plProvType)
{
    return getProviderTypeWStr(strProvName, plProvType);
}

HRESULT STDMETHODCALLTYPE CCEnroll::addNameValuePairToSignature(
     BSTR Name,
     BSTR Value) {
    return(AddNameValuePairToSignatureWStr(Name, Value));
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptFilePKCS7(
     BSTR wszPKCS7FileName) {
    return(acceptFilePKCS7WStr(wszPKCS7FileName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::freeRequestInfo(
     BSTR bstrPKCS7OrPKCS10)
{
    HRESULT  hr;
    CRYPT_DATA_BLOB blob; 
    BYTE *pbData = NULL;
    DWORD cbData = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                        (WCHAR*)bstrPKCS7OrPKCS10,
                        SysStringLen(bstrPKCS7OrPKCS10),
                        CRYPT_STRING_ANY,
                        pbData,
                        &cbData,
                        NULL,
                        NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbData)
        {
            break; 
        }
        pbData = (BYTE*)LocalAlloc(LMEM_FIXED, cbData);
        if (NULL == pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    blob.cbData = cbData;
    blob.pbData = pbData;

    hr = freeRequestInfoBlob(blob); 
    if (S_OK != hr)
    {
        goto freeRequestInfoBlobError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pbData)
    {
        LocalFree(pbData);
    }
    return hr;

TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(freeRequestInfoBlobError)
}

HCERTSTORE STDMETHODCALLTYPE CCEnroll::getMyStore( void)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
    return NULL; 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreName(
     BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_MyStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*pbstrName = SysAllocString(m_MyStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreName(
     BSTR bstrName) {
    return(put_MyStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreType(
     BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_MyStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreType(
     BSTR bstrType) {
    return(put_MyStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreFlags(
     LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_MyStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreFlags(
     LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError; 

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {

        m_MyStore.dwFlags = dwFlags;
        m_fMyStoreOpenFlagsModified = TRUE;
        m_keyProvInfo.dwFlags |= KeyLocationFromStoreLocation(dwFlags);

        if(!m_fRequestStoreOpenFlagsModified) {
            m_RequestStore.dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
            m_RequestStore.dwFlags |= (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
        }
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreNameWStr(
     LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_MyStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*szwName = CopyWideString(m_MyStore.wszName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreNameWStr(
     LPWSTR szwName) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_MyStore.wszName != wszMY)
            MyCoTaskMemFree(m_MyStore.wszName);
        if( (m_MyStore.wszName = CopyWideString(szwName)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreTypeWStr(
     LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_MyStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreTypeWStr(
     LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_MyStore.szType != szSystemStore)
            MyCoTaskMemFree(m_MyStore.szType);
        if( (m_MyStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HCERTSTORE STDMETHODCALLTYPE CCEnroll::getCAStore( void)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
    return NULL; 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreName(
     BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_CAStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*pbstrName = SysAllocString(m_CAStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreName(
     BSTR bstrName) {
    return(put_CAStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreType(
     BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_CAStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreType(
     BSTR bstrType) {
    return(put_CAStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreFlags(
     LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_CAStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreFlags(
     LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError; 

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        m_fCAStoreOpenFlagsModified = TRUE;
        m_CAStore.dwFlags = dwFlags;
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreNameWStr(
     LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_CAStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*szwName = CopyWideString(m_CAStore.wszName)) == NULL )
         hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreNameWStr(
     LPWSTR szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_CAStore.wszName != wszCA)
            MyCoTaskMemFree(m_CAStore.wszName);
        if( (m_CAStore.wszName = CopyWideString(szwName)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreTypeWStr(
     LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_CAStore.szType)) == NULL )
         hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreTypeWStr(
     LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_CAStore.szType != szSystemStore)
            MyCoTaskMemFree(m_CAStore.szType);
        if( (m_CAStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HCERTSTORE STDMETHODCALLTYPE CCEnroll::getROOTHStore( void)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
    return NULL; 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreName(
     BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_RootStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*pbstrName = SysAllocString(m_RootStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreName(
     BSTR bstrName) {
    return(put_RootStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreType(
     BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_RootStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreType(
     BSTR bstrType) {
    return(put_RootStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreFlags(
     LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_RootStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreFlags(
     LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) { 

	if (!VerifyStoreFlagsSafeForScripting(dwFlags))
	    goto AccessDeniedError; 

	if (NULL == m_RootStore.hStore) { 
	    if (dwFlags & CERT_SYSTEM_STORE_LOCAL_MACHINE) { 
		m_RootStore.wszName = wszCA; 
	    } else { 
		m_RootStore.wszName = wszROOT; 
	    }
	}
    }

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        m_fRootStoreOpenFlagsModified = TRUE;
        m_RootStore.dwFlags = dwFlags;
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreNameWStr(
     LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_RootStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*szwName = CopyWideString(m_RootStore.wszName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreNameWStr(
     LPWSTR szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
            MyCoTaskMemFree(m_RootStore.wszName);
        if( (m_RootStore.wszName = CopyWideString(szwName)) == NULL )
             hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreTypeWStr(
     LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_RootStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreTypeWStr(
     LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RootStore.szType != szSystemStore)
            MyCoTaskMemFree(m_RootStore.szType);
        if( (m_RootStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreName(
     BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_RequestStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*pbstrName = SysAllocString(m_RequestStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreName(
     BSTR bstrName) {
    return(put_RequestStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreType(
     BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_RequestStore.szType)) == NULL )
         hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreType(
     BSTR bstrType) {
    return(put_RequestStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreFlags(
     LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_RequestStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreFlags(
     LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError; 

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {

        m_RequestStore.dwFlags = dwFlags;
        m_fRequestStoreOpenFlagsModified = TRUE;

        if(!m_fMyStoreOpenFlagsModified) {
            m_MyStore.dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
            m_MyStore.dwFlags |= (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
            m_keyProvInfo.dwFlags |= KeyLocationFromStoreLocation(m_MyStore.dwFlags);
        }
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreNameWStr(
     LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if(m_RequestStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);

    if( (*szwName = CopyWideString(m_RequestStore.wszName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreNameWStr(
     LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RequestStore.wszName != wszREQUEST)
            MyCoTaskMemFree(m_RequestStore.wszName);
        if( (m_RequestStore.wszName = CopyWideString(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreTypeWStr(
     LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_RequestStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreTypeWStr(
     LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RequestStore.szType != szSystemStore)
            MyCoTaskMemFree(m_RequestStore.szType);
        if( (m_RequestStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ContainerName(
     BSTR __RPC_FAR *pbstrContainer) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrContainer = SysAllocString(m_keyProvInfo.pwszContainerName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ContainerName(
     BSTR bstrContainer) {
    return(put_ContainerNameWStr(bstrContainer));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderName(
     BSTR __RPC_FAR *pbstrProvider) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrProvider = SysAllocString(m_keyProvInfo.pwszProvName)) == NULL )
         hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderName(
     BSTR bstrProvider) {
    return(put_ProviderNameWStr(bstrProvider));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderType(
     LONG __RPC_FAR *pdwType) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwType = m_keyProvInfo.dwProvType;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderType(
     LONG dwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else
        m_keyProvInfo.dwProvType = dwType;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_KeySpec(
     LONG __RPC_FAR *pdw) {
    EnterCriticalSection(&m_csXEnroll);
    *pdw = m_keyProvInfo.dwKeySpec;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_KeySpec(
     LONG dwKeySpec) {
    HRESULT hr;
    EnterCriticalSection(&m_csXEnroll);

    if(m_hProv != NULL)
    {
        hr = E_ACCESSDENIED;
        goto NullProvError;
    }

    if (m_fSMIMESetByClient)
    {

        if (m_fEnableSMIMECapabilities && AT_SIGNATURE == dwKeySpec)
        {

            hr = XENROLL_E_KEYSPEC_SMIME_MISMATCH;
            goto MismatchError;
        }
    }
    else
    {

        m_fEnableSMIMECapabilities = (dwKeySpec == AT_KEYEXCHANGE);
    }
    m_keyProvInfo.dwKeySpec = dwKeySpec;
    m_fKeySpecSetByClient = TRUE;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(NullProvError)
TRACE_ERROR(MismatchError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ClientId(
     LONG __RPC_FAR *pdw) {
    EnterCriticalSection(&m_csXEnroll);
    *pdw = m_lClientId;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ClientId(
     LONG dw) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    m_lClientId = dw;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_IncludeSubjectKeyID(
     BOOL __RPC_FAR *pfInclude) {
    EnterCriticalSection(&m_csXEnroll);
    *pfInclude = m_fIncludeSubjectKeyID;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_IncludeSubjectKeyID(
     BOOL fInclude) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    m_fIncludeSubjectKeyID = fInclude;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderFlags(
     LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_keyProvInfo.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderFlags(
     LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions && !VerifyProviderFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError;

    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else
       m_keyProvInfo.dwFlags = dwFlags;

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ContainerNameWStr(
     LPWSTR __RPC_FAR *szwContainer) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwContainer = CopyWideString(m_keyProvInfo.pwszContainerName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ContainerNameWStr(
     LPWSTR szwContainer) {
    HRESULT hr = S_OK;

    if(szwContainer == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));

    EnterCriticalSection(&m_csXEnroll);

    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else {
        if( m_keyProvInfo.pwszContainerName != wszEmpty)
            MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
        if( (m_keyProvInfo.pwszContainerName = CopyWideString(szwContainer)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderNameWStr(
     LPWSTR __RPC_FAR *szwProvider) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwProvider = CopyWideString(m_keyProvInfo.pwszProvName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderNameWStr(
     LPWSTR szwProvider) {
    HRESULT hr = S_OK;

    if(szwProvider == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));

    EnterCriticalSection(&m_csXEnroll);

    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else {
        if (0 != wcscmp(m_keyProvInfo.pwszProvName, szwProvider))
        {
            if( m_keyProvInfo.pwszProvName != wszEmpty )
                MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
            if( (m_keyProvInfo.pwszProvName = CopyWideString(szwProvider)) == NULL )
                hr = MY_HRESULT_FROM_WIN32(GetLastError());

            if (NULL != m_hVerifyProv)
            {
                CryptReleaseContext(m_hVerifyProv, 0);
                m_hVerifyProv = NULL;
            }

            m_dwXhgKeyLenMax = 0;
            m_dwXhgKeyLenMin = 0;
            m_dwXhgKeyLenDef = 0;
            m_dwXhgKeyLenInc = 0;
            m_dwSigKeyLenMax = 0;
            m_dwSigKeyLenMin = 0;
            m_dwSigKeyLenDef = 0;
            m_dwSigKeyLenInc = 0;
        }
    }
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_UseExistingKeySet(
     BOOL __RPC_FAR *fUseExistingKeys) {

    EnterCriticalSection(&m_csXEnroll);
    *fUseExistingKeys = m_fUseExistingKey;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_UseExistingKeySet(
     BOOL fUseExistingKeys) {

    EnterCriticalSection(&m_csXEnroll);
    m_fUseExistingKey = fUseExistingKeys;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_GenKeyFlags(
     LONG __RPC_FAR * pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_dwGenKeyFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_GenKeyFlags(
     LONG dwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    m_dwGenKeyFlags = dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_DeleteRequestCert(
     BOOL __RPC_FAR *fBool) {
    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fDeleteRequestCert;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_DeleteRequestCert(
     BOOL fBool) {
    EnterCriticalSection(&m_csXEnroll);
    m_fDeleteRequestCert = fBool;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_WriteCertToCSP(
     BOOL __RPC_FAR *fBool) {
    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fWriteCertToCSP;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_WriteCertToCSP(
     BOOL fBool) {
    EnterCriticalSection(&m_csXEnroll);
    m_fWriteCertToCSP = fBool;
    m_fWriteCertToCSPModified = TRUE;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_WriteCertToUserDS(
     BOOL __RPC_FAR *fBool) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fWriteCertToUserDS;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_WriteCertToUserDS(
     BOOL fBool) {
    if (0 != m_dwEnabledSafteyOptions) 
	return E_ACCESSDENIED;

    EnterCriticalSection(&m_csXEnroll);
    m_fWriteCertToUserDS = fBool;
    m_fWriteCertToUserDSModified = TRUE;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_EnableT61DNEncoding(
     BOOL __RPC_FAR *fBool) {
    EnterCriticalSection(&m_csXEnroll);
    *fBool = (m_dwT61DNEncoding == CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG);
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_EnableT61DNEncoding(
     BOOL fBool) {

    EnterCriticalSection(&m_csXEnroll);
    if(fBool)
        m_dwT61DNEncoding = CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
    else
        m_dwT61DNEncoding = 0;

    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_SPCFileName(
     BSTR __RPC_FAR *pbstr) {

    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstr = SysAllocString(m_wszSPCFileName)) == NULL )
        hr = E_OUTOFMEMORY;

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_SPCFileName(
     BSTR bstr) {
    return(put_SPCFileNameWStr(bstr));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_PVKFileName(
     BSTR __RPC_FAR *pbstr) {

    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstr = SysAllocString(m_wszPVKFileName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_PVKFileName(
     BSTR bstr) {
    return(put_PVKFileNameWStr(bstr));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgorithm(
     BSTR __RPC_FAR *pbstr) {

    LPWSTR  wszAlg  = NULL;
    HRESULT hr      = S_OK;

    assert(pbstr != NULL);
    *pbstr          = NULL;

    if( (hr = get_HashAlgorithmWStr(&wszAlg)) == S_OK ) {

        if( (*pbstr = SysAllocString(wszAlg)) == NULL )
            hr = E_OUTOFMEMORY;
    }

    if(wszAlg != NULL)
        MyCoTaskMemFree(wszAlg);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgorithm(
     BSTR bstr) {
    return(put_HashAlgorithmWStr(bstr));
}

HRESULT STDMETHODCALLTYPE CCEnroll::enumContainers(
             LONG                     dwIndex,
             BSTR __RPC_FAR *pbstr) {

    LPWSTR      pwsz        = NULL;
    HRESULT     hr;

    assert(pbstr != NULL);

    if((hr = enumContainersWStr(dwIndex, &pwsz)) != S_OK)
        goto EnumContainerError;

    if( (*pbstr = SysAllocString(pwsz)) == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto ErrorSysAllocString;
    }

    hr = S_OK;
ErrorReturn:
    if(pwsz != NULL)
        MyCoTaskMemFree(pwsz);
    return(hr);

TRACE_ERROR(EnumContainerError);
TRACE_ERROR(ErrorSysAllocString);
}

HRESULT STDMETHODCALLTYPE CCEnroll::enumProviders(
             LONG  dwIndex,
             LONG  dwFlags,
             BSTR __RPC_FAR *pbstrProvName) {
    HRESULT hr;
    LPWSTR pwszProvName  = NULL;

    assert(pbstrProvName != NULL);
    *pbstrProvName = NULL;

    if( (hr = enumProvidersWStr(dwIndex, dwFlags, &pwszProvName)) != S_OK)
        goto EnumProvidersError;

    if( (*pbstrProvName = SysAllocString(pwszProvName)) == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto ErrorSysAllocString;
    }

    hr = S_OK;
ErrorReturn:

    if(pwszProvName != NULL)
        MyCoTaskMemFree(pwszProvName);

    return(hr);

TRACE_ERROR(EnumProvidersError);
TRACE_ERROR(ErrorSysAllocString);
}

HRESULT STDMETHODCALLTYPE CCEnroll::createFilePKCS10WStr(
     LPCWSTR DNName,
     LPCWSTR Usage,
     LPCWSTR wszPKCS10FileName) {

    HRESULT     hr;                     
    BSTR        bstrPKCS10  = NULL;

    if( (hr = createPKCS10WStrBStr(
            DNName,
            Usage,
            &bstrPKCS10)) != S_OK)
    {
        goto ErrorCreatePKCS10;
    }

    hr = BStringToFile(bstrPKCS10, wszPKCS10FileName);
    if (S_OK != hr)
    {
        goto ErrorBStringToFile;
    }

    hr = S_OK;
ErrorReturn:
    if(bstrPKCS10 != NULL)
        SysFreeString(bstrPKCS10);

    return(hr);

TRACE_ERROR(ErrorBStringToFile);
TRACE_ERROR(ErrorCreatePKCS10);
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptFilePKCS7WStr(
     LPCWSTR wszPKCS7FileName)
{
    HRESULT     hr;
    CRYPT_DATA_BLOB  blob;

    ZeroMemory(&blob, sizeof(blob));

    hr = xeStringToBinaryFromFile(
                wszPKCS7FileName,
                &blob.pbData,
                &blob.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    hr = acceptPKCS7Blob(&blob);

ErrorReturn:
    if (NULL != blob.pbData)
    {
        MyCoTaskMemFree(blob.pbData);
    }
    return(hr);

TRACE_ERROR(xeStringToBinaryFromFileError)
}

BOOL GetAlgAndBitLen(
    HCRYPTPROV hProv,
    ALG_ID *    pAlg,
    DWORD  *    pdwBitLen,
    DWORD       dwFlags)
{
    static BOOL fNew = TRUE;
    PROV_ENUMALGS_EX    enumAlgsEx;
    PROV_ENUMALGS       enumAlgs;
    DWORD               cb = 0;

    *pAlg = 0;
    *pdwBitLen = 0;

    if(fNew) {

        cb = sizeof(enumAlgsEx);
        if(CryptGetProvParam(
            hProv,
            PP_ENUMALGS_EX,
            (BYTE *) &enumAlgsEx,
            &cb,
            dwFlags)) {

            *pAlg       = enumAlgsEx.aiAlgid;
            *pdwBitLen  = enumAlgsEx.dwMaxLen;

            return(TRUE);        

        } else if(dwFlags != 0)
            fNew = FALSE;
        else
            return(FALSE);
    }

    cb = sizeof(PROV_ENUMALGS);
    if(CryptGetProvParam(
        hProv,
            PP_ENUMALGS,
        (BYTE *) &enumAlgs,
        &cb,
        dwFlags) ) {

        *pAlg       = enumAlgs.aiAlgid;
        *pdwBitLen  = enumAlgs.dwBitLen;

        return(TRUE);

    }

    return(FALSE);
}    

HRESULT
CreateSMimeExtension(
    IN  HCRYPTPROV   hProv, 
    OUT BYTE       **ppbSMime,
    OUT DWORD       *pcbSMime)
{
#define                     CINCSMIMECAP    20
    HRESULT  hr;

    DWORD                      dwBitLen;
    DWORD                      i;
    DWORD                      cbE;
    BYTE                      *pbE = NULL;
    DWORD                      dwFlags;
    PCCRYPT_OID_INFO           pOidInfo = NULL;
    CRYPT_SMIME_CAPABILITIES   smimeCaps;
    DWORD                      crgsmimeCap = 0;
    ALG_ID                     AlgID;
    BYTE                      *pb = NULL;
    DWORD                      cb = 0;

    memset(&smimeCaps, 0, sizeof(CRYPT_SMIME_CAPABILITIES));

    smimeCaps.rgCapability = (PCRYPT_SMIME_CAPABILITY) LocalAlloc(LPTR, CINCSMIMECAP * sizeof(CRYPT_SMIME_CAPABILITY));
    if (NULL == smimeCaps.rgCapability)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    crgsmimeCap = CINCSMIMECAP;

    dwFlags = CRYPT_FIRST; 
    while (GetAlgAndBitLen(hProv, &AlgID, &dwBitLen, dwFlags))
    {
        pbE = NULL;
        cbE = 0;
        dwFlags = 0; 

        if(ALG_CLASS_DATA_ENCRYPT == GET_ALG_CLASS(AlgID))
        {
            if(AlgID == CALG_RC2  || AlgID == CALG_RC4)
            {

                while (TRUE)
                {
                    if(!CryptEncodeObject(
                            CRYPT_ASN_ENCODING,
                            X509_INTEGER,
                            &dwBitLen,
                            pbE,           
                            &cbE))
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto CryptEncodeObjectError;
                    }
                    if (NULL != pbE)
                    {
                        break;
                    }
                    pbE = (BYTE *)LocalAlloc(LPTR, cbE);
                    if (NULL == pbE)
                    {
                        hr = E_OUTOFMEMORY;
                        goto OutOfMemoryError;
                    }
                }
            }
        } else {
            continue;
        }

        pOidInfo = xeCryptFindOIDInfo(
                        CRYPT_OID_INFO_ALGID_KEY,
                        (void *) &AlgID,
                        CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
        if(NULL == pOidInfo)
        {

	    if (NULL != pbE) { 
		LocalFree(pbE); 
	    }
	    pbE = NULL; 
            continue;
        }

        if(smimeCaps.cCapability >= crgsmimeCap)
        {
	    PCRYPT_SMIME_CAPABILITY pSmimeCapsTmp; 

            crgsmimeCap += CINCSMIMECAP;
	    pSmimeCapsTmp = (PCRYPT_SMIME_CAPABILITY)LocalReAlloc(
                                smimeCaps.rgCapability,
                                crgsmimeCap * sizeof(CRYPT_SMIME_CAPABILITY),
                                LMEM_MOVEABLE | LMEM_ZEROINIT);
            if(NULL == pSmimeCapsTmp)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
	    smimeCaps.rgCapability = pSmimeCapsTmp; 
        }

        smimeCaps.rgCapability[smimeCaps.cCapability].pszObjId = (char *) pOidInfo->pszOID;
        smimeCaps.rgCapability[smimeCaps.cCapability].Parameters.pbData = pbE;
        smimeCaps.rgCapability[smimeCaps.cCapability].Parameters.cbData = cbE;
        smimeCaps.cCapability++;
	pbE = NULL;  
    }

    while (TRUE)
    {
        if (!CryptEncodeObject(
                        CRYPT_ASN_ENCODING,
                        PKCS_SMIME_CAPABILITIES,
                        &smimeCaps,
                        pb,
                        &cb)) 
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptEncodeObjectError;
        }
        if (NULL != pb)
        {
            break;
        }
        pb = (BYTE *)LocalAlloc(LPTR, cb);
    }
    *ppbSMime = pb;
    *pcbSMime = cb;
    pb = NULL;

    hr = S_OK;

ErrorReturn:
    if(NULL != smimeCaps.rgCapability)
    {
        for (i = 0; i < smimeCaps.cCapability; ++i)
        {
            if (NULL != smimeCaps.rgCapability[i].Parameters.pbData)
            {
                LocalFree(smimeCaps.rgCapability[i].Parameters.pbData);
            }
        }
        LocalFree(smimeCaps.rgCapability);
    }
    if(NULL != pb)
    {
        LocalFree(pb);
    }
    if (NULL != pbE) 
    { 
	LocalFree(pbE); 
    }
    return hr;

TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(OutOfMemoryError)
}

#if DBG
void DebugGetContainerSD(HCRYPTPROV hProv)
{
    PSECURITY_DESCRIPTOR  pSD = NULL;
    DWORD                 cbSD;

    while (TRUE)
    {
        if (!CryptGetProvParam(
                hProv,
                PP_KEYSET_SEC_DESCR,
                (BYTE*)pSD,
                &cbSD,
                DACL_SECURITY_INFORMATION))
        {
            break;
        }
        if (NULL != pSD)
        {
            break;
        }
        pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD);
        if (NULL == pSD)
        {
            break;
        }
    }
    if (NULL != pSD)
    {
	LocalFree(pSD);
    }
}
#endif 

HRESULT
GetCurrentUserInfo(
    OUT PTOKEN_USER *ppUserInfo,
    OUT BOOL        *pfAdmin)
{
    HRESULT  hr;
    PTOKEN_USER   pUserInfo = NULL;
    DWORD         dwSize = 0;
    HANDLE        hToken = NULL;
    HANDLE        hDupToken = NULL;
    PSID          psidAdministrators = NULL;
    SID_IDENTIFIER_AUTHORITY    siaNtAuthority = SECURITY_NT_AUTHORITY;
    HANDLE hThread;
    HANDLE hProcess;

    *pfAdmin = FALSE;

    if (!AllocateAndInitializeSid(
                            &siaNtAuthority,
                            2,
                            SECURITY_BUILTIN_DOMAIN_RID,
                            DOMAIN_ALIAS_RID_ADMINS,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            &psidAdministrators))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto AllocateAndInitializeSidError;
    }

    hThread = GetCurrentThread();
    if (NULL == hThread)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetCurrentThreadError;
    }

    if (!OpenThreadToken(
            hThread, 
            TOKEN_QUERY | TOKEN_DUPLICATE, 
            FALSE,
            &hToken))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        if(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) != hr)
        {
            goto OpenThreadTokenError;
        }

        hProcess = GetCurrentProcess();
        if (NULL == hProcess)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto GetCurrentProcessError;
        }

        hToken = NULL;
        if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &hToken))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto OpenProcessTokenError;
        }
    }

    if (!DuplicateToken(hToken, SecurityIdentification, &hDupToken))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto DuplicateTokenError;
    }

    if (!MyCheckTokenMembership(hDupToken, psidAdministrators, pfAdmin))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CheckTokenMembershipError;
    }

    while (TRUE)
    {
        if (!GetTokenInformation(
                hToken,
                TokenUser,
                pUserInfo,
                dwSize,
                &dwSize))
        {
            if (NULL != pUserInfo ||
                ERROR_INSUFFICIENT_BUFFER != GetLastError())
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto GetTokenInformationError;
            }
        }

        if (NULL != pUserInfo)
        {

            break;
        }
        pUserInfo = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, dwSize);
        if (NULL == pUserInfo)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    if (NULL != ppUserInfo)
    {
        *ppUserInfo = pUserInfo;
        pUserInfo = NULL;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pUserInfo)
    {
        LocalFree(pUserInfo);
    }
    if (NULL != hToken)
    {
        CloseHandle(hToken);
    }
    if (NULL != hDupToken)
    {
        CloseHandle(hDupToken);
    }
    if (NULL != psidAdministrators)
    {
        FreeSid(psidAdministrators);
    }

    return hr;

TRACE_ERROR(LocalAllocError)
TRACE_ERROR(GetTokenInformationError)
TRACE_ERROR(OpenProcessTokenError)
TRACE_ERROR(GetCurrentProcessError)
TRACE_ERROR(CheckTokenMembershipError)
TRACE_ERROR(DuplicateTokenError)
TRACE_ERROR(OpenThreadTokenError)
TRACE_ERROR(GetCurrentThreadError)
TRACE_ERROR(AllocateAndInitializeSidError)
}

HRESULT
SetKeyContainerSecurityForNULLDacl(
    HCRYPTPROV   hProv,
    DWORD        dwFlags, 
    PTOKEN_USER  pUserInfo)
{
    DWORD                 ccNeeded; 
    HRESULT               hr;
    LPWSTR                wszSD       = NULL; 
    LPWSTR                wszUserSid  = NULL;
    PSECURITY_DESCRIPTOR  pSD         = NULL;

    UNREFERENCED_PARAMETER(dwFlags);

#define SDDL_NEW_KEYSET_START L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;"
#define SDDL_NEW_KEYSET_END   L")"

    if (!ConvertSidToStringSidW(pUserInfo->User.Sid, &wszUserSid)) { 
	goto ConvertSidToStringSidError; 
    }

    ccNeeded = (DWORD)(wcslen(SDDL_NEW_KEYSET_START) + wcslen(wszUserSid) + wcslen(SDDL_NEW_KEYSET_END) + 1); 
    wszSD = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*ccNeeded);
    if (NULL == wszSD) { 
	goto MemoryError;
    }

    wcscpy(wszSD, SDDL_NEW_KEYSET_START); 
    wcscat(wszSD, wszUserSid);
    wcscat(wszSD, SDDL_NEW_KEYSET_END); 

    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(wszSD, SDDL_REVISION_1, &pSD, NULL)) { 
	goto ConvertStringSecurityDescriptorToSecurityDescriptorWError; 
    }

    if (!IsValidSecurityDescriptor(pSD)) { 
	goto IsValidSecurityDescriptorError;
    }

#if DBG
    DebugGetContainerSD(hProv); 
#endif

    if (!CryptSetProvParam(hProv, PP_KEYSET_SEC_DESCR, (BYTE*)pSD, DACL_SECURITY_INFORMATION)) { 
        goto CryptSetProvParamError;
    }

#if DBG
    DebugGetContainerSD(hProv); 
#endif

    hr = S_OK;
ErrorReturn:
    if (NULL != wszSD)
    {
	LocalFree(wszSD);
    }
    if (NULL != wszUserSid)
    {
	LocalFree(wszUserSid); 
    }
    if (NULL != pSD)
    {
        LocalFree(pSD);
    }
    return hr;

SET_HRESULT(ConvertSidToStringSidError,                                 HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(ConvertStringSecurityDescriptorToSecurityDescriptorWError,  HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(CryptSetProvParamError,                                     HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(IsValidSecurityDescriptorError,                             HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR)); 
SET_HRESULT(MemoryError,                                                E_OUTOFMEMORY); 
}

HRESULT
SetKeyContainerSecurity(
    HCRYPTPROV hProv,
    DWORD      dwFlags)
{
    HRESULT               hr;
    PSECURITY_DESCRIPTOR  pNewSD = NULL;
    PSECURITY_DESCRIPTOR  pSD = NULL;

    DWORD                 cbSD;
    ACL_SIZE_INFORMATION  AclInfo;

    PTOKEN_USER           pUserInfo = NULL;
    PACL                  pNewAcl = NULL;
    LPVOID pAce;
    DWORD  dwIndex;
    BYTE   AceType;
    PACL   pAcl;
    BOOL   fDacl = TRUE;
    BOOL   fDef = FALSE;
    BOOL   fAdmin;
    BOOL   fKeepSystemSid;
    BOOL   fMachineKeySet = (0x0 != (dwFlags & CRYPT_MACHINE_KEYSET)) ?
                            TRUE : FALSE;

    PSID                      pSidSystem = NULL;
    PSID                      pSidAdministrators = NULL; 
    SID_IDENTIFIER_AUTHORITY  siaNtAuthority = SECURITY_NT_AUTHORITY;

    hr = GetCurrentUserInfo(&pUserInfo, &fAdmin);
    if (S_OK != hr)
    {
        goto GetCurrentUserInfoError;
    }

    while (TRUE)
    {
        if (!CryptGetProvParam(
                hProv,
                PP_KEYSET_SEC_DESCR,
                (BYTE*)pSD,
                &cbSD,
                DACL_SECURITY_INFORMATION))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptGetProvParamError;
        }
        if (NULL != pSD)
        {
            break;
        }
        pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD);
        if (NULL == pSD)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if (!GetSecurityDescriptorDacl(
            pSD,
            &fDacl,
            &pAcl,
            &fDef))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetSecurityDescriptorDaclError;
    }
    if (!fDacl)
    {

        hr = MY_HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
        goto GetSecurityDescriptorDaclError;
    }
    if (NULL == pAcl)
    {
#if 0

	hr = SetKeyContainerSecurityForNULLDacl(hProv, dwFlags, pUserInfo); 
#endif

	hr = S_OK; 
	goto done; 
    }

    if (!GetAclInformation(
            pAcl,
            &AclInfo,
            sizeof(AclInfo),
            AclSizeInformation))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetAclInformationError;
    }

    pNewAcl = (PACL)LocalAlloc(LMEM_ZEROINIT, AclInfo.AclBytesInUse);
    if (NULL == pNewAcl)
    {
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    }
    if (!InitializeAcl(pNewAcl, AclInfo.AclBytesInUse, ACL_REVISION))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto InitializeAclError;
    }

    fKeepSystemSid = fAdmin && fMachineKeySet;
    if (fKeepSystemSid)
    {

        if (!AllocateAndInitializeSid(
                            &siaNtAuthority,
                            1,
                            SECURITY_LOCAL_SYSTEM_RID,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            &pSidSystem))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto AllocateAndInitializeSidError;
        }
    }

    if (!AllocateAndInitializeSid(&siaNtAuthority,
				  2,
				  SECURITY_BUILTIN_DOMAIN_RID,
				  DOMAIN_ALIAS_RID_ADMINS,
				  0, 0, 0, 0, 0, 0,
				  &pSidAdministrators
				  ))
    {
	hr = MY_HRESULT_FROM_WIN32(GetLastError());
	goto AllocateAndInitializeSidError;
    }

    for (dwIndex = 0; dwIndex < AclInfo.AceCount; ++dwIndex)
    {
        if (!GetAce(pAcl, dwIndex, &pAce))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto GetAceError;
        }
        AceType = ((ACCESS_ALLOWED_ACE*)pAce)->Header.AceType;
        if (ACCESS_ALLOWED_ACE_TYPE == AceType)
        {
            if (EqualSid(pUserInfo->User.Sid, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)) 
		|| (fKeepSystemSid && EqualSid(pSidSystem, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
		|| EqualSid(pSidAdministrators, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
            {

                if (!AddAccessAllowedAce(
                        pNewAcl,
                        ACL_REVISION,
                        ((PACCESS_ALLOWED_ACE)pAce)->Mask,
                        (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto AddAccessAllowedAceError;
                }
            }
        }
        else if (ACCESS_DENIED_ACE_TYPE == AceType)
        {

            if (!AddAccessDeniedAce(
                    pNewAcl,
                    ACL_REVISION,
                    ((PACCESS_ALLOWED_ACE)pAce)->Mask,
                    (PSID)&(((PACCESS_DENIED_ACE)pAce)->SidStart)))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto AddAccessDeniedAceError;
            }
        }
    }

    pNewSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, 
                         SECURITY_DESCRIPTOR_MIN_LENGTH); 
    if (pNewSD == NULL)
    { 
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    } 

    if (!InitializeSecurityDescriptor(pNewSD, SECURITY_DESCRIPTOR_REVISION))
    {  
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto InitializeSecurityDescriptorError;
    } 

    if (!SetSecurityDescriptorDacl(
            pNewSD, 
            TRUE,     
            pNewAcl, 
            FALSE))   
    {  
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto SetSecurityDescriptorDaclError;
    } 

    if (!MySetSecurityDescriptorControl(
            pNewSD,
            SE_DACL_PROTECTED,
            SE_DACL_PROTECTED))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto SetSecurityDescriptorControlError;
    }

    if (!IsValidSecurityDescriptor(pNewSD))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
	goto IsValidSecurityDescriptorError;
    }

#if DBG
    DebugGetContainerSD(hProv); 
#endif

    if (!CryptSetProvParam(
            hProv,
            PP_KEYSET_SEC_DESCR,
            (BYTE*)pNewSD,
            DACL_SECURITY_INFORMATION))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CryptSetProvParamError;
    }

#if DBG
    DebugGetContainerSD(hProv); 
#endif

done:
    hr = S_OK;
ErrorReturn:
    if (NULL != pSD)
    {
        LocalFree(pSD);
    }
    if (NULL != pUserInfo) 
    {
        LocalFree(pUserInfo);
    }
    if (NULL != pNewAcl) 
    {
        LocalFree(pNewAcl);
    }
    if (NULL != pNewSD) 
    {
        LocalFree(pNewSD);
    }
    if (NULL != pSidSystem)
    {
        FreeSid(pSidSystem);
    }
    if (NULL != pSidAdministrators)
    {
	FreeSid(pSidAdministrators);
    }
    return hr;

TRACE_ERROR(CryptSetProvParamError)
TRACE_ERROR(SetSecurityDescriptorDaclError)
TRACE_ERROR(InitializeSecurityDescriptorError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(AddAccessAllowedAceError)
TRACE_ERROR(AddAccessDeniedAceError)
TRACE_ERROR(GetAceError)
TRACE_ERROR(GetCurrentUserInfoError)
TRACE_ERROR(InitializeAclError)
TRACE_ERROR(GetAclInformationError)
TRACE_ERROR(GetSecurityDescriptorDaclError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptGetProvParamError)
TRACE_ERROR(SetSecurityDescriptorControlError)
TRACE_ERROR(IsValidSecurityDescriptorError)
TRACE_ERROR(AllocateAndInitializeSidError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::createPKCS10WStr(
     LPCWSTR DNName,
     LPCWSTR wszPurpose,
     PCRYPT_DATA_BLOB pPkcs10Blob)
{
    #define EndExt      5

    #define EndAttr     6

    HCRYPTPROV                  hProv           = NULL;
    HCRYPTKEY                   hKey            = NULL;

    CERT_REQUEST_INFO           reqInfo;

    CERT_EXTENSIONS             Extensions;
    PCERT_EXTENSION             pExtCur         = NULL;
    PCERT_EXTENSION             rgExtension     = NULL;
    CRYPT_ATTRIBUTE             rgAttribute[EndAttr];
    CRYPT_ATTR_BLOB             blobExt;
    CRYPT_ATTR_BLOB             blobCSPAttr;
    CRYPT_CSP_PROVIDER          CSPProvider;
    CRYPT_ATTR_BLOB             blobOSVAttr;
    CRYPT_ATTR_BLOB             blobSMIMEPKCS7;
    CERT_NAME_VALUE             cnvOSVer;
    OSVERSIONINFO               osvInfo;

    DWORD                       iExt            = 0;
    CRYPT_BIT_BLOB              bbKeyUsage;
    BYTE                        bKeyUsage;

    CERT_SIGNED_CONTENT_INFO    SignatureInfo;

    HRESULT                     hr              = S_OK;
    DWORD                       errBefore       = GetLastError();

    PCCERT_CONTEXT              pCertContext    = NULL;
    HCERTSTORE                  hStore          = NULL;
    DWORD                       ssFlags         = 0;

    HANDLE                      hFile           = NULL;
    CRYPT_DATA_BLOB             blobData;

    DWORD                       cb              = 0;
    char *                      pszPurpose       = NULL;
    char *                      szStart         = NULL;
    char *                      szEnd           = NULL;
    char                        szVersion[45]   = {0};

    BOOL                        fAddCodeSign    = FALSE;
    DWORD                       cPassedEKU      = 0;
    DWORD                       i               = 0;
    BOOL                        fRet;
    BYTE                       *pbSMime = NULL;
    BYTE                       *pbKU = NULL;
    BYTE                       *pbEKU = NULL;
    PPROP_STACK                 pProp;
    CRYPT_ATTR_BLOB             blobClientId;
    DWORD                       cPublicKeyInfo = 0;
    BYTE                       *pbSubjectKeyHashExtension = NULL;
    DWORD                       cbSubjectKeyHashExtension = 0;
    DWORD                       dwErr;

    LPWSTR                      pwszNotSafeRequesting     = NULL; 
    LPWSTR                      pwszTitle                 = NULL; 

    LPSTR rgszExtensionOIDs[] = { 
        szOID_ENROLL_CERTTYPE_EXTENSION,
        szOID_CERTIFICATE_TEMPLATE
    }; 

    CERT_EXTENSION  rgNeededExtensions[sizeof(rgszExtensionOIDs) / sizeof(LPSTR)]; 

    CERT_EXTENSIONS ceExtensions; 
    ceExtensions.rgExtension = &rgNeededExtensions[0]; 
    ceExtensions.cExtension  = 0; 

    CRYPT_KEY_PROV_INFO         keyProvInfoT;
        CERT_ENHKEY_USAGE                   enhKeyUsage;

        CRYPT_DATA_BLOB             blobPKCS7;
        CRYPT_DATA_BLOB             blobRenewAttr;
        RequestFlags                requestFlags;
        CRYPT_DATA_BLOB             requestInfoBlob;
        CRYPT_DATA_BLOB             blobRenewalCert;

        ALG_ID                      rgAlg[2];
        PCCRYPT_OID_INFO            pOidInfo        = NULL;

    EnterCriticalSection(&m_csXEnroll);

    SetLastError(ERROR_SUCCESS);

    assert(pPkcs10Blob != NULL);

    memset(&Extensions, 0, sizeof(CERT_EXTENSIONS));
    memset(&rgAttribute, 0, sizeof(rgAttribute));
    memset(&reqInfo, 0, sizeof(CERT_REQUEST_INFO));
    memset(&SignatureInfo, 0, sizeof(SignatureInfo));
    memset(&blobData, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&enhKeyUsage, 0, sizeof(CERT_ENHKEY_USAGE ));
    memset(pPkcs10Blob, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&blobRenewAttr, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&requestFlags, 0, sizeof(RequestFlags));
    memset(&requestInfoBlob, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&CSPProvider, 0, sizeof(CRYPT_CSP_PROVIDER));
    memset(&cnvOSVer, 0, sizeof(CERT_NAME_VALUE));
    memset(&osvInfo, 0, sizeof(OSVERSIONINFO));
    memset(&blobSMIMEPKCS7, 0, sizeof(CRYPT_ATTR_BLOB));
    memset(&rgNeededExtensions[0], 0, sizeof(rgNeededExtensions)); 
    ZeroMemory(&blobExt, sizeof(blobExt));
    memset(&blobCSPAttr, 0, sizeof(CRYPT_ATTR_BLOB));
    memset(&blobOSVAttr, 0, sizeof(CRYPT_ATTR_BLOB));
    memset(&blobClientId, 0, sizeof(CRYPT_ATTR_BLOB));

    reqInfo.dwVersion = CERT_REQUEST_V1;

    if (0 != m_dwEnabledSafteyOptions) { 
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
	if (S_OK != hr) { 
	    SetLastError(hr);
	    goto xeLoadRCStringError;
	}

	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_REQUESTING_CERT, &pwszNotSafeRequesting);
	if (S_OK != hr) { 
	    SetLastError(hr);
	    goto xeLoadRCStringError;
	}

	if (IDYES != MessageBoxU(NULL, pwszNotSafeRequesting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) { 
	    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); 
	    SetLastError(hr); 
	    goto CancelledError; 
	}
    }

    if(!m_fUseExistingKey)
    {

        if((hProv = GetProv(CRYPT_NEWKEYSET)) == NULL) {

            if( m_fReuseHardwareKeyIfUnableToGenNew &&
                GetLastError() == NTE_TOKEN_KEYSET_STORAGE_FULL) {

                    if( m_keyProvInfo.pwszContainerName != wszEmpty )
                        MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
                    m_keyProvInfo.pwszContainerName = wszEmpty;

                    m_fUseExistingKey = TRUE;
            }
            else
                goto ErrorCryptAcquireContext;
        }
    }

    if(m_fUseExistingKey) {

        if((hProv = GetProv(0)) == NULL)
            goto ErrorCryptAcquireContext;
    }

    if(!CryptGetUserKey(
                hProv,
                m_keyProvInfo.dwKeySpec,
                &hKey))
    {

        dwErr = GetLastError();
        if (SCARD_W_CANCELLED_BY_USER == dwErr ||
            ERROR_CANCELLED == dwErr ||
            ERROR_ACCESS_DENIED == dwErr)
        {
            goto CryptGetUserKeyCancelError;
        }

        assert(hKey == NULL);

	if (NULL != m_hCachedKey)
	{
	    CryptDestroyKey(m_hCachedKey); 
	    m_hCachedKey = NULL; 
	}

        if(!CryptGenKey(    hProv,
                            m_keyProvInfo.dwKeySpec,
                            m_dwGenKeyFlags | CRYPT_ARCHIVABLE,
                            &m_hCachedKey) )
        {

            dwErr = GetLastError();
            if (SCARD_W_CANCELLED_BY_USER == dwErr ||
                ERROR_CANCELLED == dwErr ||
                ERROR_ACCESS_DENIED == dwErr)
            {
		goto ErrorCryptGenKey;
            }

            assert(NULL == m_hCachedKey);
            DWORD dwGenKeyFlags = m_dwGenKeyFlags;
            if (NULL != m_PrivateKeyArchiveCertificate && m_fNewRequestMethod && (0 == (dwGenKeyFlags & CRYPT_EXPORTABLE)))
            {

		goto ErrorCryptGenKey;
            }

            if (!CryptGenKey(
                        hProv,
                        m_keyProvInfo.dwKeySpec,
                        dwGenKeyFlags,
                        &hKey))
            {
                goto ErrorCryptGenKey;
            }
	}

	hr = SetKeyContainerSecurity(hProv, m_keyProvInfo.dwFlags);
#if DBG
	if (S_OK != hr)
	{
	    goto SetKeyContainerSecurityError;
	}
#endif 
	hr = S_OK; 
    }

    if (NULL != hKey)
    {

        CryptDestroyKey(hKey);
    }
    if ((NULL == m_PrivateKeyArchiveCertificate || !m_fNewRequestMethod) &&
        NULL != m_hCachedKey)
    {

        CryptDestroyKey(m_hCachedKey);
        m_hCachedKey = NULL;
    }

    if (NULL != m_pPublicKeyInfo)
    {
        LocalFree(m_pPublicKeyInfo);
        m_pPublicKeyInfo = NULL;
    }
    while (TRUE)
    {
        if(!CryptExportPublicKeyInfo(hProv,
                            m_keyProvInfo.dwKeySpec,
                            X509_ASN_ENCODING,
                            m_pPublicKeyInfo,
                            &cPublicKeyInfo))
        {
            goto ErrorCryptExportPublicKeyInfo;
        }
        if (NULL != m_pPublicKeyInfo)
        {
            break;
        }
        m_pPublicKeyInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(
                                LMEM_FIXED, cPublicKeyInfo);
        if (NULL == m_pPublicKeyInfo)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    reqInfo.SubjectPublicKeyInfo = *m_pPublicKeyInfo;

    reqInfo.Subject.pbData = NULL;
    while (TRUE)
    {
        if( !MyCertStrToNameW(
                CRYPT_ASN_ENCODING,
                DNName,
                0 | m_dwT61DNEncoding,
                NULL,
                reqInfo.Subject.pbData,
                &reqInfo.Subject.cbData,
                NULL))
        {
            if (CRYPT_E_INVALID_X500_STRING == GetLastError() &&
                L'\0' == DNName[0])
            {

                reqInfo.Subject.cbData = 2;
                reqInfo.Subject.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                                reqInfo.Subject.cbData);
                if (NULL == reqInfo.Subject.pbData)
                {        
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                reqInfo.Subject.pbData[0] = 0x30;
                reqInfo.Subject.pbData[1] = 0x0;

                break;
            }
            else
            {
               goto ErrorCertStrToNameW;
            }
        }
        if (NULL != reqInfo.Subject.pbData)
        {
            break;
        }
        reqInfo.Subject.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                            reqInfo.Subject.cbData);
        if (NULL == reqInfo.Subject.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    cb = (CountStackExtension(m_fNewRequestMethod) + EndExt) * sizeof(CERT_EXTENSION);
    rgExtension = (PCERT_EXTENSION)LocalAlloc(LMEM_FIXED, cb);
    if (NULL == rgExtension)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    memset(rgExtension, 0, cb);
    cb = 0;

    if (!m_fUseClientKeyUsage)
    {

        rgExtension[iExt].pszObjId = szOID_KEY_USAGE;
        rgExtension[iExt].fCritical = TRUE;

        if( m_keyProvInfo.dwKeySpec == AT_SIGNATURE)
            bKeyUsage =
                CERT_DIGITAL_SIGNATURE_KEY_USAGE |
                CERT_NON_REPUDIATION_KEY_USAGE;

        else if(m_fLimitExchangeKeyToEncipherment)
	    bKeyUsage =
                CERT_KEY_ENCIPHERMENT_KEY_USAGE |
                CERT_DATA_ENCIPHERMENT_KEY_USAGE;

        else 
            bKeyUsage =
                CERT_KEY_ENCIPHERMENT_KEY_USAGE     |
                CERT_DATA_ENCIPHERMENT_KEY_USAGE    |
                CERT_DIGITAL_SIGNATURE_KEY_USAGE    |
                CERT_NON_REPUDIATION_KEY_USAGE;

        bbKeyUsage.pbData = &bKeyUsage;
        bbKeyUsage.cbData = 1;
        bbKeyUsage.cUnusedBits = 1;

        rgExtension[iExt].Value.pbData = NULL;
        while (TRUE)
        {
            if(!CryptEncodeObject(
                    CRYPT_ASN_ENCODING,
                    X509_KEY_USAGE,
                    &bbKeyUsage,
                    pbKU,
                    &rgExtension[iExt].Value.cbData))
            {
                goto ErrorEncodeKeyUsage;
            }
            if (NULL != pbKU)
            {
                rgExtension[iExt].Value.pbData = pbKU;

                break;
            }
            pbKU = (BYTE *)LocalAlloc(LMEM_FIXED, rgExtension[iExt].Value.cbData);
            if (NULL == pbKU)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        iExt++;
    }

    if(m_fEnableSMIMECapabilities)
    {

        rgExtension[iExt].pszObjId = szOID_RSA_SMIMECapabilities;
        rgExtension[iExt].fCritical = FALSE;
        hr = CreateSMimeExtension(
                    hProv,
                    &pbSMime,
                    &rgExtension[iExt].Value.cbData);
        if (S_OK != hr)
        {
            goto CreateSMimeExtensionError;
        }
        rgExtension[iExt].Value.pbData = pbSMime;
        iExt++;
    }

    if (m_fHonorIncludeSubjectKeyID && m_fIncludeSubjectKeyID)
    {
        hr = myCreateSubjectKeyIdentifierExtension(
                    m_pPublicKeyInfo,
                    &pbSubjectKeyHashExtension,
                    &cbSubjectKeyHashExtension);
        if (S_OK != hr)
        {
            goto myCreateSubjectKeyIdentifierExtensionError;
        }

        rgExtension[iExt].pszObjId = szOID_SUBJECT_KEY_IDENTIFIER;
        rgExtension[iExt].fCritical = FALSE;
        rgExtension[iExt].Value.pbData = pbSubjectKeyHashExtension;
        rgExtension[iExt].Value.cbData = cbSubjectKeyHashExtension;
        iExt++;
    }

    if(wszPurpose != NULL) {
        cb = 0;
        while (TRUE)
	{
            if(0 == (cb = WideCharToMultiByte(
                            0, 0, wszPurpose, -1, pszPurpose, cb, NULL, NULL)))
            {
                SetLastError(ERROR_OUTOFMEMORY);
                goto ErrorCantConvertPurpose;
            }
            if (NULL != pszPurpose)
            {
                break;
            }
            pszPurpose = (CHAR*)LocalAlloc(LMEM_FIXED, cb);
            if (NULL == pszPurpose)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        szStart = pszPurpose;

        while(*szStart == ',' || *szStart == ' ')
            *szStart++ = '\0';

        while( szStart[0] != '\0' ) {

            szEnd = szStart;
            while(*szEnd != ',' && *szEnd != ' ' && *szEnd != '\0')
                szEnd++;

            while(*szEnd == ',' || *szEnd == ' ')
                *szEnd++ = '\0';

            enhKeyUsage.cUsageIdentifier++;

            fAddCodeSign |= !strcmp(szStart, SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID) ||
                            !strcmp(szStart, SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID);

            szStart = szEnd;
        }

        cPassedEKU = enhKeyUsage.cUsageIdentifier;
        if(fAddCodeSign)
            enhKeyUsage.cUsageIdentifier++;

        if(enhKeyUsage.cUsageIdentifier != 0) {

            enhKeyUsage.rgpszUsageIdentifier = (LPSTR *)LocalAlloc(LMEM_FIXED,
                            enhKeyUsage.cUsageIdentifier * sizeof(LPSTR));
            if (NULL == enhKeyUsage.rgpszUsageIdentifier)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }

            szStart = pszPurpose;
            for(i=0; i<cPassedEKU; i++) {

                while(*szStart == '\0')
                    szStart++;

                enhKeyUsage.rgpszUsageIdentifier[i] = szStart;

                while(*szStart != '\0')
                    szStart++;

            }

            if(fAddCodeSign)
                enhKeyUsage.rgpszUsageIdentifier[enhKeyUsage.cUsageIdentifier - 1] = szOID_PKIX_KP_CODE_SIGNING;

            rgExtension[iExt].pszObjId = szOID_ENHANCED_KEY_USAGE ;
            rgExtension[iExt].fCritical = FALSE;

            rgExtension[iExt].Value.cbData = 0;
            while (TRUE)
            {
                if(!CryptEncodeObject(
                        CRYPT_ASN_ENCODING, X509_ENHANCED_KEY_USAGE,
                        &enhKeyUsage,
                        pbEKU,           
                        &rgExtension[iExt].Value.cbData))
                {
                    goto ErrorEncodeEnhKeyUsage;
                }
                if (NULL != pbEKU)
                {

                    rgExtension[iExt].Value.pbData = pbEKU;
                    break;
                }
                pbEKU = (BYTE *)LocalAlloc(LMEM_FIXED,
                                           rgExtension[iExt].Value.cbData);
                if (NULL == pbEKU)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
            }
            iExt++;
        }
    }

    assert(EndExt >= iExt);

    pExtCur = NULL;
    while(NULL != (pExtCur =  EnumStackExtension(pExtCur, m_fNewRequestMethod)) ) {
        rgExtension[iExt] = *pExtCur;
        iExt++;
    }

    Extensions.cExtension = iExt;
    Extensions.rgExtension = rgExtension;

    reqInfo.cAttribute = 0;
    reqInfo.rgAttribute = rgAttribute;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING, X509_EXTENSIONS,
                &Extensions,
                blobExt.pbData,           
                &blobExt.cbData))
        {
            goto ErrorEncodeExtensions;
        }
        if (NULL != blobExt.pbData)
        {

            break;
        }
        blobExt.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobExt.cbData);
        if (NULL == blobExt.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if (m_fOID_V2)
    {

        rgAttribute[reqInfo.cAttribute].pszObjId = szOID_RSA_certExtensions;
    }
    else
    {

        rgAttribute[reqInfo.cAttribute].pszObjId = szOID_CERT_EXTENSIONS;
    }
    rgAttribute[reqInfo.cAttribute].cValue = 1;
    rgAttribute[reqInfo.cAttribute].rgValue = &blobExt;

    if( !GetSignatureFromHPROV(
       hProv,
       &CSPProvider.Signature.pbData,
       &CSPProvider.Signature.cbData
       ) )
        goto ErrorGetSignatureFromHPROV;

    CSPProvider.pwszProviderName    = m_keyProvInfo.pwszProvName;
    CSPProvider.dwKeySpec           = m_keyProvInfo.dwKeySpec;

    while (TRUE)
    {
        if( !CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                szOID_ENROLLMENT_CSP_PROVIDER,
                &CSPProvider,
                blobCSPAttr.pbData,           
                &blobCSPAttr.cbData))
        {
            goto ErrorEncodeCSPAttr;
        }
        if (NULL != blobCSPAttr.pbData)
        {

            break;
        }
        blobCSPAttr.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobCSPAttr.cbData);
        if (NULL == blobCSPAttr.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    reqInfo.cAttribute++;
    rgAttribute[reqInfo.cAttribute].pszObjId = szOID_ENROLLMENT_CSP_PROVIDER;
    rgAttribute[reqInfo.cAttribute].cValue = 1;
    rgAttribute[reqInfo.cAttribute].rgValue = &blobCSPAttr;

    osvInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if(!GetVersionExA(&osvInfo))
        goto ErrorGetVersionEx;

    wsprintfA(szVersion, "%d.%d.%d.%d", 
 	      osvInfo.dwMajorVersion,
        osvInfo.dwMinorVersion,
        osvInfo.dwBuildNumber,
        osvInfo.dwPlatformId);

    cnvOSVer.dwValueType = CERT_RDN_IA5_STRING;
    cnvOSVer.Value.cbData = (DWORD)strlen(szVersion);
    cnvOSVer.Value.pbData = (BYTE *) szVersion;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                X509_ANY_STRING,
                &cnvOSVer,
                blobOSVAttr.pbData,           
                &blobOSVAttr.cbData))
        {
            goto ErrorEncodeOSVAttr;
        }
        if (NULL != blobOSVAttr.pbData)
        {

            break;
        }
        blobOSVAttr.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobOSVAttr.cbData);
        if (NULL == blobOSVAttr.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    reqInfo.cAttribute++;
    rgAttribute[reqInfo.cAttribute].pszObjId = szOID_OS_VERSION;
    rgAttribute[reqInfo.cAttribute].cValue = 1;
    rgAttribute[reqInfo.cAttribute].rgValue = &blobOSVAttr;

    if(m_pCertContextRenewal != NULL && m_fHonorRenew) {

        reqInfo.cAttribute++;

        blobRenewAttr.pbData = m_pCertContextRenewal->pbCertEncoded;
        blobRenewAttr.cbData = m_pCertContextRenewal->cbCertEncoded;

        rgAttribute[reqInfo.cAttribute].pszObjId = szOID_RENEWAL_CERTIFICATE;
        rgAttribute[reqInfo.cAttribute].cValue = 1;
        rgAttribute[reqInfo.cAttribute].rgValue = &blobRenewAttr;
    }

    if (m_fNewRequestMethod && XECI_DISABLE != m_lClientId)
    {

        hr = myEncodeRequestClientAttributeFromClientId(
                    m_lClientId,
                    &blobClientId.pbData,
                    &blobClientId.cbData);
        if (S_OK != hr)
        {

            hr = put_ClientId(XECI_DISABLE);
            if (S_OK != hr)
            {
                goto putClientIdError;
            }
        }
        else
        {
            reqInfo.cAttribute++;
            rgAttribute[reqInfo.cAttribute].pszObjId = szOID_REQUEST_CLIENT_INFO;
            rgAttribute[reqInfo.cAttribute].cValue = 1;
            rgAttribute[reqInfo.cAttribute].rgValue = &blobClientId;
        }
    }

    if( !GetCapiHashAndSigAlgId(rgAlg) ) {
        SetLastError((DWORD)NTE_BAD_ALGID);
        goto ErrorGetCapiHashAndSigAlgId;
    }

    if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_SIGN_KEY,
        (void *) rgAlg,
        CRYPT_SIGN_ALG_OID_GROUP_ID)) ) ) {
        SetLastError((DWORD)NTE_BAD_ALGID);
        goto ErrorCryptFindOIDInfo;
    }

    reqInfo.cAttribute++;
    SignatureInfo.SignatureAlgorithm.pszObjId = (char *) pOidInfo->pszOID;
#if DBG

    assert(NULL == SignatureInfo.ToBeSigned.pbData);
#endif

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED,
                &reqInfo,
                SignatureInfo.ToBeSigned.pbData,           
                &SignatureInfo.ToBeSigned.cbData))
        {
            goto ErrorEncodePKCS10ToBeSigned;
        }
        if (NULL != SignatureInfo.ToBeSigned.pbData)
        {

            break;
        }
        SignatureInfo.ToBeSigned.pbData = (BYTE *)
            LocalAlloc(LMEM_FIXED, SignatureInfo.ToBeSigned.cbData);
        if (NULL == SignatureInfo.ToBeSigned.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

#if DBG
    assert(NULL == SignatureInfo.Signature.pbData);
#endif
    while (TRUE)
    {
        if(!CryptSignCertificate(
                hProv,
                m_keyProvInfo.dwKeySpec,
                CRYPT_ASN_ENCODING,
                SignatureInfo.ToBeSigned.pbData,
                SignatureInfo.ToBeSigned.cbData,
                &SignatureInfo.SignatureAlgorithm,
                NULL,                   
                SignatureInfo.Signature.pbData, 
                &SignatureInfo.Signature.cbData))
        {
            goto ErrorCryptSignCertificatePKCS10;
        }
        if (NULL != SignatureInfo.Signature.pbData)
        {

            break;
        }
        SignatureInfo.Signature.pbData = (BYTE *)
            LocalAlloc(LMEM_FIXED, SignatureInfo.Signature.cbData);
        if (NULL == SignatureInfo.Signature.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if( !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_CERT,
            &SignatureInfo,
            NULL,
            &pPkcs10Blob->cbData
            )                               ||
        (pPkcs10Blob->pbData = (BYTE *)
            MyCoTaskMemAlloc(pPkcs10Blob->cbData)) == NULL     ||
        !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_CERT,
            &SignatureInfo,
            pPkcs10Blob->pbData,
            &pPkcs10Blob->cbData
            ) ) {
        goto ErrorEncodePKCS10Request;
    }

    if((m_pCertContextRenewal != NULL ||
        m_pCertContextSigner  != NULL) &&
       m_fHonorRenew &&
       !m_fCMCFormat) 
    {

        if(S_OK != CreatePKCS7RequestFromRequest(
            pPkcs10Blob,
            (NULL != m_pCertContextRenewal) ? m_pCertContextRenewal :
                                              m_pCertContextSigner,
            &blobPKCS7) )
            goto ErrorCreatePKCS7RARequestFromPKCS10;

        assert(pPkcs10Blob->pbData != NULL);
        MyCoTaskMemFree(pPkcs10Blob->pbData);
        *pPkcs10Blob = blobPKCS7;
        memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));

    }

    ssFlags = CERT_CREATE_SELFSIGN_NO_SIGN;
    if(m_wszPVKFileName[0] != 0)
        ssFlags |= CERT_CREATE_SELFSIGN_NO_KEY_INFO;

    {
        PCERT_EXTENSION pCertExtCertTypeName = NULL; 
        while(NULL != (pCertExtCertTypeName =  EnumStackExtension(pCertExtCertTypeName, m_fNewRequestMethod)) ) {
            for (DWORD dTmp = 0; dTmp < sizeof(rgszExtensionOIDs) / sizeof(LPSTR); dTmp++) { 
                if (0 == strcmp(rgszExtensionOIDs[dTmp], pCertExtCertTypeName->pszObjId))
                    rgNeededExtensions[(ceExtensions.cExtension)++] = *pCertExtCertTypeName; 
            }
        }

    }

    assert(pCertContext == NULL);
    pCertContext = MyCertCreateSelfSignCertificate(
        hProv,
        &reqInfo.Subject,
        ssFlags,
        &m_keyProvInfo,
        NULL,
        NULL,
        NULL,
        (ceExtensions.cExtension > 0) ? &ceExtensions : NULL
        );
    if (NULL == pCertContext)
        goto ErrorCertCreateSelfSignCertificate;

    requestFlags.fWriteToCSP    =   (m_fWriteCertToCSP != 0);
    requestFlags.fWriteToDS     =   (m_fWriteCertToUserDS != 0);
    requestFlags.openFlags      =   m_RequestStore.dwFlags;

#if DBG
    assert(NULL == requestInfoBlob.pbData);
#endif
    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                XENROLL_REQUEST_INFO,
                &requestFlags,
                requestInfoBlob.pbData,
                &requestInfoBlob.cbData))
        {
            goto ErrorEncodeRequestInfoBlob;
        }
        if (NULL != requestInfoBlob.pbData)
        {

            break;
        }
        requestInfoBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, requestInfoBlob.cbData);
        if (NULL == requestInfoBlob.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if( !CertSetCertificateContextProperty(
            pCertContext,
            XENROLL_PASS_THRU_PROP_ID,
            0,
            &requestInfoBlob) )
        goto ErrorCertSetCertificateContextProperty;

    if(m_pCertContextRenewal != NULL && m_fHonorRenew) {

        blobRenewalCert.pbData = m_pCertContextRenewal->pbCertEncoded;
        blobRenewalCert.cbData = m_pCertContextRenewal->cbCertEncoded;

        if( !CertSetCertificateContextProperty(
                pCertContext,
                XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
                0,
                &blobRenewalCert) )
            goto ErrorCertSetCertificateContextProperty;
    }

    if(m_wszPVKFileName[0] != 0) {

        if( (hFile = CreateOpenFileSafely2(m_wszPVKFileName, IDS_PVK_C, IDS_PVK_O)) == NULL )
            goto ErrorCreatePVKFile;

        assert(m_keyProvInfo.dwKeySpec == AT_SIGNATURE || m_keyProvInfo.dwKeySpec == AT_KEYEXCHANGE);

        if( !PrivateKeySave(
            hProv,
            hFile,
            m_keyProvInfo.dwKeySpec,
            NULL,
            m_wszPVKFileName,
            0
            ) )  {
            goto ErrorPrivateKeySave;
        }

        keyProvInfoT = m_keyProvInfo;
        keyProvInfoT.pwszContainerName = m_wszPVKFileName;
        if( !CreatePvkProperty(&keyProvInfoT, &blobData) )
            goto ErrorCreatePvkProperty;

        CertSetCertificateContextProperty(
                pCertContext,
                CERT_PVK_FILE_PROP_ID,
                0,
                &blobData);

        if (!m_fNewRequestMethod)
        {

            if(!m_fUseExistingKey)
                GetProv(CRYPT_DELETEKEYSET);
        }
    }

    pProp = EnumStackProperty(NULL);
    while (NULL != pProp)
    {

        if (!CertSetCertificateContextProperty(
                        pCertContext,
                        pProp->lPropId,
                        0,
                        &pProp->prop))
        {
            goto ErrorCertSetCertificateContextProperty;
        }
        pProp = EnumStackProperty(pProp);
    }

    if( (hStore = GetStore(StoreREQUEST)) == NULL)
        goto ErrorCertOpenRequestStore;

    fRet = CertFreeCertificateContext(m_pCertContextPendingRequest);
#if DBG
    assert(fRet);
#endif 
    m_pCertContextPendingRequest = NULL;

    if( !MySafeCertAddCertificateContextToStore(
            hStore,
            pCertContext,
            CERT_STORE_ADD_NEW,
            &m_pCertContextPendingRequest, 
	    m_dwEnabledSafteyOptions) ) {
        goto ErrorCertAddToRequestStore;
    }

    if (m_hashBlobPendingRequest.pbData != NULL)
    {
        LocalFree(m_hashBlobPendingRequest.pbData);
        m_hashBlobPendingRequest.pbData = NULL;
    }

CommonReturn:

    if(pCertContext != NULL)
        CertFreeCertificateContext(pCertContext);
    if(hFile != NULL)
        CloseHandle(hFile);
    if(blobData.pbData != NULL)
        MyCoTaskMemFree(blobData.pbData);
    if(blobPKCS7.pbData != NULL)
        MyCoTaskMemFree(blobPKCS7.pbData);
    if(CSPProvider.Signature.pbData)
        LocalFree(CSPProvider.Signature.pbData);
    if (NULL != pbSMime)
    {
        LocalFree(pbSMime);
    }
    if (NULL != reqInfo.Subject.pbData)
    {
        LocalFree(reqInfo.Subject.pbData);
    }
    if (NULL != rgExtension)
    {
        LocalFree(rgExtension);
    }
    if (NULL != pbKU)
    {
        LocalFree(pbKU);
    }
    if (NULL != pbEKU)
    {
        LocalFree(pbEKU);
    }
    if (NULL != pszPurpose)
    {
        LocalFree(pszPurpose);
    }
    if (NULL != enhKeyUsage.rgpszUsageIdentifier)
    {
        LocalFree(enhKeyUsage.rgpszUsageIdentifier);
    }
    if (NULL != blobExt.pbData)
    {
        LocalFree(blobExt.pbData);
    }
    if (NULL != blobCSPAttr.pbData)
    {
        LocalFree(blobCSPAttr.pbData);
    }
    if (NULL != blobOSVAttr.pbData)
    {
        LocalFree(blobOSVAttr.pbData);
    }
    if (NULL != SignatureInfo.ToBeSigned.pbData)
    {
        LocalFree(SignatureInfo.ToBeSigned.pbData);
    }
    if (NULL != SignatureInfo.Signature.pbData)
    {
        LocalFree(SignatureInfo.Signature.pbData);
    }
    if (NULL != requestInfoBlob.pbData)
    {
        LocalFree(requestInfoBlob.pbData);
    }
    if (NULL != blobClientId.pbData)
    {
        LocalFree(blobClientId.pbData);
    }
    if (NULL != pbSubjectKeyHashExtension)
    {
        LocalFree(pbSubjectKeyHashExtension);
    }
    if (NULL != pwszNotSafeRequesting)
    {
	LocalFree(pwszNotSafeRequesting);
    }
    if (NULL != pwszTitle)
    {
	LocalFree(pwszTitle);
    }

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    if(pPkcs10Blob->pbData != NULL)
        MyCoTaskMemFree(pPkcs10Blob->pbData);
    memset(pPkcs10Blob, 0, sizeof(CRYPT_DATA_BLOB));

    goto CommonReturn;

TRACE_ERROR(ErrorGetSignatureFromHPROV);
TRACE_ERROR(ErrorEncodeCSPAttr);
TRACE_ERROR(ErrorCertSetCertificateContextProperty);
TRACE_ERROR(ErrorCryptAcquireContext);
TRACE_ERROR(ErrorCryptGenKey);
TRACE_ERROR(ErrorCryptExportPublicKeyInfo);
TRACE_ERROR(ErrorCertStrToNameW);
TRACE_ERROR(ErrorEncodeKeyUsage);
TRACE_ERROR(ErrorEncodeEnhKeyUsage);
TRACE_ERROR(ErrorEncodeExtensions);
TRACE_ERROR(ErrorEncodePKCS10ToBeSigned);
TRACE_ERROR(ErrorCryptSignCertificatePKCS10);
TRACE_ERROR(ErrorEncodePKCS10Request);
TRACE_ERROR(ErrorCantConvertPurpose);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorCertAddToRequestStore);
TRACE_ERROR(ErrorCreatePVKFile);
TRACE_ERROR(ErrorPrivateKeySave);
TRACE_ERROR(ErrorCreatePvkProperty);
TRACE_ERROR(ErrorCertCreateSelfSignCertificate);
TRACE_ERROR(ErrorEncodeRequestInfoBlob);
TRACE_ERROR(ErrorCreatePKCS7RARequestFromPKCS10);
TRACE_ERROR(ErrorGetCapiHashAndSigAlgId);
TRACE_ERROR(ErrorCryptFindOIDInfo);
TRACE_ERROR(ErrorEncodeOSVAttr);
TRACE_ERROR(ErrorGetVersionEx);
TRACE_ERROR(CancelledError);
TRACE_ERROR(CreateSMimeExtensionError);
TRACE_ERROR(OutOfMemoryError);
TRACE_ERROR(putClientIdError);
TRACE_ERROR(myCreateSubjectKeyIdentifierExtensionError)
TRACE_ERROR(xeLoadRCStringError);
TRACE_ERROR(CryptGetUserKeyCancelError)
#if DBG
TRACE_ERROR(SetKeyContainerSecurityError)
#endif 
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptPKCS7Blob(
     PCRYPT_DATA_BLOB pBlobPKCS7) {

    HRESULT     hr;
    HRESULT     hr2 = S_OK;
    LONG                       dwKeySpec               = 0;
    PCCERT_CONTEXT              pCertContextMy          = NULL;
    PCCERT_CONTEXT              pCertContextRequest     = NULL;
    PCCERT_CONTEXT              pCertContextEnd         = NULL;
    HANDLE                      hFile                   = NULL;
    DWORD                       cb                      = 0;
    HCRYPTPROV                  hProv                   = NULL;
    HCRYPTKEY                   hKey                    = NULL;
    HCERTSTORE                  hStoreDS                = NULL;
    HCERTSTORE                  hStoreRequest           = NULL;
    HCERTSTORE                  hStoreMy                = NULL;
    LPWSTR                      pwszTitle               = NULL; 
    LPWSTR                      pwszNotSafeAccepting    = NULL; 

    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) { 
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
	if (S_OK != hr)
	    goto xeLoadRCStringError;

	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_ACCEPTING_CERT, &pwszNotSafeAccepting);
	if (S_OK != hr)
	    goto xeLoadRCStringError;

	if (IDYES != MessageBoxU(NULL, pwszNotSafeAccepting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) { 
	    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
	    goto CancelledError; 
	}
    }

    hr2 = GetEndEntityCert(pBlobPKCS7, TRUE, &pCertContextEnd);
    if (S_OK != hr2 && XENROLL_E_CANNOT_ADD_ROOT_CERT != hr2)
    {
        hr = hr2;
        goto ErrorGetEndEntityCert;
    }

    if(m_fDeleteRequestCert)
    {
        if ((hStoreRequest = GetStore(StoreREQUEST)) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenRequestStore;
        }

        if ((pCertContextRequest = CertFindCertificateInStore(
                hStoreRequest,
                CRYPT_ASN_ENCODING,
                0,
                CERT_FIND_PUBLIC_KEY,
                (void *) &pCertContextEnd->pCertInfo->SubjectPublicKeyInfo,
                NULL)) != NULL)
        {
            CertDeleteCertificateFromStore(pCertContextRequest);
            pCertContextRequest = NULL;
        }
    }

    cb = 0;

    if (m_fWriteCertToCSP  &&
        CertGetCertificateContextProperty(
            pCertContextEnd,
            CERT_KEY_PROV_INFO_PROP_ID, NULL, &cb))
    {
        if ((hProv = GetProv(0)) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCryptAcquireContext;
        }

        get_KeySpec(&dwKeySpec);

        if (!CryptGetUserKey(
                hProv,
                dwKeySpec,
                &hKey))
        {
            hKey = NULL;
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCryptGetUserKey;
        }

        if (!CryptSetKeyParam(
                hKey,
                KP_CERTIFICATE,
                pCertContextEnd->pbCertEncoded,
                0))
        {

            if (SCODE_FACILITY(GetLastError()) == FACILITY_SCARD)
            {

                hr = MY_HRESULT_FROM_WIN32(GetLastError());

                if ((hStoreMy = GetStore(StoreMY)) == NULL)
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertOpenMyStore;
                }

                if ((pCertContextMy = CertFindCertificateInStore(
                        hStoreMy,
                        CRYPT_ASN_ENCODING,
                        0,
                        CERT_FIND_PUBLIC_KEY,
                        (void *) &pCertContextEnd->pCertInfo->SubjectPublicKeyInfo,
                        NULL)) != NULL)
                {

                    CertDeleteCertificateFromStore(pCertContextMy);
                    pCertContextMy = NULL;
                }
                if (!m_fUseExistingKey)
                {
                        GetProv(CRYPT_DELETEKEYSET);
                }

                goto ErrorWriteToCSP;
            }
        }
    }

    if(m_fWriteCertToUserDS)
    {

        if ((hStoreDS = CertOpenStore(
                CERT_STORE_PROV_SYSTEM,
                X509_ASN_ENCODING,
                NULL,
                CERT_SYSTEM_STORE_CURRENT_USER,
                L"UserDS")) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenDSStore;
        }

        if (!CertAddCertificateContextToStore(
                    hStoreDS,
                    pCertContextEnd,
                    CERT_STORE_ADD_NEW,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorAddCertificateContextToDSStore;
        }

        CertCloseStore(hStoreDS, 0);
        hStoreDS = NULL;

    }

    if (m_wszSPCFileName[0] != 0)
    {

        hFile = CreateOpenFileSafely2(m_wszSPCFileName, IDS_SPC_C, IDS_SPC_O);
        if (NULL == hFile)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCreateSPCFile;
        }

        assert(pBlobPKCS7->pbData != NULL);
        cb = 0;
        if (!WriteFile(
            hFile,
            pBlobPKCS7->pbData,
            pBlobPKCS7->cbData,
            &cb,
            NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorWriteSPCFile;
        }
    }

    if (S_OK != hr2)
    {

        hr = hr2;
    }
    else
    {
        hr = S_OK;
    }
ErrorReturn:

    if(hKey != NULL)
        CryptDestroyKey(hKey);
    if(hFile != NULL)
        CloseHandle(hFile);

    if(pCertContextEnd != NULL)
        CertFreeCertificateContext(pCertContextEnd);
    if(hStoreDS != NULL)
        CertCloseStore(hStoreDS, 0);
    if (NULL != pwszNotSafeAccepting)
	LocalFree(pwszNotSafeAccepting);
    if (NULL != pwszTitle)
	LocalFree(pwszTitle);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(ErrorWriteToCSP);
TRACE_ERROR(ErrorCreateSPCFile);
TRACE_ERROR(ErrorWriteSPCFile);
TRACE_ERROR(ErrorGetEndEntityCert);
TRACE_ERROR(ErrorCryptAcquireContext);
TRACE_ERROR(ErrorCryptGetUserKey);
TRACE_ERROR(ErrorCertOpenDSStore);
TRACE_ERROR(ErrorAddCertificateContextToDSStore);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorCertOpenMyStore);
TRACE_ERROR(CancelledError);
TRACE_ERROR(xeLoadRCStringError);
}

PCCERT_CONTEXT STDMETHODCALLTYPE CCEnroll::getCertContextFromPKCS7(
     PCRYPT_DATA_BLOB pBlobPKCS7) {
    HRESULT hr;
    PCCERT_CONTEXT pCert;

    hr = GetEndEntityCert(pBlobPKCS7, FALSE, &pCert);
#if DBG
    if (S_OK != hr)
    {
        assert(NULL == pCert);
    }
#endif 
    return pCert;
}

HRESULT STDMETHODCALLTYPE CCEnroll::enumProvidersWStr(
     LONG dwIndex,
     LONG dwFlags,
     LPWSTR __RPC_FAR *ppwsz) {

    DWORD   iLast = 0;
    LONG    i;
    DWORD   dwProvType = 0;
    DWORD   cb = 0;
    HRESULT hr = S_OK;
    DWORD errBefore = GetLastError();

    assert(ppwsz != NULL);
    *ppwsz = NULL;
    SetLastError(ERROR_SUCCESS);

    EnterCriticalSection(&m_csXEnroll);

    for(i=0; i<=dwIndex; i++) {

        do {

            cb = 0;
            if( !CryptEnumProvidersU(
                           iLast,
                           0,
                           0,
                           &dwProvType,
                           NULL,
                           &cb
                           ) ) {

                if( GetLastError() != NTE_PROV_TYPE_ENTRY_BAD)
                    goto ErrorCryptEnumProvidersU;
            }
            iLast++;
        } while((CRYPT_ENUM_ALL_PROVIDERS & dwFlags) != CRYPT_ENUM_ALL_PROVIDERS  &&
                    dwProvType != m_keyProvInfo.dwProvType);
    }

    iLast--;
    if( (*ppwsz = (LPWSTR) MyCoTaskMemAlloc(cb)) == NULL  ||
        !CryptEnumProvidersU(
                       iLast,
                       0,
                       0,
                       &dwProvType,
                       *ppwsz,
                       &cb
                       )                        ) {
        goto ErrorCryptEnumProvidersU;
    }

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    if(*ppwsz != NULL)
    {
        MyCoTaskMemFree(*ppwsz);
	*ppwsz = NULL;
    }

    goto CommonReturn;

TRACE_ERROR(ErrorCryptEnumProvidersU);
}

HRESULT STDMETHODCALLTYPE CCEnroll::enumContainersWStr(
     LONG               dwIndex,
     LPWSTR __RPC_FAR *ppwsz) {

    DWORD       errBefore   = GetLastError();
    DWORD       cb          = 0;
    LONG        i           = 0;
    char *      psz         = NULL;
    HRESULT     hr          = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    SetLastError(ERROR_SUCCESS);

    assert(ppwsz != NULL);
    *ppwsz = NULL;

    if (0 != m_dwEnabledSafteyOptions) 
	goto AccessDeniedError;

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    while (TRUE)
    {
        if(!CryptGetProvParam(
                m_hVerifyProv,
                PP_ENUMCONTAINERS,
                (BYTE*)psz,
                &cb,
                CRYPT_FIRST))
        {
            goto ErrorCryptGetProvParam;
        }
        if (NULL != psz)
        {

            break;
        }
        psz = (char*)LocalAlloc(LMEM_FIXED, cb);
        if (NULL == psz)
        {
            goto ErrorOutOfMem;
        }
    }

    for(i=1; i<=dwIndex; i++) {

        if( !CryptGetProvParam(
            m_hVerifyProv,
            PP_ENUMCONTAINERS,
            (BYTE *) psz,
            &cb,
            0) )
            goto ErrorCryptGetProvParam;
    }

    if( (*ppwsz = WideFromMB(psz)) == NULL )
        goto ErrorOutOfMem;

CommonReturn:
    if (NULL != psz)
    {
        LocalFree(psz);
    }

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    if(*ppwsz != NULL)
        MyCoTaskMemFree(*ppwsz);
    *ppwsz = NULL;

    goto CommonReturn;

SET_ERROR(AccessDeniedError, E_ACCESSDENIED);
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(ErrorOutOfMem);
}

HRESULT CCEnroll::PKCS10ToCert(IN   HCERTSTORE        hCertStore,
                               IN   CRYPT_DATA_BLOB   pkcs10Blob, 
                               OUT  PCCERT_CONTEXT   *ppCertContext)
{
    HRESULT            hr       = E_FAIL; 
    PCERT_REQUEST_INFO pReqInfo = NULL; 

    if (NULL == hCertStore || NULL == pkcs10Blob.pbData || NULL == ppCertContext)
        return E_INVALIDARG;

    if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                            &pkcs10Blob, 
                            CERT_QUERY_CONTENT_FLAG_PKCS10,
                            CERT_QUERY_FORMAT_FLAG_ALL,
                            CRYPT_DECODE_ALLOC_FLAG,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            (const void **) &pReqInfo) )
        goto MyCryptQueryObjectError;

    if ( NULL == (*ppCertContext = CertFindCertificateInStore
                  (hCertStore,
                   CRYPT_ASN_ENCODING, 
                   0, 
                   CERT_FIND_PUBLIC_KEY, 
                   (void *) &pReqInfo->SubjectPublicKeyInfo, 
                   NULL)) )
        goto CertFindCertificateInStoreError;

    hr = S_OK; 

CommonReturn:
    if (NULL != pReqInfo) { LocalFree(pReqInfo); } 
    return hr; 

 ErrorReturn:
    goto CommonReturn; 

SET_HRESULT(CertFindCertificateInStoreError,  MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MyCryptQueryObjectError,          MY_HRESULT_FROM_WIN32(GetLastError())); 
}

HRESULT CCEnroll::PKCS7ToCert(IN  HCERTSTORE       hCertStore,
                              IN  CRYPT_DATA_BLOB  pkcs7Blob, 
                              OUT PCCERT_CONTEXT  *ppCertContext)
{
    CRYPT_DATA_BLOB            pkcs10Blob;
    CRYPT_VERIFY_MESSAGE_PARA  VerifyPara; 
    HRESULT                    hr           = E_FAIL; 

    ZeroMemory(&pkcs10Blob, sizeof(pkcs10Blob)); 
    ZeroMemory(&VerifyPara, sizeof(VerifyPara)); 

    VerifyPara.cbSize                   = sizeof(VerifyPara); 
    VerifyPara.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; 

    if (!MyCryptVerifyMessageSignature
        (&VerifyPara,
         0,                  
         pkcs7Blob.pbData,
         pkcs7Blob.cbData,
         pkcs10Blob.pbData,
         &(pkcs10Blob.cbData),
         NULL                
         ) || 0 == pkcs10Blob.cbData)
        goto MyCryptVerifyMessageSignatureError;

    if (NULL == (pkcs10Blob.pbData = (PBYTE)LocalAlloc(LPTR, pkcs10Blob.cbData)))
        goto MemoryError; 

    if (!MyCryptVerifyMessageSignature
        (&VerifyPara,
         0,                  
         pkcs7Blob.pbData,
         pkcs7Blob.cbData,
         pkcs10Blob.pbData,
         &pkcs10Blob.cbData,
         NULL                
         ))
        goto MyCryptVerifyMessageSignatureError;

    hr = this->PKCS10ToCert(hCertStore, pkcs10Blob, ppCertContext); 

CommonReturn:
    if (NULL != pkcs10Blob.pbData) { LocalFree(pkcs10Blob.pbData); } 
    return hr; 

ErrorReturn:
    goto CommonReturn;

SET_HRESULT(MemoryError,                        E_OUTOFMEMORY); 
SET_HRESULT(MyCryptVerifyMessageSignatureError, MY_HRESULT_FROM_WIN32(GetLastError()));
}

HRESULT STDMETHODCALLTYPE CCEnroll::freeRequestInfoBlob(
     CRYPT_DATA_BLOB pkcs7OrPkcs10) {

    DWORD            dwContentType   = NULL;
    HCERTSTORE       hStoreRequest   = NULL;
    HRESULT          hr              = E_FAIL;
    PCCERT_CONTEXT   pCertContext    = NULL; 

    if (!m_fDeleteRequestCert)
        return S_OK; 

    if (NULL == pkcs7OrPkcs10.pbData)
        return E_INVALIDARG;

    EnterCriticalSection(&m_csXEnroll);

    if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                            &pkcs7OrPkcs10,
                            (CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | CERT_QUERY_CONTENT_FLAG_PKCS10),
                            CERT_QUERY_FORMAT_FLAG_ALL,
                            0,
                            NULL,
                            &dwContentType,  
                            NULL,
                            NULL, 
                            NULL,
                            NULL) )
        goto MyCryptQueryObjectError;

    if (NULL == (hStoreRequest = GetStore(StoreREQUEST)))
        goto UnexpectedError; 

    switch (dwContentType) 
    {
    case CERT_QUERY_CONTENT_PKCS7_SIGNED:
        hr = this->PKCS7ToCert(hStoreRequest, pkcs7OrPkcs10, &pCertContext);
        if (S_OK != hr)
        {
            if (CRYPT_E_NOT_FOUND == hr)
            {

                if (NULL != m_pCertContextPendingRequest)
                {

                    pCertContext = CertDuplicateCertificateContext(
                                        m_pCertContextPendingRequest);
                    if (NULL == pCertContext)
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto CertDuplicateCertificateContextError;
                    }
                }
                else if (NULL != m_hashBlobPendingRequest.pbData &&
                         0 < m_hashBlobPendingRequest.cbData)
                {

                    pCertContext = CertFindCertificateInStore(
                                hStoreRequest,  
                                X509_ASN_ENCODING,
                                0,
                                CERT_FIND_HASH,
                                &m_hashBlobPendingRequest,
                                NULL);
                    if (NULL == pCertContext)
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto CertFindCertificateInStoreError;
                    }
                }
                else
                {

                    goto PKCS7ToCertError; 
                }
            }
            else
            {

                goto PKCS7ToCertError; 
            }
        }
        break;
    case CERT_QUERY_CONTENT_PKCS10:
        if (S_OK != (hr = this->PKCS10ToCert(hStoreRequest, pkcs7OrPkcs10, &pCertContext)))
            goto PKCS10ToCertError; 
        break;
    default:
        goto InvalidContentTypeError; 
    }

    if (!CertDeleteCertificateFromStore(pCertContext))
    {

        pCertContext = NULL; 
        goto CertDeleteCertificateFromStoreError; 
    }

    hr = S_OK; 
CommonReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

ErrorReturn:
    if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); } 
    goto CommonReturn;

SET_HRESULT(CertDeleteCertificateFromStoreError,  MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(PKCS7ToCertError,                     hr);
SET_HRESULT(PKCS10ToCertError,                    hr);
SET_HRESULT(InvalidContentTypeError,              E_INVALIDARG);
SET_HRESULT(MyCryptQueryObjectError,              MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(UnexpectedError,                      E_UNEXPECTED);
TRACE_ERROR(CertDuplicateCertificateContextError)
TRACE_ERROR(CertFindCertificateInStoreError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_SPCFileNameWStr(
     LPWSTR __RPC_FAR *szw) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szw = CopyWideString(m_wszSPCFileName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_SPCFileNameWStr(
     LPWSTR pwsz) {

    HRESULT hr = S_OK;

    if(pwsz == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));

    EnterCriticalSection(&m_csXEnroll);

    if( m_wszSPCFileName != wszEmpty)
        MyCoTaskMemFree(m_wszSPCFileName);
    if( (m_wszSPCFileName = CopyWideString(pwsz)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_PVKFileNameWStr(
     LPWSTR __RPC_FAR *szw) {

    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if( (*szw = CopyWideString(m_wszPVKFileName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_PVKFileNameWStr(
     LPWSTR pwsz) {

    HRESULT hr = S_OK;

    if(pwsz == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));

    EnterCriticalSection(&m_csXEnroll);

    if( m_wszPVKFileName != wszEmpty)
        MyCoTaskMemFree(m_wszPVKFileName);
    if( (m_wszPVKFileName = CopyWideString(pwsz)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    else
        m_dwGenKeyFlags |= CRYPT_EXPORTABLE; 

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
  }

HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgorithmWStr(
     LPWSTR __RPC_FAR *ppwsz) {

    PCCRYPT_OID_INFO            pOidInfo        = NULL;
    ALG_ID                      rgAlg[2];
    HRESULT                     hr              = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    assert(ppwsz != NULL);
    *ppwsz  = NULL;

    if( !GetCapiHashAndSigAlgId(rgAlg) )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    else if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_ALGID_KEY,
        (void *) &rgAlg[0],
        CRYPT_HASH_ALG_OID_GROUP_ID)) ) ) {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

    else if( (*ppwsz = WideFromMB(pOidInfo->pszOID)) == NULL)
            hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgorithmWStr(
     LPWSTR pwsz) {

    HRESULT             hr          = S_OK;
    char *              szObjId     = NULL;
    PCCRYPT_OID_INFO    pOidInfo    = NULL;

    if(pwsz == NULL) {
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
    }

    if(!_wcsicmp(L"SHA1", pwsz))
        szObjId = CopyAsciiString(szOID_OIWSEC_sha1);
    else if(!_wcsicmp(L"MD5", pwsz))
        szObjId = CopyAsciiString(szOID_RSA_MD5RSA);
    else if(!_wcsicmp(L"MD2", pwsz))
        szObjId = CopyAsciiString(szOID_RSA_MD2RSA);
    else
        szObjId = MBFromWide(pwsz);

    if(szObjId == NULL)
        return(MY_HRESULT_FROM_WIN32(GetLastError()));

    if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_OID_KEY,
        szObjId,
        0)) ) )
    {

        hr=NTE_BAD_ALGID;
    }

    assert(szObjId != NULL);
    MyCoTaskMemFree(szObjId);

    EnterCriticalSection(&m_csXEnroll);

    if(hr == S_OK) {
        if( pOidInfo->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID ||
            pOidInfo->dwGroupId == CRYPT_SIGN_ALG_OID_GROUP_ID )
            m_HashAlgId = pOidInfo->Algid;
        else
            hr = CRYPT_E_NOT_FOUND;
    }

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgID(
    LONG    hashAlgID
    ) {

    EnterCriticalSection(&m_csXEnroll);
    m_HashAlgId = hashAlgID;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgID(
    LONG *   hashAlgID
    ) {
    EnterCriticalSection(&m_csXEnroll);
    *hashAlgID = m_HashAlgId;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RenewalCertificate(
             PCCERT_CONTEXT __RPC_FAR *ppCertContext) {

    HRESULT     hr      = S_OK;

    *ppCertContext = NULL;

    if( m_pCertContextRenewal == NULL)
        return(MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND));

    EnterCriticalSection(&m_csXEnroll);
    if( NULL == (*ppCertContext = CertDuplicateCertificateContext(m_pCertContextRenewal)) )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RenewalCertificate(
             PCCERT_CONTEXT pCertContext)
{
    HRESULT  hr;
    PCCERT_CONTEXT  pGoodCertContext= NULL;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetGoodCertContext(pCertContext, &pGoodCertContext);
    if (S_OK != hr)
    {
        goto GetGoodCertContextError;
    }

    if(m_pCertContextRenewal != NULL)
    {
        CertFreeCertificateContext(m_pCertContextRenewal);
    }
    m_pCertContextRenewal = pGoodCertContext;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(GetGoodCertContextError);
}

BOOL
CCEnroll::CopyAndPushStackExtension(
    PCERT_EXTENSION pExt,
    BOOL            fCMC)
{

    DWORD       cb              = 0;
    DWORD       cbOid           = 0;
    PEXT_STACK  pExtStackEle    = NULL;
    PBYTE       pb              = NULL;
    PEXT_STACK  *ppExtStack = NULL;
    DWORD       *pcExtStack = NULL;

    assert(pExt != NULL);

    cbOid = POINTERROUND((DWORD)strlen(pExt->pszObjId) + 1); 
    cb = sizeof(EXT_STACK) + cbOid + pExt->Value.cbData;
    if(NULL == (pb = (PBYTE) malloc(cb))) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(FALSE);
    }

    pExtStackEle = (PEXT_STACK) pb;
    pb += sizeof(EXT_STACK);
    pExtStackEle->ext.pszObjId = (LPSTR) pb;
    pb += cbOid;
    pExtStackEle->ext.Value.pbData = pb;

    strcpy(pExtStackEle->ext.pszObjId, pExt->pszObjId);
    pExtStackEle->ext.fCritical     = pExt->fCritical;
    pExtStackEle->ext.Value.cbData  = pExt->Value.cbData;
    memcpy(pExtStackEle->ext.Value.pbData, pExt->Value.pbData, pExt->Value.cbData);

    EnterCriticalSection(&m_csXEnroll);

    ppExtStack = fCMC ? &m_pExtStackNew : &m_pExtStack;
    pcExtStack = fCMC ? &m_cExtStackNew : &m_cExtStack;
    pExtStackEle->pNext = *ppExtStack;
    *ppExtStack = pExtStackEle;
    (*pcExtStack)++;

    LeaveCriticalSection(&m_csXEnroll);

    return(TRUE);
}

PCERT_EXTENSION
CCEnroll::PopStackExtension(
    BOOL fCMC)
{

    PEXT_STACK  pExtStackEle = NULL;
    PEXT_STACK *ppExtStack = NULL;
    DWORD      *pcExtStack = NULL;

    EnterCriticalSection(&m_csXEnroll);

    ppExtStack = fCMC ? &m_pExtStackNew : &m_pExtStack;
    if(NULL != *ppExtStack)
    {
        pExtStackEle = *ppExtStack;
        *ppExtStack = (*ppExtStack)->pNext;
        pcExtStack = fCMC ? &m_cExtStackNew : &m_cExtStack;
        (*pcExtStack)--;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCERT_EXTENSION) pExtStackEle);
}

DWORD
CCEnroll::CountStackExtension(BOOL fCMC)
{
    DWORD   cExt = 0;

    EnterCriticalSection(&m_csXEnroll);
    cExt = fCMC ? m_cExtStackNew : m_cExtStack;
    LeaveCriticalSection(&m_csXEnroll);

    return(cExt);
}

PCERT_EXTENSION
CCEnroll::EnumStackExtension(
    PCERT_EXTENSION pExtLast,
    BOOL            fCMC)
{
    PEXT_STACK pExtStackEle    = (PEXT_STACK)pExtLast;

    EnterCriticalSection(&m_csXEnroll);

    if(NULL == pExtStackEle)
    {
        pExtStackEle = fCMC ? m_pExtStackNew : m_pExtStack;
    }
    else
    {
        pExtStackEle = pExtStackEle->pNext;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCERT_EXTENSION) pExtStackEle);
}

void
CCEnroll::FreeAllStackExtension(void)
{
    EnterCriticalSection(&m_csXEnroll);

    while(0 != m_cExtStackNew)
    {
        FreeStackExtension(PopStackExtension(TRUE));
    }

    while(0 != m_cExtStack)
    {
        FreeStackExtension(PopStackExtension(FALSE));
    }

    LeaveCriticalSection(&m_csXEnroll);
}

void CCEnroll::FreeStackExtension(PCERT_EXTENSION pExt) {
    if(pExt != NULL)
        free(pExt);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::AddExtensionsToRequest(
     PCERT_EXTENSIONS pCertExtensions)
{

    HRESULT hr  = S_OK;
    DWORD   i   = 0;

    assert(pCertExtensions != NULL);

    for(i = 0; i < pCertExtensions->cExtension; i++)
    {

        if(!CopyAndPushStackExtension(&pCertExtensions->rgExtension[i], FALSE))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            break;
        }
    }

    return(hr);
}

BOOL
CCEnroll::CopyAndPushStackAttribute(
    PCRYPT_ATTRIBUTE pAttr,
    BOOL             fCMC)
{
    DWORD       i               = 0;
    DWORD       cb              = 0;
    DWORD       cbOid           = 0;
    PATTR_STACK pAttrStackEle   = NULL;
    PBYTE       pb              = NULL;
    PATTR_STACK *ppAttrStack = NULL;
    DWORD       *pcAttrStack = NULL;

    assert(pAttr != NULL);

    cb = sizeof(ATTR_STACK);

    cbOid = POINTERROUND((DWORD)strlen(pAttr->pszObjId) + 1);
    cb += cbOid;
    cb += sizeof(CRYPT_ATTR_BLOB) * pAttr->cValue;
    for(i=0; i<pAttr->cValue; i++)
        cb += POINTERROUND(pAttr->rgValue[i].cbData); 

    if(NULL == (pb = (PBYTE) malloc(cb))) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(FALSE);
    }

    pAttrStackEle = (PATTR_STACK) pb;
    pb += sizeof(ATTR_STACK);
    pAttrStackEle->attr.pszObjId = (LPSTR) pb;
    pb += cbOid;
    strcpy(pAttrStackEle->attr.pszObjId, pAttr->pszObjId);

    pAttrStackEle->attr.cValue = pAttr->cValue;
    pAttrStackEle->attr.rgValue = (PCRYPT_ATTR_BLOB) pb;
    pb += sizeof(CRYPT_ATTR_BLOB) * pAttr->cValue;
    for(i=0; i<pAttr->cValue; i++) {
        pAttrStackEle->attr.rgValue[i].pbData = pb;
        pAttrStackEle->attr.rgValue[i].cbData = pAttr->rgValue[i].cbData;
        memcpy(pAttrStackEle->attr.rgValue[i].pbData, pAttr->rgValue[i].pbData, pAttr->rgValue[i].cbData);
        pb += POINTERROUND(pAttr->rgValue[i].cbData);
    }
    assert( pb == ((BYTE *) pAttrStackEle) + cb );

    EnterCriticalSection(&m_csXEnroll);

    ppAttrStack = fCMC ? &m_pAttrStackNew : &m_pAttrStack;
    pcAttrStack = fCMC ? &m_cAttrStackNew : &m_cAttrStack;
    pAttrStackEle->pNext = *ppAttrStack;
    *ppAttrStack = pAttrStackEle;
    (*pcAttrStack)++;

    LeaveCriticalSection(&m_csXEnroll);

    return(TRUE);
}

PCRYPT_ATTRIBUTE
CCEnroll::PopStackAttribute(BOOL fCMC)
{
    PATTR_STACK pAttrStackEle = NULL;
    PATTR_STACK *ppAttrStack = NULL;
    DWORD       *pcAttrStack = NULL;

    EnterCriticalSection(&m_csXEnroll);

    ppAttrStack = fCMC ? &m_pAttrStackNew : &m_pAttrStack;

    if(NULL != *ppAttrStack)
    {
        pAttrStackEle = *ppAttrStack;
        *ppAttrStack = (*ppAttrStack)->pNext;
        pcAttrStack = fCMC ? &m_cAttrStackNew : &m_cAttrStack;
        (*pcAttrStack)--;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCRYPT_ATTRIBUTE) pAttrStackEle);
}

DWORD
CCEnroll::CountStackAttribute(BOOL fCMC)
{
    DWORD   cAttr = 0;

    EnterCriticalSection(&m_csXEnroll);
    cAttr = fCMC ? m_cAttrStackNew : m_cAttrStack;
    LeaveCriticalSection(&m_csXEnroll);

    return(cAttr);
}

PCRYPT_ATTRIBUTE
CCEnroll::EnumStackAttribute(
    PCRYPT_ATTRIBUTE pAttrLast,
    BOOL             fCMC)
{
    PATTR_STACK pAttrStackEle    = (PATTR_STACK) pAttrLast;

    EnterCriticalSection(&m_csXEnroll);

    if(NULL == pAttrLast)
    {
        pAttrStackEle = fCMC ? m_pAttrStackNew : m_pAttrStack;
    }
    else
    {
        pAttrStackEle = pAttrStackEle->pNext;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCRYPT_ATTRIBUTE) pAttrStackEle);
}

void CCEnroll::FreeAllStackAttribute(void)
{
    EnterCriticalSection(&m_csXEnroll);

    while(0 != m_cAttrStackNew)
    {
        FreeStackAttribute(PopStackAttribute(TRUE));
    }

    while(0 != m_cAttrStack)
    {
        FreeStackAttribute(PopStackAttribute(FALSE));
    }

    LeaveCriticalSection(&m_csXEnroll);
}

void CCEnroll::FreeStackAttribute(PCRYPT_ATTRIBUTE pAttr) {
    if(pAttr != NULL)
        free(pAttr);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::AddAuthenticatedAttributesToPKCS7Request(
     PCRYPT_ATTRIBUTES pAttributes)
{
    HRESULT hr = S_OK;
    DWORD i;

    for(i = 0; i < pAttributes->cAttr; i++)
    {
        if(!CopyAndPushStackAttribute(&pAttributes->rgAttr[i], FALSE))
        {
            hr = (MY_HRESULT_FROM_WIN32(GetLastError()));
            break;
        }

        if(!CopyAndPushStackAttribute(&pAttributes->rgAttr[i], TRUE))
        {
            hr = (MY_HRESULT_FROM_WIN32(GetLastError()));
            break;
        }
    }

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::CreatePKCS7RequestFromRequest(
      PCRYPT_DATA_BLOB pRequest,
      PCCERT_CONTEXT pSigningRACertContext,
     PCRYPT_DATA_BLOB pPkcs7Blob) {

    HRESULT                     hr              = S_OK;
    DWORD                       errBefore       = GetLastError();
    CRYPT_SIGN_MESSAGE_PARA     signMsgPara;
    PCCRYPT_OID_INFO            pOidInfo        = NULL;
    PCRYPT_ATTRIBUTE            pAttrCur        = NULL;
    DWORD                       i;
    ALG_ID                      rgAlg[2];
    CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
    DWORD                cb = 0;

    assert(pSigningRACertContext != NULL);
    assert(pRequest != NULL);
    assert(pPkcs7Blob != NULL);

    memset(&signMsgPara, 0, sizeof(CRYPT_SIGN_MESSAGE_PARA));
    memset(pPkcs7Blob, 0, sizeof(CRYPT_DATA_BLOB));

    if( !GetCapiHashAndSigAlgId(rgAlg) )
        goto ErrorGetCapiHashAndSigAlgId;

    if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_ALGID_KEY,
        (void *) &rgAlg[0],
        CRYPT_HASH_ALG_OID_GROUP_ID)) ) )
    {
        SetLastError((DWORD)NTE_BAD_ALGID);
        goto ErrorCryptFindOIDInfo;
    }

    EnterCriticalSection(&m_csXEnroll);
    signMsgPara.cAuthAttr = CountStackAttribute(m_fNewRequestMethod);

    signMsgPara.rgAuthAttr = (PCRYPT_ATTRIBUTE)LocalAlloc(LMEM_FIXED,
                    signMsgPara.cAuthAttr * sizeof(CRYPT_ATTRIBUTE));
    if( NULL == signMsgPara.rgAuthAttr)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        LeaveCriticalSection(&m_csXEnroll);
        goto ErrorOutOfMemory;
    }

    i = 0;
    pAttrCur = NULL;
    while(NULL != (pAttrCur = EnumStackAttribute(pAttrCur, m_fNewRequestMethod)) ) {
        signMsgPara.rgAuthAttr[i] = *pAttrCur;
        i++;
    }
    LeaveCriticalSection(&m_csXEnroll);

    signMsgPara.cbSize                  = sizeof(CRYPT_SIGN_MESSAGE_PARA);
    signMsgPara.dwMsgEncodingType       = PKCS_7_ASN_ENCODING;
    signMsgPara.pSigningCert            = pSigningRACertContext;
    signMsgPara.HashAlgorithm.pszObjId  = (char *) pOidInfo->pszOID;
    signMsgPara.cMsgCert                = 1;
    signMsgPara.rgpMsgCert              = &pSigningRACertContext;

    while (TRUE)
    {
        if(!CertGetCertificateContextProperty(
                pSigningRACertContext,
                CERT_KEY_PROV_INFO_PROP_ID,
                pKeyProvInfo,
                &cb))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertGetCertificateContextPropertyError;
        }
        if (NULL != pKeyProvInfo)
        {

            break;
        }
        pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)LocalAlloc(LMEM_FIXED, cb);
        if (NULL == pKeyProvInfo)
        {
            hr = E_OUTOFMEMORY;
            goto ErrorOutOfMemory;
        }
    }
    if (0x0 != (pKeyProvInfo->dwFlags & CRYPT_SILENT))
    {

        signMsgPara.dwFlags |= CRYPT_MESSAGE_SILENT_KEYSET_FLAG;
    }

    if( !CryptSignMessage(
        &signMsgPara,
        FALSE,
        1,
        (const BYTE **) &pRequest->pbData,
        &pRequest->cbData ,
        NULL,
        &pPkcs7Blob->cbData)                          ||
    (pPkcs7Blob->pbData = (BYTE *)
        MyCoTaskMemAlloc(pPkcs7Blob->cbData)) == NULL ||
    !CryptSignMessage(
        &signMsgPara,
        FALSE,
        1,
        (const BYTE **) &pRequest->pbData,
        &pRequest->cbData ,
        pPkcs7Blob->pbData,
        &pPkcs7Blob->cbData) )
        goto ErrorCryptSignMessage;

CommonReturn:
    if (NULL != pKeyProvInfo)
    {
        LocalFree(pKeyProvInfo);
    }
    if (NULL != signMsgPara.rgAuthAttr)
    {
        LocalFree(signMsgPara.rgAuthAttr);
    }

    SetLastError(errBefore);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    if(pPkcs7Blob->pbData != NULL)
        MyCoTaskMemFree(pPkcs7Blob->pbData);
    memset(pPkcs7Blob, 0, sizeof(CRYPT_DATA_BLOB));

    goto CommonReturn;

TRACE_ERROR(ErrorGetCapiHashAndSigAlgId);
TRACE_ERROR(ErrorCryptSignMessage);
TRACE_ERROR(ErrorCryptFindOIDInfo);
TRACE_ERROR(ErrorOutOfMemory);
TRACE_ERROR(CertGetCertificateContextPropertyError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::AddNameValuePairToSignatureWStr(
     LPWSTR pwszName,
     LPWSTR pwszValue)
{
    HRESULT hr = S_OK;

    assert(pwszName != NULL && pwszValue != NULL);

    CRYPT_ENROLLMENT_NAME_VALUE_PAIR nameValuePair = {pwszName, pwszValue};
    CRYPT_ATTR_BLOB blobAttr;
    CRYPT_ATTRIBUTE attr = {szOID_ENROLLMENT_NAME_VALUE_PAIR, 1, &blobAttr};
    CRYPT_ATTRIBUTES attrs = {1, &attr};

    memset(&blobAttr, 0, sizeof(CRYPT_ATTR_BLOB));

    hr = xeEncodeNameValuePair(
                &nameValuePair,
                &blobAttr.pbData,
                &blobAttr.cbData);
    if (S_OK != hr)
    {
        goto error;
    }

    hr = AddAuthenticatedAttributesToPKCS7Request(&attrs);

error:
    if (NULL != blobAttr.pbData)
    {
        MyCoTaskMemFree(blobAttr.pbData);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE CCEnroll::AddCertTypeToRequestWStr(
            LPWSTR szw) {

    HRESULT                     hr              = S_OK;
    DWORD                       errBefore       = GetLastError();

    CERT_NAME_VALUE  nameValue;
    CERT_EXTENSION  ext;
    CERT_EXTENSIONS exts = {1, &ext};

    memset(&ext, 0, sizeof(CERT_EXTENSION));

    nameValue.dwValueType = CERT_RDN_BMP_STRING;
    nameValue.Value.cbData = 0;
    nameValue.Value.pbData = (PBYTE) szw;

    ext.pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION;

    if( !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_UNICODE_ANY_STRING,
            &nameValue,
            NULL,
            &ext.Value.cbData
            ) )
        goto ErrorCryptEncodeObject;

    ext.Value.pbData = (PBYTE)LocalAlloc(LMEM_FIXED, ext.Value.cbData);
    if(NULL == ext.Value.pbData)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        goto ErrorOutOfMemory;
    }

    if( !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_UNICODE_ANY_STRING,
            &nameValue,
            ext.Value.pbData,
            &ext.Value.cbData
            ) )
        goto ErrorCryptEncodeObject;

    if(S_OK != AddExtensionsToRequest(&exts))
        goto ErrorAddExtensionsToRequest;

    if(!CopyAndPushStackExtension(&ext, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CopyAndPushStackExtensionError;
    }

CommonReturn:
    if (NULL != ext.Value.pbData)
    {
        LocalFree(ext.Value.pbData);
    }

    SetLastError(errBefore);
    return(hr);

ErrorReturn:
    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorCryptEncodeObject);
TRACE_ERROR(ErrorAddExtensionsToRequest);
TRACE_ERROR(ErrorOutOfMemory);
TRACE_ERROR(CopyAndPushStackExtensionError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::AddCertTypeToRequestWStrEx(
            IN  LONG            lType,
            IN  LPCWSTR         pwszOIDOrName,
            IN  LONG            lMajorVersion,
            IN  BOOL            fMinorVersion,
            IN  LONG            lMinorVersion)
{
    HRESULT hr;
    LPCSTR            lpszStructType;
    CERT_NAME_VALUE   nameValue;
    CERT_TEMPLATE_EXT Template;
    VOID             *pv;
    CERT_EXTENSION    ext; 
    DWORD             cb = 0;
    CHAR             *pszOID = NULL;

    ZeroMemory(&ext, sizeof(ext));
    ext.fCritical = FALSE;

    if (NULL == pwszOIDOrName)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    switch (lType)
    {
        case XECT_EXTENSION_V1:
            ext.pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION;
            nameValue.dwValueType = CERT_RDN_BMP_STRING;
            nameValue.Value.cbData = 0;
            nameValue.Value.pbData = (BYTE*)pwszOIDOrName;
            pv = (VOID*)&nameValue;
            lpszStructType = X509_UNICODE_ANY_STRING;
        break;
        case XECT_EXTENSION_V2:
            ext.pszObjId = szOID_CERTIFICATE_TEMPLATE;

            while (TRUE)
            {
                cb = WideCharToMultiByte(
                            GetACP(),
                            0,
                            pwszOIDOrName,
                            -1,
                            pszOID,
                            cb,
                            NULL,
                            NULL);
                if (0 == cb)
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto WideCharToMultiByteError;
                }
                if (NULL != pszOID)
                {

                    break;
                }
                pszOID = (CHAR*)LocalAlloc(LMEM_FIXED, cb);
                if (NULL == pszOID)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
            }

            ZeroMemory(&Template, sizeof(Template));
            Template.pszObjId = pszOID;
            Template.dwMajorVersion = lMajorVersion;
            Template.fMinorVersion =  fMinorVersion;
            Template.dwMinorVersion = lMinorVersion;
            pv = (VOID*)&Template;
            lpszStructType = X509_CERTIFICATE_TEMPLATE;
        break;
        default:
            hr = E_INVALIDARG;
            goto InvalidArgError;
        break;
    }

    while (TRUE)
    {
        if (!CryptEncodeObject(
                    X509_ASN_ENCODING,
                    lpszStructType,
                    pv,
                    ext.Value.pbData,
                    &ext.Value.cbData))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptEncodeObjectError;
        }
        if (NULL != ext.Value.pbData)
        {

            break;
        }
        ext.Value.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, ext.Value.cbData);
        if (NULL == ext.Value.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if(!CopyAndPushStackExtension(&ext, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CopyAndPushStackExtensionError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pszOID)
    {
        LocalFree(pszOID);
    }
    if (NULL != ext.Value.pbData)
    {
        LocalFree(ext.Value.pbData);
    }
    return hr;

TRACE_ERROR(InvalidArgError)
TRACE_ERROR(CopyAndPushStackExtensionError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(WideCharToMultiByteError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::getProviderTypeWStr( 
    IN  LPCWSTR  pwszProvName,
    OUT LONG *   plProvType)
{
    HRESULT  hr;
    DWORD    i = 0;
    DWORD    cb;
    DWORD    dwProvType;
    WCHAR   *pwszEnumProvName = NULL;

    if (NULL == pwszProvName)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    *plProvType = -1;

    while (TRUE)
    {
        while (TRUE)
        {
            if (!CryptEnumProvidersU(
                    i,
                    NULL,
                    0,
                    &dwProvType,
                    pwszEnumProvName,
                    &cb))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                if (MY_HRESULT_FROM_WIN32(NTE_PROV_TYPE_ENTRY_BAD) == hr)
                {

                    assert(NULL == pwszEnumProvName);
                    break; 
                }

                goto CryptEnumProvidersUError;
            }
            if (NULL != pwszEnumProvName)
            {

                break;
            }
            pwszEnumProvName = (WCHAR*)LocalAlloc(LMEM_FIXED, cb);
            if (NULL == pwszEnumProvName)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        if (NULL != pwszEnumProvName)
        {
            if (0 == _wcsicmp(pwszProvName, pwszEnumProvName))
            {

                *plProvType = (LONG)dwProvType;
                break; 
            }
        }

        ++i;
        if (NULL != pwszEnumProvName)
        {
            LocalFree(pwszEnumProvName);
            pwszEnumProvName = NULL;
        }
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszEnumProvName)
    {
        LocalFree(pwszEnumProvName);
    }
    return hr;

TRACE_ERROR(InvalidArgError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptEnumProvidersUError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7Blob( 
     PCRYPT_DATA_BLOB pBlobPKCS7)
{
    return InstallPKCS7BlobEx(pBlobPKCS7, NULL);
}

HRESULT CCEnroll::InstallPKCS7BlobEx( 
     PCRYPT_DATA_BLOB pBlobPKCS7,
     LONG           *plCertInstalled)
{

    HRESULT                     hr                      = S_OK;
    DWORD                       errBefore               = GetLastError();
    HCERTSTORE                  hStoreMsg               = NULL;
    LPWSTR                      pwszTitle               = NULL; 
    LPWSTR                      pwszNotSafeAccepting    = NULL; 

    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) { 
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
	if (S_OK != hr) { 
	    SetLastError(hr); 
	    goto xeLoadRCStringError;
	}

	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_ACCEPTING_CERT, &pwszNotSafeAccepting);
	if (S_OK != hr) { 
	    SetLastError(hr); 
	    goto xeLoadRCStringError;
	}

	if (IDYES != MessageBoxU(NULL, pwszNotSafeAccepting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) { 
	    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
	    SetLastError(hr); 
	    goto CancelledError; 
	}
    }

    if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                       pBlobPKCS7,
                       (CERT_QUERY_CONTENT_FLAG_CERT |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
                       CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED) ,
                       CERT_QUERY_FORMAT_FLAG_ALL,
                       0,
                       NULL,
                       NULL,
                       NULL,
                       &hStoreMsg,
                       NULL,
                       NULL) )
        goto ErrorCryptQueryObject;

    hr = AddCertsToStores(hStoreMsg, plCertInstalled);

    if (S_OK != hr && MY_HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
    {
        goto ErrorAddCertsToStores;
    }

CommonReturn:

    if(hStoreMsg != NULL)
        CertCloseStore(hStoreMsg, 0);
    if (NULL != pwszNotSafeAccepting)
	LocalFree(pwszNotSafeAccepting);
    if (NULL != pwszTitle)
	LocalFree(pwszTitle);

    SetLastError(errBefore);
    LeaveCriticalSection(&m_csXEnroll);

    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorCryptQueryObject);
TRACE_ERROR(ErrorAddCertsToStores);
TRACE_ERROR(CancelledError);
TRACE_ERROR(xeLoadRCStringError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7( 
     BSTR wszPKCS7)
{
    CRYPT_DATA_BLOB             blobPKCS7;

    assert(wszPKCS7 != NULL);

    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    return(InstallPKCS7Blob(&blobPKCS7));
}

HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7Ex( 
     BSTR   wszPKCS7,
     LONG __RPC_FAR *plCertInstalled)
{
    CRYPT_DATA_BLOB             blobPKCS7;

    assert(wszPKCS7 != NULL);

    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    return(InstallPKCS7BlobEx(&blobPKCS7, plCertInstalled));
}

HRESULT STDMETHODCALLTYPE CCEnroll::Reset(void)
{
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);
    Destruct();
    hr = Init();
    LeaveCriticalSection(&m_csXEnroll);

    return hr;
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetSupportedKeySpec(
    LONG __RPC_FAR *pdwKeySpec) {

    DWORD               errBefore   = GetLastError();
    DWORD               hr          = S_OK;
    DWORD               cb          = sizeof(DWORD);

    SetLastError(ERROR_SUCCESS);

    assert(pdwKeySpec != NULL);
    *pdwKeySpec = 0;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    if( !CryptGetProvParam(
            m_hVerifyProv,
            PP_KEYSPEC,
            (BYTE *) pdwKeySpec,
            &cb,
            0
            ) ) 
        goto ErrorCryptGetProvParam;

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);

    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(GetVerifyProvError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetKeyLenEx(
    LONG    lSizeSpec,
    LONG    lKeySpec,
    LONG __RPC_FAR *pdwKeySize)
{
    BOOL        fKeyX;
    BOOL        fKeyInc = FALSE;
    DWORD       dwKeySize = 0xFFFFFFFF;
    DWORD       cb;
    HRESULT     hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    switch (lKeySpec)
    {
        case XEKL_KEYSPEC_KEYX:
            fKeyX = TRUE;
            break;
        case XEKL_KEYSPEC_SIG:
            fKeyX = FALSE;
            break;
        default:

            hr = E_INVALIDARG;
            goto InvalidArgError;
    }

    switch (lSizeSpec)
    {
        case XEKL_KEYSIZE_MIN:
        case XEKL_KEYSIZE_MAX:
        case XEKL_KEYSIZE_DEFAULT:
            break;
        case XEKL_KEYSIZE_INC:
            fKeyInc = TRUE;
            break;
        default:

            hr = E_INVALIDARG;
            goto InvalidArgError;
    }

    if (!fKeyInc)
    {
        DWORD dwAlg = (fKeyX ? ALG_CLASS_KEY_EXCHANGE : ALG_CLASS_SIGNATURE);

        *pdwKeySize = GetKeySizeInfo(lSizeSpec, dwAlg);

        if(0xFFFFFFFF == *pdwKeySize)
        {
            *pdwKeySize = 0;
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto GetKeySizeInfoError;
        }
    }
    else
    {
        if ((fKeyX && (0 != m_dwXhgKeyLenInc)) ||
            (!fKeyX && (0 != m_dwSigKeyLenInc)))
        {

            if (fKeyX)
            {
                *pdwKeySize = m_dwXhgKeyLenInc;
            }
            else
            {
                *pdwKeySize = m_dwSigKeyLenInc;
            }
        }
        else
        {
            hr = GetVerifyProv();
            if (S_OK != hr)
            {
                goto GetVerifyProvError;
            }

            *pdwKeySize = 0;
            cb = sizeof(dwKeySize);
            if (!CryptGetProvParam(
                    m_hVerifyProv,
                    fKeyX ? PP_KEYX_KEYSIZE_INC : PP_SIG_KEYSIZE_INC,
                    (BYTE*)&dwKeySize,
                    &cb,
                    0))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto CryptGetProvParamError;
            }            
            else
            {
                *pdwKeySize = dwKeySize;

                if (fKeyX)
                {
                    m_dwXhgKeyLenInc = dwKeySize;
                }
                else
                {
                    m_dwSigKeyLenInc = dwKeySize;
                }
            }
        }
    }

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(CryptGetProvParamError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(GetKeySizeInfoError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetKeyLen(
    BOOL    fMin,
    BOOL    fExchange,
    LONG __RPC_FAR *pdwKeySize) {

    DWORD   hr = S_OK;
    LONG    lKeySizeSpec = (fMin ? XEKL_KEYSIZE_MIN : XEKL_KEYSIZE_MAX);

    if(fExchange)
        *pdwKeySize = GetKeySizeInfo(lKeySizeSpec, ALG_CLASS_KEY_EXCHANGE);
    else
        *pdwKeySize = GetKeySizeInfo(lKeySizeSpec, ALG_CLASS_SIGNATURE);

    if(*pdwKeySize == 0xFFFFFFFF) {
        *pdwKeySize = 0;
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

    return(hr);
}

DWORD CCEnroll::GetKeySizeInfo(
    LONG    lKeySizeSpec,
    DWORD   algClass) {

    DWORD               cb = sizeof(PROV_ENUMALGS_EX);
    HRESULT             hr = S_OK;
    DWORD               errBefore   = GetLastError();
    DWORD               dwFlags     = CRYPT_FIRST;
    PROV_ENUMALGS_EX    algInfo;
    DWORD               dwKeySize   = 0xFFFFFFFF;
    DWORD               err         = ERROR_SUCCESS;

#ifdef DBG

    assert(ALG_CLASS_KEY_EXCHANGE == algClass ||
           ALG_CLASS_SIGNATURE == algClass);
#endif 

    SetLastError(ERROR_SUCCESS);

    memset(&algInfo, 0, sizeof(algInfo));

    EnterCriticalSection(&m_csXEnroll);

    if ((ALG_CLASS_KEY_EXCHANGE == algClass && 0 != m_dwXhgKeyLenMax) ||
        (ALG_CLASS_SIGNATURE == algClass && 0 != m_dwSigKeyLenMax))
    {

#if DBG
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            assert(0 != m_dwXhgKeyLenMin);
            assert(0 != m_dwXhgKeyLenDef);
        }
        if (ALG_CLASS_SIGNATURE == algClass)
        {
            assert(0 != m_dwSigKeyLenMin);
            assert(0 != m_dwSigKeyLenDef);
        }
#endif 

    }
    else
    {
#if DBG
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            assert(0 == m_dwXhgKeyLenMin);
            assert(0 == m_dwXhgKeyLenDef);
        }
        if (ALG_CLASS_SIGNATURE == algClass)
        {
            assert(0 == m_dwSigKeyLenMin);
            assert(0 == m_dwSigKeyLenDef);
        }
#endif 
        hr = GetVerifyProv();
        if (S_OK != hr)
        {
            goto GetVerifyProvError;
        }

        while (CryptGetProvParam(
                m_hVerifyProv,
                PP_ENUMALGS_EX,
                (BYTE *) &algInfo,
                &cb,
                dwFlags))
        {

            dwFlags = 0;

            if (ALG_CLASS_KEY_EXCHANGE == GET_ALG_CLASS(algInfo.aiAlgid))
            {

                m_dwXhgKeyLenMax = algInfo.dwMaxLen;
                m_dwXhgKeyLenMin = algInfo.dwMinLen;
                m_dwXhgKeyLenDef = algInfo.dwDefaultLen;
            }
            else if (ALG_CLASS_SIGNATURE == GET_ALG_CLASS(algInfo.aiAlgid))
            {
                m_dwSigKeyLenMax = algInfo.dwMaxLen;
                m_dwSigKeyLenMin = algInfo.dwMinLen;
                m_dwSigKeyLenDef = algInfo.dwDefaultLen;
            }

            if (0 != m_dwXhgKeyLenMax &&
                0 != m_dwXhgKeyLenMin &&
                0 != m_dwXhgKeyLenDef &&
                0 != m_dwSigKeyLenMax &&
                0 != m_dwSigKeyLenMin &&
                0 != m_dwSigKeyLenDef)
            {

                break;
            }
        }
    }

    err = GetLastError();

    if (err != ERROR_SUCCESS)
    {
        if (err != ERROR_NO_MORE_ITEMS) 
        {
            goto ErrorCryptGetProvParam;
        }

        if ((ALG_CLASS_KEY_EXCHANGE == algClass && 0 != m_dwXhgKeyLenMax) ||
            (ALG_CLASS_SIGNATURE == algClass && 0 != m_dwSigKeyLenMax))
        {

            SetLastError(ERROR_SUCCESS);
        }
        else
        {
            SetLastError((DWORD)NTE_BAD_ALGID);
        }
    }

    if(XEKL_KEYSIZE_MIN == lKeySizeSpec)
    {
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            dwKeySize = m_dwXhgKeyLenMin;
        }
        else
        {
            dwKeySize = m_dwSigKeyLenMin;
        }
    }
    else if (XEKL_KEYSIZE_MAX == lKeySizeSpec)
    {
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            dwKeySize = m_dwXhgKeyLenMax;
        }
        else
        {
            dwKeySize = m_dwSigKeyLenMax;
        }
    }
    else if (XEKL_KEYSIZE_DEFAULT == lKeySizeSpec)
    {
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            dwKeySize = m_dwXhgKeyLenDef;
        }
        else
        {
            dwKeySize = m_dwSigKeyLenDef;
        }
    }

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(dwKeySize);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);

    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
}

HRESULT STDMETHODCALLTYPE CCEnroll::EnumAlgs(
     LONG  dwIndex,
     LONG  algMask,
     LONG  __RPC_FAR *pdwAlgID) {

    DWORD           errBefore   = GetLastError();
    PROV_ENUMALGS       enumAlgs;
    DWORD           cb          = sizeof(enumAlgs);
    LONG            i           = 0;
    HRESULT         hr          = S_OK;
    DWORD           dwFlags;
    BOOL            f1st = TRUE;

    SetLastError(ERROR_SUCCESS);

    memset(&enumAlgs, 0, sizeof(enumAlgs));
    assert(pdwAlgID != NULL);
    *pdwAlgID = 0;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    if (MAXDWORD != m_dwLastAlgIndex &&
        ((DWORD)dwIndex) == m_dwLastAlgIndex + 1)
    {

        dwFlags = 0;
        while (f1st || (DWORD)algMask != GET_ALG_CLASS(enumAlgs.aiAlgid))
        {
            if(!CryptGetProvParam(
                        m_hVerifyProv,
                        PP_ENUMALGS,
                        (BYTE*)&enumAlgs,
                        &cb,
                        dwFlags))
            {
                goto ErrorCryptGetProvParam;
            }
            f1st = FALSE;
        }
    }
    else
    {
        dwFlags = CRYPT_FIRST;
        for (i = 0; i <= dwIndex; i++)
        {
            if(!CryptGetProvParam(
                   m_hVerifyProv,
                   PP_ENUMALGS,
                   (BYTE*)&enumAlgs,
                   &cb,
                   dwFlags))
            {
                    goto ErrorCryptGetProvParam;
            }
            dwFlags = 0; 

            if ((DWORD)algMask != GET_ALG_CLASS(enumAlgs.aiAlgid)) 
            {
                i--;
            }
        }
    }

    m_dwLastAlgIndex = dwIndex;

    *pdwAlgID = enumAlgs.aiAlgid;

CommonReturn:
    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    m_dwLastAlgIndex = MAXDWORD;

    goto CommonReturn;

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetAlgNameWStr(
     LONG               algID,
     LPWSTR __RPC_FAR *ppwsz) {

    DWORD           errBefore   = GetLastError();
    PROV_ENUMALGS       enumAlgs;
    DWORD           cb          = sizeof(enumAlgs);
    HRESULT         hr          = S_OK;
    DWORD           dwFlags     = CRYPT_FIRST;

    SetLastError(ERROR_SUCCESS);

    memset(&enumAlgs, 0, sizeof(enumAlgs));

    EnterCriticalSection(&m_csXEnroll);

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    do {

        if( !CryptGetProvParam(
            m_hVerifyProv,
            PP_ENUMALGS,
            (BYTE *) &enumAlgs,
            &cb,
            dwFlags) )
            goto ErrorCryptGetProvParam;

        dwFlags = 0; 

   } while((DWORD)algID != enumAlgs.aiAlgid);

   if( (*ppwsz = WideFromMB(enumAlgs.szName)) == NULL )
        goto ErrorOutOfMem;

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(ErrorOutOfMem);
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetAlgName(
             LONG                     algID,
             BSTR __RPC_FAR *pbstr) {

    DWORD       errBefore   = GetLastError();
    LPWSTR      pwsz        = NULL;
    HRESULT     hr          = S_OK;

    SetLastError(ERROR_SUCCESS);

    assert(pbstr != NULL);

    if((hr = GetAlgNameWStr(algID, &pwsz)) != S_OK)
        goto ErrorgetAlgNameWStr;

    if( (*pbstr = SysAllocString(pwsz)) == NULL )
        goto ErrorSysAllocString;

CommonReturn:

    if(pwsz != NULL)
        MyCoTaskMemFree(pwsz);

    SetLastError(errBefore);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorgetAlgNameWStr);
TRACE_ERROR(ErrorSysAllocString);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ReuseHardwareKeyIfUnableToGenNew(
     BOOL __RPC_FAR *fBool) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fReuseHardwareKeyIfUnableToGenNew;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ReuseHardwareKeyIfUnableToGenNew(
     BOOL fBool) {
    EnterCriticalSection(&m_csXEnroll);
    m_fReuseHardwareKeyIfUnableToGenNew = fBool;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreMy(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;

    else {
        if(m_MyStore.wszName != wszMY)
            MyCoTaskMemFree(m_MyStore.wszName);

        m_MyStore.wszName = NULL;
        m_MyStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreCA(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_CAStore.wszName != wszCA)
            MyCoTaskMemFree(m_CAStore.wszName);

        m_CAStore.wszName = NULL;
        m_CAStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreROOT(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
            MyCoTaskMemFree(m_RootStore.wszName);

        m_RootStore.wszName = NULL;
        m_RootStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreRequest(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RequestStore.wszName != wszREQUEST)
            MyCoTaskMemFree(m_RequestStore.wszName);

        m_RequestStore.wszName = NULL;
        m_RequestStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return(hr);
}

HRESULT STDMETHODCALLTYPE  CCEnroll::put_LimitExchangeKeyToEncipherment(
    BOOL    fBool
    ) {

    EnterCriticalSection(&m_csXEnroll);
    m_fLimitExchangeKeyToEncipherment = fBool;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
    }

HRESULT STDMETHODCALLTYPE  CCEnroll::get_LimitExchangeKeyToEncipherment(
    BOOL * fBool
    ) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fLimitExchangeKeyToEncipherment;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
    }

HRESULT STDMETHODCALLTYPE  CCEnroll::put_EnableSMIMECapabilities(
    BOOL fSMIME
    )
{
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);

    if (m_fKeySpecSetByClient)
    {

        if (AT_SIGNATURE == m_keyProvInfo.dwKeySpec && fSMIME)
        {

            hr = XENROLL_E_KEYSPEC_SMIME_MISMATCH;
            goto MismatchError;
        }
    }
    else
    {

        m_keyProvInfo.dwKeySpec = fSMIME ? AT_KEYEXCHANGE : AT_SIGNATURE;
    }
    m_fEnableSMIMECapabilities = fSMIME;
    m_fSMIMESetByClient = TRUE;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(MismatchError)
}

HRESULT STDMETHODCALLTYPE  CCEnroll::get_EnableSMIMECapabilities(
    BOOL * fBool
    ) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fEnableSMIMECapabilities;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
    }

HRESULT
GetCertificateContextFromBStr(
    IN  BSTR  bstrCert,
    OUT PCCERT_CONTEXT *ppCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pCert = NULL;
    BYTE    *pbCert = NULL;
    DWORD    cbCert = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                        (WCHAR*)bstrCert,
                        SysStringLen(bstrCert),
                        CRYPT_STRING_ANY,
                        pbCert,
                        &cbCert,
                        NULL,
                        NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbCert)
        {
            break; 
        }
        pbCert = (BYTE*)LocalAlloc(LMEM_FIXED, cbCert);
        if (NULL == pbCert)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    pCert = CertCreateCertificateContext(
                                X509_ASN_ENCODING,
                                pbCert,
                                cbCert);
    if (NULL == pCert)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertCreateCertificateContextError;
    }
    *ppCert = pCert;
    pCert = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pbCert)
    {
        LocalFree(pbCert);
    }
    if (NULL != pCert)
    {
        CertFreeCertificateContext(pCert);
    }
    return (hr);

TRACE_ERROR(CertCreateCertificateContextError)
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(OutOfMemoryError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::put_PrivateKeyArchiveCertificate(
    IN  BSTR  bstrCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pPrivateKeyArchiveCert = NULL;

    if (NULL != bstrCert)
    {
        hr = GetCertificateContextFromBStr(bstrCert, &pPrivateKeyArchiveCert);
        if (S_OK != hr)
        {
            goto GetCertificateContextFromBStrError;
        }
    }

    hr = SetPrivateKeyArchiveCertificate(pPrivateKeyArchiveCert);
    if (S_OK != hr)
    {
        goto SetPrivateKeyArchiveCertificateError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pPrivateKeyArchiveCert)
    {
        CertFreeCertificateContext(pPrivateKeyArchiveCert);
    }
    return hr;

TRACE_ERROR(GetCertificateContextFromBStrError)
TRACE_ERROR(SetPrivateKeyArchiveCertificateError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::get_PrivateKeyArchiveCertificate(
    OUT BSTR __RPC_FAR *pbstrCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pPrivateKeyArchiveCert = NULL;
    CRYPT_DATA_BLOB blobCert;

    *pbstrCert = NULL;

    pPrivateKeyArchiveCert = GetPrivateKeyArchiveCertificate();

    if (NULL != pPrivateKeyArchiveCert)
    {
        blobCert.pbData = pPrivateKeyArchiveCert->pbCertEncoded;
        blobCert.cbData = pPrivateKeyArchiveCert->cbCertEncoded;
        hr = BlobToBstring(&blobCert, CRYPT_STRING_BASE64HEADER, pbstrCert);
        if (S_OK != hr)
        {
            goto BlobToBstringError;
        }
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pPrivateKeyArchiveCert)
    {
        CertFreeCertificateContext(pPrivateKeyArchiveCert);
    }
    return hr;

TRACE_ERROR(BlobToBstringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::put_ThumbPrint(IN BSTR bstrThumbPrint) 
{ 
    CRYPT_DATA_BLOB hashBlob; 
    HRESULT         hr; 

    if (bstrThumbPrint == NULL)
        return E_INVALIDARG; 

    hashBlob.cbData = 0; 
    hashBlob.pbData = NULL;

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_BASE64,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData); 
    if (NULL == hashBlob.pbData)
      goto MemoryErr; 

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_BASE64,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hr = this->put_ThumbPrintWStr(hashBlob); 

 ErrorReturn: 
    if (NULL != hashBlob.pbData) { LocalFree(hashBlob.pbData); } 
    return hr; 

SET_HRESULT(MyCryptToBinaryErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(MemoryErr,          E_OUTOFMEMORY); 
} 

HRESULT STDMETHODCALLTYPE
CCEnroll::put_ThumbPrintWStr(IN CRYPT_DATA_BLOB  hashBlob)
{
    if (hashBlob.pbData == NULL)
        return E_INVALIDARG; 

    if (m_hashBlobPendingRequest.pbData != NULL)
    {
        LocalFree(m_hashBlobPendingRequest.pbData); 
        m_hashBlobPendingRequest.pbData = NULL; 
    }

    m_hashBlobPendingRequest.cbData = hashBlob.cbData; 
    m_hashBlobPendingRequest.pbData = (LPBYTE)LocalAlloc(LPTR, m_hashBlobPendingRequest.cbData);

    if (m_hashBlobPendingRequest.pbData == NULL)
        return E_OUTOFMEMORY; 

    CopyMemory(m_hashBlobPendingRequest.pbData, hashBlob.pbData, hashBlob.cbData); 
    return S_OK; 
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::get_ThumbPrint(OUT BSTR __RPC_FAR *pbstrThumbPrint) 
{ 
    CRYPT_DATA_BLOB hashBlob; 
    DWORD           cchThumbPrintStr; 
    HRESULT         hr; 
    WCHAR          *pwszThumbPrint = NULL; 
    int             i, n; 

    if (pbstrThumbPrint == NULL)
        return E_INVALIDARG; 

    ZeroMemory(&hashBlob, sizeof(hashBlob));
    *pbstrThumbPrint  = NULL; 

    if (S_OK != (hr = this->get_ThumbPrintWStr(&hashBlob)))
        goto ErrorReturn; 

    hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData); 
    if (NULL == hashBlob.pbData)
        goto MemoryErr; 

    if (S_OK != (hr = this->get_ThumbPrintWStr(&hashBlob)))
        goto ErrorReturn; 

    while (TRUE)
    {
        if (!MyCryptBinaryToStringW(
                        hashBlob.pbData, 
                        hashBlob.cbData, 
                        CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR,
                        pwszThumbPrint,
                        &cchThumbPrintStr))
        {
            goto MyCryptToStringErr; 
        }
        if (NULL != pwszThumbPrint)
        {

            break;
        }
        pwszThumbPrint = (WCHAR*)LocalAlloc(LMEM_FIXED,
                                          cchThumbPrintStr * sizeof(WCHAR)); 
        if (NULL == pwszThumbPrint)
        {
            goto MemoryErr; 
        }
    }

    n = (int)wcslen(pwszThumbPrint);
    for (i = n - 1; i > -1; --i)
    {
        if (L'\r' != pwszThumbPrint[i] &&
            L'\n' != pwszThumbPrint[i])
        {
            break; 
        }
        pwszThumbPrint[i] = L'\0'; 
    }

    *pbstrThumbPrint = SysAllocString(pwszThumbPrint); 
    if (NULL == *pbstrThumbPrint)
    {
        goto MemoryErr; 
    }

    hr = S_OK; 
ErrorReturn:
    if (NULL != hashBlob.pbData)
    {
        LocalFree(hashBlob.pbData);
    }
    if (NULL != pwszThumbPrint)
    {
        LocalFree(pwszThumbPrint);
    } 
    return hr; 

SET_HRESULT(MyCryptToStringErr, MY_HRESULT_FROM_WIN32(GetLastError())) 
SET_HRESULT(MemoryErr, E_OUTOFMEMORY) 
} 

HRESULT STDMETHODCALLTYPE
CCEnroll::get_ThumbPrintWStr(IN OUT PCRYPT_DATA_BLOB pHashBlob) { 
    HRESULT hr = S_OK; 

    if (NULL == pHashBlob)
        return E_INVALIDARG; 

    if (NULL != m_hashBlobPendingRequest.pbData)
    {
        if (NULL != pHashBlob->pbData)
        {
            if (pHashBlob->cbData < m_hashBlobPendingRequest.cbData) { 
                hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
            }
            else { 
                CopyMemory(pHashBlob->pbData, m_hashBlobPendingRequest.pbData, m_hashBlobPendingRequest.cbData);
                hr = S_OK;
            }
        }

        pHashBlob->cbData = m_hashBlobPendingRequest.cbData; 
        return hr; 
    }

    else
    {
        if (NULL == m_pCertContextPendingRequest)
            return E_POINTER; 

        if (!CertGetCertificateContextProperty
            (m_pCertContextPendingRequest, 
             CERT_HASH_PROP_ID, 
             (LPVOID)(pHashBlob->pbData), 
             &(pHashBlob->cbData)))
        {
            return MY_HRESULT_FROM_WIN32(GetLastError()); 
        }

        return S_OK; 
    }
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::binaryToString(
    IN  LONG  Flags,
    IN  BSTR  strBinary,
    OUT BSTR *pstrEncoded)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobBinary;
    WCHAR            *pwszEncoded = NULL;

    blobBinary.pbData = (BYTE*)strBinary;
    blobBinary.cbData = SysStringByteLen(strBinary);

    hr = binaryBlobToString(Flags, &blobBinary, &pwszEncoded);
    if (S_OK != hr)
    {
        goto binaryBlobToStringError;
    }

    *pstrEncoded = SysAllocString(pwszEncoded);
    if (NULL == pstrEncoded)
    {
        hr = E_OUTOFMEMORY;
        goto SysAllocStringLenError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszEncoded)
    {
        MyCoTaskMemFree(pwszEncoded);
    }
    return hr;

TRACE_ERROR(binaryBlobToStringError);
TRACE_ERROR(SysAllocStringLenError);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::stringToBinary(
    IN  LONG  Flags,
    IN  BSTR  strEncoded,
    OUT BSTR *pstrBinary)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobBinary;

    ZeroMemory(&blobBinary, sizeof(blobBinary));

    hr = stringToBinaryBlob(Flags, (LPCWSTR)strEncoded, &blobBinary, NULL, NULL);
    if (S_OK != hr)
    {
        goto stringToBinaryBlobError;
    }
    *pstrBinary = SysAllocStringLen(
            (OLECHAR*)blobBinary.pbData, blobBinary.cbData);
    if (NULL == *pstrBinary)
    {
        hr = E_OUTOFMEMORY;
        goto SysAllocStringLenError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobBinary.pbData)
    {
        MyCoTaskMemFree(blobBinary.pbData);
    }
    return hr;

TRACE_ERROR(stringToBinaryBlobError);
TRACE_ERROR(SysAllocStringLenError);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addExtensionToRequest(
    IN  LONG  Flags,
    IN  BSTR  strName,
    IN  BSTR  strValue)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobValue;
    DWORD  cchStrValue = SysStringLen(strValue);
    BYTE   *pbExtVal = NULL;
    DWORD  cbExtVal = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (WCHAR*)strValue,
                    cchStrValue,
                    CRYPT_STRING_ANY,
                    pbExtVal,
                    &cbExtVal,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbExtVal)
        {

            break;
        }
        pbExtVal = (BYTE*)LocalAlloc(LMEM_FIXED, cbExtVal);
        if (NULL == pbExtVal)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    blobValue.pbData = pbExtVal;
    blobValue.cbData = cbExtVal;

    hr = addExtensionToRequestWStr(Flags, strName, &blobValue);
    if (S_OK != hr)
    {
        goto addExtensionToRequestWStrError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pbExtVal)
    {
        LocalFree(pbExtVal);
    }
    return hr;

TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(addExtensionToRequestWStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addAttributeToRequest(
    IN  LONG  Flags,
    IN  BSTR  strName,
    IN  BSTR  strValue)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobValue;
    DWORD  cchStrValue = SysStringLen(strValue);
    BYTE   *pbAttVal = NULL;
    DWORD  cbAttVal = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (WCHAR*)strValue,
                    cchStrValue,
                    CRYPT_STRING_ANY,
                    pbAttVal,
                    &cbAttVal,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbAttVal)
        {

            break;
        }
        pbAttVal = (BYTE*)LocalAlloc(LMEM_FIXED, cbAttVal);
        if (NULL == pbAttVal)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    blobValue.pbData = pbAttVal;
    blobValue.cbData = cbAttVal;

    hr = addAttributeToRequestWStr(Flags, strName, &blobValue);
    if (S_OK != hr)
    {
        goto addAttributeToRequestWStrError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pbAttVal)
    {
        LocalFree(pbAttVal);
    }
    return hr;

TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(addAttributeToRequestWStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addNameValuePairToRequest(
    IN  LONG  Flags, 
    IN  BSTR  strName,
    IN  BSTR  strValue)
{
    return addNameValuePairToRequestWStr(Flags, strName, strValue);
}

HRESULT STDMETHODCALLTYPE CCEnroll::addBlobPropertyToCertificate(
    IN  LONG   lPropertyId,
    IN  LONG   lFlags,
    IN  BSTR   strProperty)
{
    CRYPT_DATA_BLOB  blob;

    blob.pbData = (BYTE*)strProperty;
    blob.cbData = SysStringByteLen(strProperty);
    if (0x0 != (XECP_STRING_PROPERTY & lFlags))
    {

        blob.cbData += sizeof(WCHAR);
    }

    return addBlobPropertyToCertificateWStr(lPropertyId, lFlags, &blob);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::put_SignerCertificate(
    IN  BSTR  bstrCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pSignerCert = NULL;

    if (NULL != bstrCert)
    {
        hr = GetCertificateContextFromBStr(bstrCert, &pSignerCert);
        if (S_OK != hr)
        {
            goto GetCertificateContextFromBStrError;
        }
    }

    hr = SetSignerCertificate(pSignerCert);
    if (S_OK != hr)
    {
        goto SetSignerCertificateError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pSignerCert)
    {
        CertFreeCertificateContext(pSignerCert);
    }
    return hr;

TRACE_ERROR(GetCertificateContextFromBStrError)
TRACE_ERROR(SetSignerCertificateError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::resetExtensions()
{
    HRESULT hr = S_OK;

    FreeAllStackExtension();

    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::resetAttributes()
{
    HRESULT hr = S_OK;

    FreeAllStackAttribute();

    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createRequest(
    IN  LONG  Flags,
    IN  BSTR  strDNName,
    IN  BSTR  strUsage,
    OUT BSTR *pstrRequest)
{
    return createRequestWStrBStr(
                Flags,
                (LPCWSTR)strDNName,
                (LPCWSTR)strUsage,
                CRYPT_STRING_BASE64REQUESTHEADER,
                pstrRequest);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFileRequest(
    IN  LONG  Flags,
    IN  BSTR  strDNName,
    IN  BSTR  strUsage,
    IN  BSTR  strRequestFileName)
{
    return createFileRequestWStr(Flags, (LPCWSTR)strDNName, (LPCWSTR)strUsage, (LPCWSTR)strRequestFileName);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptResponse(
    IN  BSTR  bstrResponse)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;
    DWORD  cchStrResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    if (NULL == bstrResponse)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    cchStrResponse = SysStringLen(bstrResponse);

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (WCHAR*)bstrResponse,
                    cchStrResponse,
                    CRYPT_STRING_ANY,
                    blobResponse.pbData,
                    &blobResponse.cbData,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != blobResponse.pbData)
        {

            break;
        }
        blobResponse.pbData = (BYTE*)LocalAlloc(
                                        LMEM_FIXED, blobResponse.cbData);
        if (NULL == blobResponse.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    hr = acceptResponseBlob(&blobResponse);
    if (S_OK != hr)
    {
        goto acceptResponseBlobError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobResponse.pbData)
    {
        LocalFree(blobResponse.pbData);
    }
    return (hr);

TRACE_ERROR(acceptResponseBlobError)
TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptFileResponse(
    IN  BSTR  bstrResponseFileName)
{
    return acceptFileResponseWStr((LPCWSTR)bstrResponseFileName);
}

HRESULT
CCEnroll::GetCertFromResponseBlobToBStr(
    IN  CRYPT_DATA_BLOB  *pBlobResponse,
    OUT BSTR *pstrCert)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobCert;
    PCCERT_CONTEXT pCert = NULL;

    hr = getCertContextFromResponseBlob(
                pBlobResponse,
                &pCert);
    if (S_OK != hr)
    {
        goto getCertContextFromResponseBlobError;
    }

    assert(NULL != pCert);

    blobCert.pbData = pCert->pbCertEncoded;
    blobCert.cbData = pCert->cbCertEncoded;
    hr = BlobToBstring(&blobCert, CRYPT_STRING_BASE64HEADER, pstrCert);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pCert)
    {
        CertFreeCertificateContext(pCert);
    }
    return hr;

TRACE_ERROR(getCertContextFromResponseBlobError)
TRACE_ERROR(BlobToBstringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertFromResponse(
    IN  BSTR  strResponse,
    OUT BSTR *pstrCert)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    if (NULL == strResponse)
    {
        hr = E_POINTER;
        goto NullPointerError;
    }

    hr = BstringToBlob(strResponse, &blobResponse);
    if (S_OK != hr)
    {
        goto BstringToBlobError;
    }

    hr = GetCertFromResponseBlobToBStr(
                &blobResponse,
                pstrCert);
    if (S_OK != hr)
    {
        goto GetCertFromResponseBlobToBStrError;
    }

    hr = S_OK;
ErrorReturn:
    return hr;

TRACE_ERROR(NullPointerError)
TRACE_ERROR(BstringToBlobError)
TRACE_ERROR(GetCertFromResponseBlobToBStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertFromFileResponse(
    IN  BSTR  strResponseFileName,
    OUT BSTR *pstrCert)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    hr = xeStringToBinaryFromFile(
                (LPCWSTR)strResponseFileName,
                &blobResponse.pbData,
                &blobResponse.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    hr = GetCertFromResponseBlobToBStr(
                &blobResponse,
                pstrCert);
    if (S_OK != hr)
    {
        goto GetCertFromResponseBlobToBStrError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobResponse.pbData)
    {
        LocalFree(blobResponse.pbData);
    }
    return hr;

TRACE_ERROR(xeStringToBinaryFromFileError)
TRACE_ERROR(GetCertFromResponseBlobToBStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createPFX(
    IN  BSTR  strPassword,
    OUT BSTR *pstrPFX)
{
    if (0 != m_dwEnabledSafteyOptions) 
	return E_ACCESSDENIED; 
    return createPFXWStrBStr((LPCWSTR)strPassword, pstrPFX);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFilePFX(
    IN  BSTR  strPassword,
    IN  BSTR  strPFXFileName)
{
    return createFilePFXWStr((LPCWSTR)strPassword, (LPCWSTR)strPFXFileName);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::setPendingRequestInfo(
    IN  LONG  lRequestID,
    IN  BSTR  strCADNS,
    IN  BSTR  strCAName,
    IN  BSTR  strFriendlyName
    )
{
    return setPendingRequestInfoWStr(
                lRequestID,
                (LPCWSTR)strCADNS,
                (LPCWSTR)strCAName,
                (LPCWSTR)strFriendlyName);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::enumPendingRequest(
    IN  LONG     lIndex,
    IN  LONG     lDesiredProperty,
    OUT VARIANT *pvarProperty
    )
{
    CRYPT_DATA_BLOB  dataBlobProperty; 
    HRESULT          hr; 
    LONG             lProperty; 
    VARIANT          varProperty; 

    if (XEPR_ENUM_FIRST == lIndex) { 
        return enumPendingRequestWStr(XEPR_ENUM_FIRST, 0, NULL); 
    }

    if (lIndex < 0 || NULL == pvarProperty)
        return E_INVALIDARG; 

    memset(&varProperty,      0, sizeof(VARIANT));
    memset(&dataBlobProperty, 0, sizeof(CRYPT_DATA_BLOB)); 

    switch (lDesiredProperty) 
        { 
        case XEPR_REQUESTID: 
        case XEPR_VERSION:
            if (S_OK != (hr = enumPendingRequestWStr(lIndex, lDesiredProperty, &lProperty)))
                goto ErrorReturn;

            varProperty.vt   = VT_I4; 
            varProperty.lVal = lProperty; 
            *pvarProperty    = varProperty; 
            goto CommonReturn; 

        case XEPR_CANAME:       
        case XEPR_CAFRIENDLYNAME: 
        case XEPR_CADNS:          
        case XEPR_HASH:            
        case XEPR_V1TEMPLATENAME:  
        case XEPR_V2TEMPLATEOID:   
            dataBlobProperty.cbData = 0; 
            dataBlobProperty.pbData = NULL;

            hr = enumPendingRequestWStr(lIndex, lDesiredProperty, (LPVOID)&dataBlobProperty);
            if (S_OK != hr || 0 == dataBlobProperty.cbData)
                goto ErrorReturn; 

            dataBlobProperty.pbData = (LPBYTE)LocalAlloc(LPTR, dataBlobProperty.cbData); 
            if (NULL == dataBlobProperty.pbData)
                goto MemoryErr; 

            hr = enumPendingRequestWStr(lIndex, lDesiredProperty, (LPVOID)&dataBlobProperty);
            if (hr != S_OK)
                goto ErrorReturn; 

            varProperty.vt      = VT_BSTR; 
            varProperty.bstrVal = SysAllocStringByteLen((LPCSTR)dataBlobProperty.pbData, dataBlobProperty.cbData); 
            if (NULL == varProperty.bstrVal)
                goto MemoryErr; 

            *pvarProperty = varProperty; 
            goto CommonReturn; 

        case XEPR_DATE:            
            goto NotImplErr; 

        default: 
            goto InvalidArgErr; 
    }

 CommonReturn: 
    if (NULL != dataBlobProperty.pbData) { LocalFree(dataBlobProperty.pbData); } 
    return hr; 

 ErrorReturn:
    if (NULL != varProperty.bstrVal) { SysFreeString(varProperty.bstrVal); } 
    goto CommonReturn; 

SET_HRESULT(InvalidArgErr, E_INVALIDARG); 
SET_HRESULT(MemoryErr,     E_OUTOFMEMORY); 
SET_HRESULT(NotImplErr,    E_NOTIMPL); 
}

HRESULT STDMETHODCALLTYPE
CCEnroll::removePendingRequest(
    IN  BSTR bstrThumbPrint
    )
{
    CRYPT_DATA_BLOB hashBlob; 
    HRESULT         hr; 

    if (bstrThumbPrint == NULL)
        return E_INVALIDARG; 

    hashBlob.cbData = 0; 
    hashBlob.pbData = NULL;

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_ANY,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData); 
    if (NULL == hashBlob.pbData)
      goto MemoryErr; 

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_ANY,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hr = this->removePendingRequestWStr(hashBlob); 

 CommonReturn: 
    if (NULL != hashBlob.pbData) { LocalFree(hashBlob.pbData); } 
    return hr; 

 ErrorReturn: 
    goto CommonReturn; 

SET_HRESULT(MyCryptToBinaryErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(MemoryErr,          E_OUTOFMEMORY); 
}

HRESULT
myCertGetNameString(
    IN  PCCERT_CONTEXT pCert,
    IN  BOOL           fIssuer,
    OUT WCHAR        **ppwszName)
{
    HRESULT  hr;
    DWORD    dwFlags = fIssuer ? CERT_NAME_ISSUER_FLAG : 0;
    DWORD    dwTypePara;
    WCHAR   *pwszName = NULL;
    DWORD    cch = 0;

    while (TRUE)
    {
        cch = CertGetNameStringW(
                pCert,
                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                dwFlags,
                (void*)&dwTypePara,
                pwszName,
                cch);
        if (0 == cch)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertGetNameStringError;
        }
        if (NULL != pwszName)
        {

            break;
        }
        pwszName = (WCHAR*)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
        if (NULL == pwszName)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    *ppwszName = pwszName;
    pwszName = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszName)
    {
        LocalFree(pwszName);
    }
    return hr;

TRACE_ERROR(CertGetNameStringError)
TRACE_ERROR(OutOfMemoryError)
}

HRESULT CCEnroll::GetGoodCertContext(
    IN PCCERT_CONTEXT pCertContext,
    OUT PCCERT_CONTEXT *ppGoodCertContext)
{
    HRESULT hr;
    PCCERT_CONTEXT  pGoodCertContext = NULL;
    DWORD           cb;

    *ppGoodCertContext = NULL;

    if(pCertContext == NULL)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    if(CertGetCertificateContextProperty(
                pCertContext,
                CERT_KEY_PROV_INFO_PROP_ID,
                NULL,
                &cb))
    {

        pGoodCertContext = CertDuplicateCertificateContext(pCertContext);
        if (NULL == pGoodCertContext)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertDuplicateCertificateContextError;
        }
    }

    *ppGoodCertContext = pGoodCertContext;
    pGoodCertContext = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pGoodCertContext)
    {
        CertFreeCertificateContext(pGoodCertContext);
    }
    return hr;

TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(CertDuplicateCertificateContextError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetSignerCertificate(
    IN PCCERT_CONTEXT pCertContext)
{
    HRESULT hr;
    PCCERT_CONTEXT  pCertGoodContext = NULL;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetGoodCertContext(pCertContext, &pCertGoodContext);
    if (S_OK != hr)
    {
        goto GetGoodCertContextError;
    }
    if(NULL != m_pCertContextSigner)
    {
        CertFreeCertificateContext(m_pCertContextSigner);
    }
    m_pCertContextSigner = pCertGoodContext;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(GetGoodCertContextError)
}

HRESULT
VerifyPrivateKeyArchiveCertificate(
    IN PCCERT_CONTEXT pCert)
{
    HRESULT  hr;
    CERT_CHAIN_PARA ChainParams;
    CERT_CHAIN_POLICY_PARA ChainPolicy;
    CERT_CHAIN_POLICY_STATUS PolicyStatus;
    CERT_CHAIN_CONTEXT const *pCertChain = NULL;
    char *apszCAXchgOids[] = {szOID_KP_CA_EXCHANGE};
    WCHAR               *pwszSubject = NULL;
    WCHAR               *pwszIssuer = NULL;
    WCHAR               *pwszDesignedSubject = NULL;

    hr = myCertGetNameString(
                pCert,
                FALSE,
                &pwszSubject);
    if (S_OK != hr)
    {
        goto myCertGetNameStringError;
    }

    hr = myCertGetNameString(
                pCert,
                TRUE,
                &pwszIssuer);
    if (S_OK != hr)
    {
        goto myCertGetNameStringError;
    }

    hr = myAddNameSuffix(
                pwszIssuer,
                wszCNXCHGSUFFIX,
                cchCOMMONNAMEMAX_XELIB,
                &pwszDesignedSubject);
    if (S_OK != hr)
    {
        goto myAddNameSuffixError;
    }

    if (0 != wcscmp(pwszSubject, pwszDesignedSubject))
    {

        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    ZeroMemory(&ChainParams, sizeof(ChainParams));
    ChainParams.cbSize = sizeof(ChainParams);
    ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
    ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = apszCAXchgOids;
    ChainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(apszCAXchgOids);

    if (!MyCertGetCertificateChain(
                NULL,   
                pCert,   
                NULL,   
                NULL,   
                &ChainParams,   
                CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,  
                NULL,   
                &pCertChain))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertGetCertificateChainError;
    }

    ZeroMemory(&ChainPolicy, sizeof(ChainPolicy));
    ChainPolicy.cbSize = sizeof(ChainPolicy);
    ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;

    ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
    PolicyStatus.cbSize = sizeof(PolicyStatus);
    PolicyStatus.lChainIndex = -1;
    PolicyStatus.lElementIndex = -1;

    if (!MyCertVerifyCertificateChainPolicy(
                CERT_CHAIN_POLICY_BASE,
                pCertChain,
                &ChainPolicy,
                &PolicyStatus))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertVerifyCertificateChainPolicyError;
    }

    if (S_OK != PolicyStatus.dwError)
    {

        hr = PolicyStatus.dwError;
        goto CertVerifyCertificateChainPolicyError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pCertChain)
    {
        MyCertFreeCertificateChain(pCertChain);
    }
    if (NULL != pwszSubject)
    {
        LocalFree(pwszSubject);
    }
    if (NULL != pwszDesignedSubject)
    {
        LocalFree(pwszDesignedSubject);
    }
    if (NULL != pwszIssuer)
    {
        LocalFree(pwszIssuer);
    }
    return hr;

TRACE_ERROR(CertGetCertificateChainError)
TRACE_ERROR(CertVerifyCertificateChainPolicyError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(myCertGetNameStringError)
TRACE_ERROR(myAddNameSuffixError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::SetPrivateKeyArchiveCertificate(
    IN PCCERT_CONTEXT  pPrivateKeyArchiveCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pCert = NULL;

    if (NULL != pPrivateKeyArchiveCert)
    {

        pCert = CertDuplicateCertificateContext(pPrivateKeyArchiveCert);
        if (NULL == pCert)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertDuplicateCertificateContextError;
        }

        hr = VerifyPrivateKeyArchiveCertificate(pCert);
        if (S_OK != hr)
        {
            goto VerifyPrivateKeyArchiveCertificateError;
        }
    }

    EnterCriticalSection(&m_csXEnroll);

    if (NULL != m_PrivateKeyArchiveCertificate)
    {
        CertFreeCertificateContext(m_PrivateKeyArchiveCertificate);
    }
    m_PrivateKeyArchiveCertificate = pCert;
    pCert = NULL; 

    LeaveCriticalSection(&m_csXEnroll);

    hr = S_OK;
ErrorReturn:
    if (NULL != pCert)
    {
        CertFreeCertificateContext(pCert);
    }
    return (hr);

TRACE_ERROR(CertDuplicateCertificateContextError)
TRACE_ERROR(VerifyPrivateKeyArchiveCertificateError)
}

PCCERT_CONTEXT STDMETHODCALLTYPE
CCEnroll::GetPrivateKeyArchiveCertificate(void)
{
    PCCERT_CONTEXT pCert = NULL;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL != m_PrivateKeyArchiveCertificate)
    {
        pCert = CertDuplicateCertificateContext(m_PrivateKeyArchiveCertificate);
    }
    LeaveCriticalSection(&m_csXEnroll);

    return pCert;
}

HRESULT STDMETHODCALLTYPE
CCEnroll::binaryBlobToString(
    IN   LONG               Flags,
    IN   PCRYPT_DATA_BLOB   pblobBinary,
    OUT  LPWSTR            *ppwszString)
{
    HRESULT hr;
    WCHAR  *pwszEncoded = NULL;
    DWORD   dwEncoded = 0;

    while (TRUE)
    {
        if (!MyCryptBinaryToStringW(
                    pblobBinary->pbData,
                    pblobBinary->cbData,
                    Flags,
                    pwszEncoded,
                    &dwEncoded))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptBinaryToStringError;
        }
        if (NULL != pwszEncoded)
        {

            break;
        }

        pwszEncoded = (WCHAR*)MyCoTaskMemAlloc(dwEncoded * sizeof(WCHAR));
        if (NULL == pwszEncoded)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    *ppwszString = pwszEncoded;
    pwszEncoded = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszEncoded)
    {
        MyCoTaskMemFree(pwszEncoded);
    }
    return hr;

TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(MyCryptBinaryToStringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::stringToBinaryBlob(
    IN   LONG               Flags,
    IN   LPCWSTR            pwszString,
    OUT  PCRYPT_DATA_BLOB   pblobBinary,
    OUT  LONG              *pdwSkip,
    OUT  LONG              *pdwFlags)
{
    HRESULT  hr;
    size_t   nLength = wcslen(pwszString); 

    if (nLength*sizeof(WCHAR) > (DWORD)-1)
	goto InvalidArgError; 

    pblobBinary->pbData = NULL;
    pblobBinary->cbData = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    pwszString,
                    (DWORD)nLength, 
                    Flags,
                    pblobBinary->pbData,
                    &pblobBinary->cbData,
                    (DWORD*)pdwSkip,
                    (DWORD*)pdwFlags))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pblobBinary->pbData)
        {

            break;
        }
        pblobBinary->pbData = (BYTE*)MyCoTaskMemAlloc(pblobBinary->cbData);
        if (NULL == pblobBinary->pbData)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    hr = S_OK;
ErrorReturn:
    return hr;

SET_HRESULT(InvalidArgError, E_INVALIDARG);
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(MyCoTaskMemAllocError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addExtensionToRequestWStr(
    IN   LONG               Flags,
    IN   LPCWSTR            pwszName,
    IN   PCRYPT_DATA_BLOB   pblobValue)
{
    HRESULT hr = S_OK;
    CERT_EXTENSION ext;
    CERT_EXTENSION *pExt = NULL; 
    CHAR   *pszName = NULL;

    hr = xeWSZToSZ(pwszName, &pszName);
    if (S_OK != hr)
    {
        goto error;
    }

    while (NULL != (pExt = EnumStackExtension(pExt, TRUE)))
    {
	if (0 == strcmp(pszName, pExt->pszObjId))
	{

	    hr = MY_HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
	    goto error;
	}
    }

    if (0 == strcmp(pszName, szOID_KEY_USAGE))
    {
	EnterCriticalSection(&m_csXEnroll);
	m_fUseClientKeyUsage = TRUE;
	LeaveCriticalSection(&m_csXEnroll);
    }

    ZeroMemory(&ext, sizeof(ext));
    ext.fCritical = Flags;
    ext.pszObjId = pszName;
    ext.Value = *pblobValue;

    if(!CopyAndPushStackExtension(&ext, TRUE))
    {
	hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

error:
    if (NULL != pszName)
    {
        MyCoTaskMemFree(pszName);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addAttributeToRequestWStr(
    IN   LONG               , 
    IN   LPCWSTR            pwszName,
    IN   PCRYPT_DATA_BLOB   pblobValue)
{
    HRESULT hr = S_OK;
    CRYPT_ATTR_BLOB attrBlob;
    CRYPT_ATTRIBUTE attr;
    CHAR   *pszName = NULL;

    hr = xeWSZToSZ(pwszName, &pszName);
    if (S_OK != hr)
    {
        goto error;
    }

    ZeroMemory(&attr, sizeof(attr));
    attrBlob = *pblobValue;
    attr.pszObjId = pszName;
    attr.cValue = 1;
    attr.rgValue = &attrBlob;

    if(!CopyAndPushStackAttribute(&attr, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

error:
    if (NULL != pszName)
    {
        MyCoTaskMemFree(pszName);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addNameValuePairToRequestWStr(
    IN   LONG         , 
    IN   LPCWSTR      pwszName,
    IN   LPCWSTR      pwszValue)
{
    HRESULT hr = S_OK;

    assert(pwszName != NULL && pwszValue != NULL);

    CRYPT_ENROLLMENT_NAME_VALUE_PAIR nameValuePair =
            {const_cast<LPWSTR>(pwszName), const_cast<LPWSTR>(pwszValue)};
    CRYPT_ATTR_BLOB blobAttr;
    CRYPT_ATTRIBUTE attr = {szOID_ENROLLMENT_NAME_VALUE_PAIR, 1, &blobAttr};

    memset(&blobAttr, 0, sizeof(CRYPT_ATTR_BLOB));

    hr = xeEncodeNameValuePair(
                &nameValuePair,
                &blobAttr.pbData,
                &blobAttr.cbData);
    if (S_OK != hr)
    {
        goto error;
    }

    if(!CopyAndPushStackAttribute(&attr, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

error:
    if (NULL != blobAttr.pbData)
    {
        MyCoTaskMemFree(blobAttr.pbData);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE CCEnroll::addBlobPropertyToCertificateWStr(
    IN  LONG               lPropertyId,
    IN  LONG               lFlags,
    IN  PCRYPT_DATA_BLOB   pBlobProp)
{
    HRESULT      hr;
    PPROP_STACK  pProp;
    PPROP_STACK  pPropEle = NULL;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == pBlobProp ||
        NULL == pBlobProp->pbData ||
        0 == pBlobProp->cbData)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    if (0 != m_dwEnabledSafteyOptions && !IsDesiredProperty(lPropertyId))
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    pProp = EnumStackProperty(NULL);
    while (NULL != pProp)
    {
        if (pProp->lPropId == lPropertyId)
        {

            hr = MY_HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
            goto PropertyExistError;
        }
        pProp = EnumStackProperty(pProp);
    }

    pPropEle = (PPROP_STACK)LocalAlloc(LMEM_ZEROINIT, sizeof(PROP_STACK));
    if (NULL == pPropEle)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }

    pPropEle->lPropId = lPropertyId;
    pPropEle->lFlags = lFlags;
    pPropEle->prop.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, pBlobProp->cbData);
    if (NULL == pPropEle->prop.pbData)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    CopyMemory(pPropEle->prop.pbData, pBlobProp->pbData, pBlobProp->cbData);
    pPropEle->prop.cbData = pBlobProp->cbData;

    pPropEle->pNext = m_pPropStack;
    m_pPropStack = pPropEle; 
    m_cPropStack++; 
    pPropEle = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pPropEle)
    {
        if (NULL != pPropEle->prop.pbData)
        {
            LocalFree(pPropEle->prop.pbData);
        }
        LocalFree(pPropEle);
    }

    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(PropertyExistError)
TRACE_ERROR(OutOfMemoryError)
}

PPROP_STACK
CCEnroll::EnumStackProperty(PPROP_STACK pProp)
{
    EnterCriticalSection(&m_csXEnroll);

    if(NULL == pProp)
    {

        pProp = m_pPropStack;
    }
    else
    {
        pProp = pProp->pNext;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return pProp;
}

HRESULT STDMETHODCALLTYPE CCEnroll::resetBlobProperties()
{
    PPROP_STACK  pPropEle;
    PPROP_STACK  pPropNext;

    EnterCriticalSection(&m_csXEnroll);

    pPropEle = m_pPropStack;
    while (NULL != pPropEle)
    {

        pPropNext = EnumStackProperty(pPropEle);

        if (NULL != pPropEle->prop.pbData)
        {
            LocalFree(pPropEle->prop.pbData);
        }
        LocalFree(pPropEle);
        pPropEle = pPropNext;
    }
    m_pPropStack = NULL;
    m_cPropStack = 0;

    LeaveCriticalSection(&m_csXEnroll);
    return S_OK;
}

HRESULT
CCEnroll::GetKeyArchivePKCS7(
    OUT CRYPT_ATTR_BLOB *pBlobKeyArchivePKCS7)
{
    HRESULT    hr;
    HCRYPTPROV hProv;
    HCRYPTKEY  hKey = NULL;
    BYTE      *pBlobPrivateKey = NULL;
    DWORD      cBlobPrivateKey = 0;
    CRYPT_ENCRYPT_MESSAGE_PARA cemp;
    ALG_ID  algId[] = {CALG_3DES, CALG_RC4, CALG_RC2, ALG_TYPE_ANY};
    CRYPT_OID_INFO const *pOidInfo = NULL;
    DWORD i = 0;

    pBlobKeyArchivePKCS7->pbData = NULL;
    pBlobKeyArchivePKCS7->cbData = 0;

    EnterCriticalSection(&m_csXEnroll);

    assert(NULL != m_PrivateKeyArchiveCertificate);

    PCCERT_CONTEXT apCert[] = {m_PrivateKeyArchiveCertificate};

    hProv = GetProv(0); 
    if (NULL == hProv)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CryptAcquireContextError;
    }

    if (NULL == m_hCachedKey)
    {

        if(!CryptGetUserKey(
                    hProv,
                    m_keyProvInfo.dwKeySpec,
                    &hKey))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptGetUserKeyError;
        }
    }

    while (TRUE)
    {
        if (!CryptExportKey(
                NULL != hKey ? hKey : m_hCachedKey,
                NULL, 
                PRIVATEKEYBLOB,
                0,
                pBlobPrivateKey,
                &cBlobPrivateKey))
        {

            hr = XENROLL_E_KEY_NOT_EXPORTABLE;
            goto CryptExportKeyError;
        }
        if (NULL != pBlobPrivateKey)
        {

            break;
        }
        pBlobPrivateKey = (BYTE*)MyCoTaskMemAlloc(cBlobPrivateKey);
        if (NULL == pBlobPrivateKey)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if (NULL == m_hCachedKey)
    {

        if (0x0 == (m_dwGenKeyFlags & CRYPT_EXPORTABLE))
        {

            DWORD dwFlags = 0;
            DWORD dwSize = sizeof(dwFlags);
            if (!CryptGetKeyParam(
                    hKey,
                    KP_PERMISSIONS,
                    (BYTE*)&dwFlags,
                    &dwSize,
                    0))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto CryptGetKeyParamError;
            }
#if DBG
            assert(dwSize = sizeof(dwFlags));

            assert(0x0 != (dwFlags & CRYPT_EXPORT));
#endif

            dwFlags = dwFlags & (~CRYPT_EXPORT);
            if (!CryptSetKeyParam(
                    hKey,
                    KP_PERMISSIONS,
                    (BYTE*)&dwFlags,
                    0))
            {

                hr = S_OK; 
            }
        }
    }

    ZeroMemory(&cemp, sizeof(cemp)); 
    cemp.cbSize = sizeof(cemp);
    cemp.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
    hr = S_OK; 

    while (ALG_TYPE_ANY != algId[i])
    {
        pOidInfo = xeCryptFindOIDInfo(
                        CRYPT_OID_INFO_ALGID_KEY,
                        &algId[i],
                        CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
        if (NULL != pOidInfo)
        {
            cemp.ContentEncryptionAlgorithm.pszObjId = 
                                const_cast<char *>(pOidInfo->pszOID);

            while (TRUE)
            {
                if (!CryptEncryptMessage(
                        &cemp,
                        sizeof(apCert)/sizeof(apCert[0]),
                        apCert,
                        pBlobPrivateKey,
                        cBlobPrivateKey,
                        pBlobKeyArchivePKCS7->pbData,
                        &pBlobKeyArchivePKCS7->cbData))
                {

                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
#ifdef DBG
                    assert(NULL == pBlobKeyArchivePKCS7->pbData);
#endif
                    break; 
                }
                if (NULL != pBlobKeyArchivePKCS7->pbData)
                {

                    hr = S_OK;
                    break;
                }
                pBlobKeyArchivePKCS7->pbData = (BYTE*)MyCoTaskMemAlloc(
                                        pBlobKeyArchivePKCS7->cbData);
                if (NULL == pBlobKeyArchivePKCS7->pbData)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
            }
            if (S_OK == hr)
            {

                break;
            }
        }
        ++i;
    }
    if (NULL == pOidInfo)
    {
        hr = CRYPT_E_NOT_FOUND;
        goto CryptElemNotFoundError;
    }

    if (S_OK != hr)
    {
        goto CryptEncryptMessageError;
    }

    hr = S_OK;
ErrorReturn:

    if (NULL != m_hCachedKey)
    {
        CryptDestroyKey(m_hCachedKey);
        m_hCachedKey = NULL; 
    }

    LeaveCriticalSection(&m_csXEnroll);
    if (NULL != pBlobPrivateKey)
    {
	SecureZeroMemory(pBlobPrivateKey, cBlobPrivateKey);
        MyCoTaskMemFree(pBlobPrivateKey);
    }
    if (NULL != hKey)
    {
        CryptDestroyKey(hKey);
    }
    return hr;

TRACE_ERROR(CryptEncryptMessageError)
TRACE_ERROR(CryptAcquireContextError)
TRACE_ERROR(CryptGetUserKeyError)
TRACE_ERROR(CryptExportKeyError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptElemNotFoundError)

TRACE_ERROR(CryptGetKeyParamError)
}

HRESULT
GetKeyProvInfoFromCert(
    IN  PCCERT_CONTEXT    pCert,
    OUT DWORD            *pdwKeySpec,
    OUT HCRYPTPROV       *phProv)
{
    HRESULT hr;
    CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
    DWORD                cb = 0;
    HCRYPTPROV           hProv = NULL;

    if (NULL == pCert || NULL == phProv || NULL == pdwKeySpec)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    while (TRUE)
    {
        if(!CertGetCertificateContextProperty(
                pCert,
                CERT_KEY_PROV_INFO_PROP_ID,
                pKeyProvInfo,
                &cb))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertGetCertificateContextPropertyError;
        }
        if (NULL != pKeyProvInfo)
        {

            break;
        }
        pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)LocalAlloc(LMEM_FIXED, cb);
        if (NULL == pKeyProvInfo)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if (!CryptAcquireContextU(
            &hProv,
            pKeyProvInfo->pwszContainerName,
            pKeyProvInfo->pwszProvName,
            pKeyProvInfo->dwProvType,
            pKeyProvInfo->dwFlags))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CryptAcquireContextUError;
    }
    *phProv = hProv;
    hProv = NULL;
    *pdwKeySpec = pKeyProvInfo->dwKeySpec;

    hr = S_OK;
ErrorReturn:
    if (NULL != pKeyProvInfo)
    {
        LocalFree(pKeyProvInfo);
    }
    if (NULL != hProv)
    {
        CryptReleaseContext(hProv, 0);
    }
    return hr;

TRACE_ERROR(CryptAcquireContextUError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CertGetCertificateContextPropertyError)
TRACE_ERROR(InvalidArgError)
}

HRESULT
xeCreateKeyArchivalHashAttribute(
    IN  CRYPT_HASH_BLOB     *pBlobKAHash,
    OUT CRYPT_ATTR_BLOB     *pBlobKAAttr)
{
    HRESULT hr;
    BYTE   *pbData = NULL;
    DWORD   cbData = 0;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                X509_OCTET_STRING,
                (void*)pBlobKAHash,
                pbData,
                &cbData))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptEncodeObjectError;
        }

        if (NULL != pbData)
        {

            break;
        }

        pbData = (BYTE*)LocalAlloc(LMEM_FIXED, cbData);
        if (NULL == pbData)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }
    pBlobKAAttr->pbData = pbData;
    pBlobKAAttr->cbData = cbData;
    pbData = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pbData)
    {
        LocalFree(pbData);
    }
    return hr;

TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(LocalAllocError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createRequestWStr(
    IN   LONG              Flags,
    IN   LPCWSTR           pwszDNName,
    IN   LPCWSTR           pwszUsage,
    OUT  PCRYPT_DATA_BLOB  pblobRequest)
{
    HRESULT hr;
    CRYPT_DATA_BLOB  blobPKCS10;
    CRYPT_ATTR_BLOB  blobKeyArchivePKCS7;
    ALG_ID           rgAlg[2];
    PCCRYPT_OID_INFO pOidInfo;
    CERT_EXTENSION  *rgExt = NULL;
    DWORD            cExt = 0;
    CERT_EXTENSION  *pExt = NULL; 
    CRYPT_ATTRIBUTE  *rgAttr = NULL;
    DWORD            cAttr = 0;
    CRYPT_ATTRIBUTE  *pAttr = NULL; 
    CRYPT_ATTRIBUTES rgAttributes;
    CRYPT_ATTRIBUTE  *rgUnauthAttr = NULL; 
    DWORD            cUnauthAttr = 0; 
    DWORD            cb;
    HCRYPTPROV       hProvSigner = NULL;
    DWORD            dwKeySpecSigner = 0;
    PCCERT_CONTEXT   pCertSigner = NULL; 
    HCRYPTPROV       hRequestProv = NULL;
    BYTE            *pbSubjectKeyHash = NULL;
    DWORD            cbSubjectKeyHash = 0;
    CRYPT_HASH_BLOB  blobKAHash;
    CRYPT_ATTR_BLOB  blobKAHashAttr;
    CRYPT_ATTRIBUTE  attrKAHash =
        {szOID_ENCRYPTED_KEY_HASH, 1, &blobKAHashAttr};

    ZeroMemory(&blobPKCS10, sizeof(blobPKCS10));
    ZeroMemory(&blobKeyArchivePKCS7, sizeof(blobKeyArchivePKCS7));
    ZeroMemory(&blobKAHash, sizeof(blobKAHash));
    ZeroMemory(&blobKAHashAttr, sizeof(blobKAHashAttr));

    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions && NULL != m_PrivateKeyArchiveCertificate && m_fUseExistingKey) { 
	hr = E_ACCESSDENIED; 
	goto AccessDeniedError; 
    }

    m_fNewRequestMethod = TRUE;  
    m_fOID_V2 = TRUE;
    m_fCMCFormat = FALSE;
    m_fHonorIncludeSubjectKeyID = FALSE;

    switch (Flags)
    {
        case XECR_CMC:
        {
            if (NULL != m_pCertContextRenewal &&
                NULL != m_pCertContextSigner)
            {

                hr = MY_HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                goto NotSupportedError;
            }

            m_fCMCFormat = TRUE;
            m_fHonorIncludeSubjectKeyID = TRUE;

            hr = createPKCS10WStr(
                        pwszDNName,
                        pwszUsage,     
                        &blobPKCS10);
            if(S_OK != hr)
            {
                goto createPKCS10WStrError;
            }

            m_fCMCFormat = FALSE;

            cb = CountStackExtension(TRUE) * sizeof(CERT_EXTENSION);
            if (0 < cb)
            {
                rgExt = (CERT_EXTENSION*)LocalAlloc(LMEM_FIXED, cb);
                if (NULL == rgExt)
                {    
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                ZeroMemory(rgExt, cb);
                while(NULL != (pExt = EnumStackExtension(pExt, TRUE)))
                {
                    rgExt[cExt] = *pExt;
                    cExt++;
                }
            }

            cb = CountStackAttribute(TRUE) * sizeof(CRYPT_ATTRIBUTE);
            if (NULL != m_PrivateKeyArchiveCertificate)
            {

                cb += sizeof(CRYPT_ATTRIBUTE);
            }
            if (0 < cb)
            {
                rgAttr = (CRYPT_ATTRIBUTE*)LocalAlloc(LMEM_FIXED, cb);
                if (NULL == rgAttr)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                ZeroMemory(rgAttr, cb);
                while(NULL != (pAttr = EnumStackAttribute(pAttr, TRUE)))
                {
                    rgAttr[cAttr] = *pAttr;
                    cAttr++;
                }
                rgAttributes.rgAttr = rgAttr;
                rgAttributes.cAttr = cAttr;
            }

            if (NULL != m_PrivateKeyArchiveCertificate)
            {
                hr = GetKeyArchivePKCS7(&blobKeyArchivePKCS7);
                if (S_OK != hr)
                {
                    goto GetKeyArchivePKCS7Error;
                }
                rgUnauthAttr = (CRYPT_ATTRIBUTE*)LocalAlloc(LMEM_FIXED,
                                        sizeof(CRYPT_ATTRIBUTE));
                if (NULL == rgUnauthAttr)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                rgUnauthAttr->pszObjId = szOID_ARCHIVED_KEY_ATTR;
                rgUnauthAttr->cValue = 1;
                rgUnauthAttr->rgValue = &blobKeyArchivePKCS7;
                ++cUnauthAttr;

                hr = myCalculateKeyArchivalHash(
                            blobKeyArchivePKCS7.pbData,
                            blobKeyArchivePKCS7.cbData,
                            &blobKAHash.pbData,
                            &blobKAHash.cbData);
                if (S_OK != hr)
                {
                    goto myCalculateKeyArchivalHashError;
                }

                if (!CertSetCertificateContextProperty(
                        m_pCertContextPendingRequest, 
                        CERT_ARCHIVED_KEY_HASH_PROP_ID,
                        0,
                        &blobKAHash))
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto CertSetCertificateContextPropertyError;
                }

                hr = xeCreateKeyArchivalHashAttribute(
                            &blobKAHash,
                            &blobKAHashAttr);
                if (S_OK != hr)
                {
                    goto xeCreateKeyArchivalHashAttributeError;
                }

                rgAttr[cAttr] = attrKAHash;
                cAttr++;
                rgAttributes.rgAttr = rgAttr;
                rgAttributes.cAttr = cAttr;
            }

            if (!GetCapiHashAndSigAlgId(rgAlg))
            {
                hr = NTE_BAD_ALGID;
                goto GetCapiHashAndSigAlgIdError;
            }
            pOidInfo = xeCryptFindOIDInfo(
                            CRYPT_OID_INFO_ALGID_KEY,
                            (void*)rgAlg, 
                            CRYPT_HASH_ALG_OID_GROUP_ID);
            if (NULL == pOidInfo)
            {
                goto xeCryptFindOIDInfoError;
            }

            if (NULL != m_pCertContextRenewal)
            {
                pCertSigner = m_pCertContextRenewal;
            }
            if (NULL != m_pCertContextSigner)
            {
                pCertSigner = m_pCertContextSigner;
            }
            if (NULL != pCertSigner)
            {

                hr = GetKeyProvInfoFromCert(
                                pCertSigner,
                                &dwKeySpecSigner,
                                &hProvSigner);
                if (S_OK != hr)
                {
                    goto GetKeyProvInfoFromCertError;
                }
            }

            if (m_fIncludeSubjectKeyID)
            {
                hr = myGetPublicKeyHash(
                            NULL,
                            m_pPublicKeyInfo,
                            &pbSubjectKeyHash,
                            &cbSubjectKeyHash);
                if (S_OK != hr)
                {
                    goto myGetPublicKeyHashError;
                }
            }
            hRequestProv = GetProv(0);
            if (NULL == hRequestProv)
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto GetProvError;
            }

            hr = BuildCMCRequest(
                        m_lClientId,
                        FALSE,       
                        blobPKCS10.pbData,
                        blobPKCS10.cbData,
                        rgExt,
                        cExt,
                        (0 != cAttr) ? &rgAttributes : NULL,
                        (0 != cAttr) ? 1 : 0,
                        rgUnauthAttr,
                        cUnauthAttr,
                        pbSubjectKeyHash,
                        cbSubjectKeyHash,
                        hRequestProv,
                        m_keyProvInfo.dwKeySpec,
                        pOidInfo->pszOID,
                        pCertSigner,
                        hProvSigner,
                        dwKeySpecSigner,
                        NULL, 
                        &pblobRequest->pbData,
                        &pblobRequest->cbData);
            if (S_OK != hr)
            {
                goto BuildCMCRequestError;
            }
        }
        break;

        case XECR_PKCS7:
            if ((NULL == m_pCertContextRenewal &&
                 NULL == m_pCertContextSigner) ||
                NULL != m_PrivateKeyArchiveCertificate)
            {

                hr = E_INVALIDARG;
                goto InvalidArgError;
            }

            hr = createPKCS10WStr(
                        pwszDNName,
                        pwszUsage,     
                        pblobRequest);
            if(S_OK != hr)
            {
                goto createPKCS10WStrError;
            }
        break;

        case XECR_PKCS10_V1_5:
            m_fOID_V2 = FALSE;

        case XECR_PKCS10_V2_0:

            if (NULL != m_PrivateKeyArchiveCertificate)
            {

                hr = E_INVALIDARG;
                goto InvalidArgError;
            }
            m_fHonorRenew = FALSE; 

            m_fHonorIncludeSubjectKeyID = TRUE;

            hr = createPKCS10WStr(
                        pwszDNName,
                        pwszUsage,     
                        pblobRequest);
            if(S_OK != hr)
            {
                goto createPKCS10WStrError;
            }
        break;

        default:
            hr = E_INVALIDARG;
            goto InvalidArgError;
        break;
    }

    if(m_wszPVKFileName[0] != 0 && !m_fUseExistingKey)
    {

        GetProv(CRYPT_DELETEKEYSET);
    }

    hr = S_OK;
ErrorReturn:
    m_fNewRequestMethod = FALSE; 
    m_fOID_V2 = FALSE; 
    m_fHonorRenew = TRUE; 
    m_fHonorIncludeSubjectKeyID = TRUE; 
    LeaveCriticalSection(&m_csXEnroll);

    if (NULL != rgExt)
    {
        LocalFree(rgExt);
    }
    if (NULL != rgAttr)
    {
        LocalFree(rgAttr);
    }
    if (NULL != rgUnauthAttr)
    {
        LocalFree(rgUnauthAttr);
    }
    if (NULL != blobKeyArchivePKCS7.pbData)
    {
        MyCoTaskMemFree(blobKeyArchivePKCS7.pbData);
    }
    if (NULL != blobPKCS10.pbData)
    {
        MyCoTaskMemFree(blobPKCS10.pbData);
    }
    if (NULL != hProvSigner)
    {
        CryptReleaseContext(hProvSigner, 0);
    }
    if (NULL != pbSubjectKeyHash)
    {
        LocalFree(pbSubjectKeyHash);
    }
    if (NULL != blobKAHash.pbData)
    {
        LocalFree(blobKAHash.pbData);
    }
    if (NULL != blobKAHashAttr.pbData)
    {
        LocalFree(blobKAHashAttr.pbData);
    }
    return hr;

TRACE_ERROR(AccessDeniedError);
TRACE_ERROR(createPKCS10WStrError)
TRACE_ERROR(BuildCMCRequestError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(GetCapiHashAndSigAlgIdError)
TRACE_ERROR(GetKeyArchivePKCS7Error)
TRACE_ERROR(xeCryptFindOIDInfoError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(GetKeyProvInfoFromCertError)
TRACE_ERROR(NotSupportedError)
TRACE_ERROR(GetProvError)
TRACE_ERROR(myGetPublicKeyHashError)
TRACE_ERROR(CertSetCertificateContextPropertyError)
TRACE_ERROR(myCalculateKeyArchivalHashError)
TRACE_ERROR(xeCreateKeyArchivalHashAttributeError)
}

HRESULT
CCEnroll::BlobToBstring(
    IN   CRYPT_DATA_BLOB   *pBlob,
    IN   DWORD              dwFlag,
    OUT  BSTR              *pBString)
{
    HRESULT           hr;
    WCHAR            *pwszB64;
    DWORD             cch;

    *pBString = NULL;

    pwszB64 = NULL;
    cch = 0;
    while (TRUE)
    {
        if (!MyCryptBinaryToStringW(
                pBlob->pbData,
                pBlob->cbData,
                dwFlag,
                pwszB64,
                &cch))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptBinaryToStringWError;
        }
        if (NULL != pwszB64)
        {

            break;
        }
        pwszB64 = (WCHAR *)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
        if (NULL == pwszB64)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    *pBString = SysAllocStringLen(pwszB64, cch);
    if(NULL == *pBString)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        hr = E_OUTOFMEMORY;
        goto SysAllocStringLenError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszB64)
    {
        LocalFree(pwszB64);
    }
    return(hr);

TRACE_ERROR(MyCryptBinaryToStringWError)
TRACE_ERROR(SysAllocStringLenError)
TRACE_ERROR(OutOfMemoryError)
}

HRESULT
CCEnroll::BstringToBlob(
    IN  BSTR               bString,
    OUT CRYPT_DATA_BLOB   *pBlob)
{
    HRESULT  hr;

    assert(NULL != pBlob);

    pBlob->pbData = NULL;
    pBlob->cbData = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (LPCWSTR)bString,
                    SysStringLen(bString),
                    CRYPT_STRING_ANY,
                    pBlob->pbData,
                    &pBlob->cbData,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pBlob->pbData)
        {
            break; 
        }
        pBlob->pbData = (BYTE*)MyCoTaskMemAlloc(pBlob->cbData);
        if (NULL == pBlob->pbData)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    hr = S_OK;
ErrorReturn:
    return(hr);

TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(MyCryptStringToBinaryWError)
}

HRESULT
CCEnroll::createRequestWStrBStr(
    IN   LONG              Flags,
    IN   LPCWSTR           pwszDNName,
    IN   LPCWSTR           pwszUsage,
    IN   DWORD             dwFlag,
    OUT  BSTR __RPC_FAR   *pbstrRequest)
{
    HRESULT           hr;
    CRYPT_DATA_BLOB   blobRequest;

    memset(&blobRequest, 0, sizeof(blobRequest));

    hr = createRequestWStr(Flags, pwszDNName, pwszUsage, &blobRequest);
    if (S_OK != hr)
    {
        goto createRequestWStrError;
    }

    hr = BlobToBstring(&blobRequest, dwFlag, pbstrRequest);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != blobRequest.pbData)
    {
        MyCoTaskMemFree(blobRequest.pbData);
    }
    return(hr);

TRACE_ERROR(createRequestWStrError)
TRACE_ERROR(BlobToBstringError)
}

HRESULT
CCEnroll::BStringToFile(
    IN BSTR         bString,
    IN LPCWSTR      pwszFileName)
{
    HRESULT hr;
    HANDLE  hFile = NULL;
    DWORD   cb = 0;
    LPSTR   sz = NULL;
    size_t  nLength; 

    sz = MBFromWide(bString);
    if(NULL == sz)
    {
        hr = E_OUTOFMEMORY;
        goto MBFromWideError;
    }

    nLength = strlen(sz); 
    if (nLength > (DWORD)-1)
	goto InvalidArgError; 

    hFile = CreateFileSafely(pwszFileName);
    if (NULL == hFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CreateFileFileSafelyError;
    }

    if(!WriteFile(
        hFile,
        sz,
	(DWORD)nLength, 
        &cb,
        NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto WriteFileError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != hFile)
    {
        CloseHandle(hFile);
    }
    if(NULL != sz)
    {
        MyCoTaskMemFree(sz);
    }
    return(hr);

TRACE_ERROR(CreateFileFileSafelyError)
SET_HRESULT(InvalidArgError, E_INVALIDARG);
TRACE_ERROR(MBFromWideError)
TRACE_ERROR(WriteFileError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFileRequestWStr(
    IN   LONG        Flags,
    IN   LPCWSTR     pwszDNName,
    IN   LPCWSTR     pwszUsage,
    IN   LPCWSTR     pwszRequestFileName)
{
    HRESULT hr;
    BSTR    bstrRequest = NULL;

    hr = createRequestWStrBStr(
                Flags,
                pwszDNName,
                pwszUsage,
                CRYPT_STRING_BASE64REQUESTHEADER,
                &bstrRequest);
    if(S_OK != hr)
    {
        goto createRequestWStrBStrError;
    }

    hr = BStringToFile(bstrRequest, pwszRequestFileName);
    if (S_OK != hr)
    {
        goto BStringToFileError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != bstrRequest)
    {
        SysFreeString(bstrRequest);
    }
    return(hr);

TRACE_ERROR(createRequestWStrBStrError)
TRACE_ERROR(BStringToFileError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptResponseBlob(
    IN   PCRYPT_DATA_BLOB   pblobResponse)
{
    HRESULT hr_old = S_OK;
    HRESULT hr;
    XCMCRESPONSE *prgResponse = NULL;
    DWORD         cResponse = 0;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == pblobResponse)
    {
        hr = E_POINTER;
        goto NullPointerError;
    }
    if (NULL == pblobResponse->pbData ||
        0 == pblobResponse->cbData)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));

    hr_old = ParseCMCResponse(
                pblobResponse->pbData,
                pblobResponse->cbData,
                NULL,
                &prgResponse,
                &cResponse);

    if (S_OK == hr_old)
    {
        if (1 < cResponse)
        {

            hr = MY_HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            goto NotSupportedError;
        }
#if DBG

        assert(1 == cResponse);
#endif 

        if (CMC_STATUS_SUCCESS != prgResponse->StatusInfo.dwStatus)
        {
            hr = prgResponse->StatusInfo.dwStatus; 
            goto CMCResponseStatusError;
        }

        if (NULL != prgResponse->pbEncryptedKeyHash)
        {
            m_blobResponseKAHash.pbData = prgResponse->pbEncryptedKeyHash;
            m_blobResponseKAHash.cbData = prgResponse->cbEncryptedKeyHash;
        }
    }

    hr = acceptPKCS7Blob(pblobResponse);
    if (S_OK != hr)
    {
        if (S_OK != hr_old)
        {

            hr = hr_old;
        }
        goto acceptPKCS7BlobError;
    }

    hr = S_OK;
ErrorReturn:

    ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));
    LeaveCriticalSection(&m_csXEnroll);
    if (NULL != prgResponse)
    {
        FreeCMCResponse(prgResponse, cResponse);
    }
    return hr;

TRACE_ERROR(acceptPKCS7BlobError)
TRACE_ERROR(CMCResponseStatusError)
TRACE_ERROR(NotSupportedError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(NullPointerError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptFileResponseWStr(
    IN   LPCWSTR     pwszResponseFileName)
{
    HRESULT     hr;
    CRYPT_DATA_BLOB  blob;

    ZeroMemory(&blob, sizeof(blob));

    hr = xeStringToBinaryFromFile(
                pwszResponseFileName,
                &blob.pbData,
                &blob.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    hr = acceptResponseBlob(&blob);

ErrorReturn:
    if (NULL != blob.pbData)
    {
        LocalFree(blob.pbData);
    }
    return(hr);

TRACE_ERROR(xeStringToBinaryFromFileError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertContextFromResponseBlob(
    IN   PCRYPT_DATA_BLOB   pblobResponse,
    OUT  PCCERT_CONTEXT    *ppCertContext)
{
    HRESULT hr;

    if (NULL == ppCertContext)
    {
        hr = E_POINTER;
        goto NullPointerError;
    }

    hr = GetEndEntityCert(pblobResponse, FALSE, ppCertContext);
    if (S_OK != hr)
    {
        goto GetEndEntityCertError;
    }

    hr = S_OK;
ErrorReturn:
    return hr;

TRACE_ERROR(NullPointerError)
TRACE_ERROR(GetEndEntityCertError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertContextFromFileResponseWStr(
    IN   LPCWSTR          pwszResponseFileName,
    OUT  PCCERT_CONTEXT  *ppCertContext)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    hr = xeStringToBinaryFromFile(
                pwszResponseFileName,
                &blobResponse.pbData,
                &blobResponse.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    hr = getCertContextFromResponseBlob(
                &blobResponse,
                ppCertContext);
    if (S_OK != hr)
    {
        goto getCertContextFromResponseBlobError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobResponse.pbData)
    {
        LocalFree(blobResponse.pbData);
    }
    return hr;

TRACE_ERROR(xeStringToBinaryFromFileError)
TRACE_ERROR(getCertContextFromResponseBlobError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createPFXWStr(
    IN   LPCWSTR           pwszPassword,
    OUT  PCRYPT_DATA_BLOB  pblobPFX)
{
    HRESULT hr;
    HCERTSTORE hMemStore = NULL;
    DWORD i;
    CERT_CHAIN_CONTEXT const *pCertChainContext = NULL;
    CERT_CHAIN_PARA CertChainPara;
    CERT_SIMPLE_CHAIN *pSimpleChain;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == pblobPFX)
    {
        goto EPointerError;
    }

    if (NULL == m_pCertContextStatic)
    {
        hr = E_UNEXPECTED;
        goto UnexpectedError;
    }

    hMemStore = CertOpenStore(
                    CERT_STORE_PROV_MEMORY,
                    X509_ASN_ENCODING,
                    NULL,
                    0,
                    NULL);
    if (NULL == hMemStore)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertOpenStoreError;
    }

    ZeroMemory(&CertChainPara, sizeof(CertChainPara));
    CertChainPara.cbSize = sizeof(CertChainPara);

    if (!MyCertGetCertificateChain(
                HCCE_CURRENT_USER,
                m_pCertContextStatic,
                NULL,
                NULL,
                &CertChainPara,
                0,
                NULL,
                &pCertChainContext))
    {

        hr = MY_HRESULT_FROM_WIN32(GetLastError());

        if (!MyCertGetCertificateChain(
                    HCCE_LOCAL_MACHINE,
                    m_pCertContextStatic,
                    NULL,
                    NULL,
                    &CertChainPara,
                    0,
                    NULL,
                    &pCertChainContext))
        {

            goto MyCertGetCertificateChainError;
        }
    }

    if (0 == pCertChainContext->cChain)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
        goto NoCertificateChainError;
    }

    pSimpleChain = pCertChainContext->rgpChain[0];
    for (i = 0; i < pSimpleChain->cElement; i++)
    {
        if (!CertAddCertificateContextToStore(
                hMemStore,
                pSimpleChain->rgpElement[i]->pCertContext,
                CERT_STORE_ADD_REPLACE_EXISTING,
                NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertAddCertificateContextToStoreError;
        }
    }

    pblobPFX->pbData = NULL;
    while (TRUE)
    {
        if (!PFXExportCertStore(
                hMemStore,
                pblobPFX,
                pwszPassword,
                EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto PFXExportCertStoreError;
        }
        if (NULL != pblobPFX->pbData)
        {

            break;
        }
        pblobPFX->pbData = (BYTE*)MyCoTaskMemAlloc(pblobPFX->cbData);
        if (NULL == pblobPFX->pbData)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    if (pCertChainContext != NULL)
    {
        MyCertFreeCertificateChain(pCertChainContext);
    }
    if (NULL != hMemStore)
    {
        CertCloseStore(hMemStore, CERT_CLOSE_STORE_CHECK_FLAG);
    }
    return hr;

TRACE_ERROR(UnexpectedError)
TRACE_ERROR(CertOpenStoreError)
TRACE_ERROR(PFXExportCertStoreError)
TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(CertAddCertificateContextToStoreError)
TRACE_ERROR(NoCertificateChainError)
TRACE_ERROR(MyCertGetCertificateChainError)
SET_HRESULT(EPointerError, E_POINTER)
}

HRESULT
CCEnroll::createPFXWStrBStr( 
    IN  LPCWSTR         pwszPassword,
    OUT BSTR __RPC_FAR *pbstrPFX)
{
    HRESULT           hr;
    CRYPT_DATA_BLOB   blobPFX;

    memset(&blobPFX, 0, sizeof(CRYPT_DATA_BLOB));

    hr = createPFXWStr(pwszPassword, &blobPFX);
    if (S_OK != hr)
    {
        goto createPFXWStrError;
    }

    hr = BlobToBstring(&blobPFX, CRYPT_STRING_BASE64, pbstrPFX);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != blobPFX.pbData)
    {
        MyCoTaskMemFree(blobPFX.pbData);
    }
    return(hr);

TRACE_ERROR(createPFXWStrError)
TRACE_ERROR(BlobToBstringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFilePFXWStr(
    IN   LPCWSTR     pwszPassword,
    IN   LPCWSTR     pwszPFXFileName)
{
    HRESULT hr;
    HANDLE  hFile = NULL;
    DWORD   cb = 0;
    CRYPT_DATA_BLOB   blobPFX;

    memset(&blobPFX, 0, sizeof(CRYPT_DATA_BLOB));

    hr = createPFXWStr(pwszPassword, &blobPFX);
    if (S_OK != hr)
    {
        goto createPFXWStrError;
    }

    hFile = CreateFileSafely(pwszPFXFileName);
    if (NULL == hFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CreateFileFileSafelyError;
    }

    if(!WriteFile(
        hFile,
        blobPFX.pbData,
        blobPFX.cbData,
        &cb,
        NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto WriteFileError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != hFile)
    {
        CloseHandle(hFile);
    }
    if(NULL != blobPFX.pbData)
    {
        MyCoTaskMemFree(blobPFX.pbData);
    }
    return(hr);

TRACE_ERROR(createPFXWStrError)
TRACE_ERROR(CreateFileFileSafelyError)
TRACE_ERROR(WriteFileError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::setPendingRequestInfoWStr(
    IN   LONG     lRequestID,
    IN   LPCWSTR  pwszCADNS,
    IN   LPCWSTR  pwszCAName,
    IN   LPCWSTR  pwszFriendlyName
    )
{

    CEnrollLocalScope(SetPendingRequestInfoHelper): 

        HRESULT GetPendingRequestCertContext(IN  HCERTSTORE       hStoreRequest,
                                             IN  CRYPT_DATA_BLOB  hashBlob, 
                                             IN  PCCERT_CONTEXT   pCertContextCachedPendingRequest,
                                             OUT PCCERT_CONTEXT  *pCertContextPendingRequest)
        {
            EquivalentHashCertContextFilter filter(hashBlob); 

            if (hashBlob.pbData == NULL)
            {

                *pCertContextPendingRequest = CertDuplicateCertificateContext(pCertContextCachedPendingRequest); 
                return S_OK; 
            }
            else
            {

                return FilteredCertEnumCertificatesInStore
                    (hStoreRequest, NULL, &filter, pCertContextPendingRequest); 
            }
        }

        DWORD   GetPendingInfoBlobSize(IN  LONG              lRequestID,
                                       IN  LPCWSTR           pwszCADNS,
                                       IN  LPCWSTR           pwszCAName, 
                                       IN  LPCWSTR           pwszFriendlyName)
        {
            assert(pwszCADNS != NULL && pwszCAName != NULL && pwszFriendlyName != NULL); 

            return  (DWORD)(sizeof(lRequestID) +                            
                     sizeof(DWORD) +                                 
                     sizeof(WCHAR) * (wcslen(pwszCADNS) + 1) +       
                     sizeof(DWORD) +                                 
                     sizeof(WCHAR) * (wcslen(pwszCAName) + 1) +      
                     sizeof(DWORD) +                                 
                     sizeof(WCHAR) * (wcslen(pwszFriendlyName) + 1)  
                     ); 
        }

        void MakePendingInfoBlob(IN  LONG              lRequestID,
                                 IN  LPCWSTR           pwszCADNS,
                                 IN  LPCWSTR           pwszCAName, 
                                 IN  LPCWSTR           pwszFriendlyName, 
                                 OUT CRYPT_DATA_BLOB   pendingInfoBlob)
        {
            LPBYTE  pbBlob; 

            assert(pwszCADNS != NULL && pwszCAName != NULL && pwszFriendlyName != NULL); 

            struct StringsToWrite { 
                DWORD    cc; 
                LPCWSTR  pwsz;
            } rgStrings[] = { 
                { (DWORD)wcslen(pwszCADNS)        + 1, pwszCADNS         }, 
                { (DWORD)wcslen(pwszCAName)       + 1, pwszCAName        }, 
                { (DWORD)wcslen(pwszFriendlyName) + 1, pwszFriendlyName  }
            }; 

            pbBlob = pendingInfoBlob.pbData; 
            memcpy(pbBlob, &lRequestID, sizeof(lRequestID)); 
            pbBlob += sizeof(lRequestID); 

            for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(rgStrings); dwIndex++) 
            { 
                memcpy(pbBlob, &rgStrings[dwIndex].cc, sizeof(rgStrings[dwIndex].cc)); 
                pbBlob += sizeof(rgStrings[dwIndex].cc);
                memcpy(pbBlob, rgStrings[dwIndex].pwsz, rgStrings[dwIndex].cc * sizeof(WCHAR)); 
                pbBlob += rgStrings[dwIndex].cc * sizeof(WCHAR); 
            }

            assert(pbBlob == (pendingInfoBlob.pbData + pendingInfoBlob.cbData)); 
        }
    CEnrollEndLocalScope; 

    CRYPT_DATA_BLOB  pendingInfoBlob;
    HCERTSTORE       hStoreRequest; 
    HRESULT          hr                         = S_OK; 
    PCCERT_CONTEXT   pCertContextPendingRequest = NULL; 

    ZeroMemory(&pendingInfoBlob, sizeof(pendingInfoBlob)); 

    EnterCriticalSection(&m_csXEnroll); 

    if (lRequestID < 0 || pwszCADNS == NULL || pwszCAName == NULL)
        goto InvalidArgErr; 

    if (pwszFriendlyName == NULL) { pwszFriendlyName = L""; }

    if (NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
        goto GetStoreErr; 

    if (S_OK != (hr = local.GetPendingRequestCertContext
                 (hStoreRequest,
                  m_hashBlobPendingRequest, 
                  m_pCertContextPendingRequest,
                  &pCertContextPendingRequest)))
        goto GetPendingRequestCertContextErr; 

    pendingInfoBlob.cbData  = local.GetPendingInfoBlobSize
        (lRequestID,
         pwszCADNS,
         pwszCAName,
         pwszFriendlyName); 
    pendingInfoBlob.pbData  = (LPBYTE)LocalAlloc(LPTR, pendingInfoBlob.cbData); 
    if (NULL == pendingInfoBlob.pbData)
        goto MemoryErr; 

    local.MakePendingInfoBlob
        (lRequestID, 
         pwszCADNS, 
         pwszCAName,
         pwszFriendlyName,
         pendingInfoBlob);

    if (!CertSetCertificateContextProperty
        (pCertContextPendingRequest, 
         CERT_ENROLLMENT_PROP_ID, 
         0,
         &pendingInfoBlob))
    {

        goto CertSetCertificateContextPropertyErr; 
    }

    hr = S_OK;   

 CommonReturn: 
    if (NULL != pendingInfoBlob.pbData)      { LocalFree(pendingInfoBlob.pbData); }
    if (NULL != pCertContextPendingRequest)  { CertFreeCertificateContext(pCertContextPendingRequest); } 

    LeaveCriticalSection(&m_csXEnroll); 
    return hr; 

 ErrorReturn:
    goto CommonReturn; 

SET_HRESULT(CertSetCertificateContextPropertyErr,  MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(GetPendingRequestCertContextErr,       hr); 
SET_HRESULT(GetStoreErr,                           MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(InvalidArgErr,                         E_INVALIDARG); 
SET_HRESULT(MemoryErr,                             E_OUTOFMEMORY); 
} 

HRESULT STDMETHODCALLTYPE
CCEnroll::enumPendingRequestWStr(
    IN  LONG   lIndex,
    IN  LONG   lDesiredProperty,
    OUT LPVOID ppProperty
    )
{

    CEnrollLocalScope(EnumPendingRequestHelper): 
        CRYPT_DATA_BLOB dataBlob;

        HRESULT GetContextPropertySimple(PCCERT_CONTEXT pCertContext, DWORD dwPropID)
        {
            BOOL fDone = FALSE;
            dataBlob.pbData = NULL; 
            dataBlob.cbData = 0x150; 

            do { 
                if (dataBlob.pbData != NULL) { LocalFree(dataBlob.pbData); } 

                dataBlob.pbData = (LPBYTE)LocalAlloc(LPTR, dataBlob.cbData);
                if (dataBlob.pbData == NULL) { return E_OUTOFMEMORY; } 

                if (!CertGetCertificateContextProperty
                    (pCertContext, 
                     dwPropID, 
                     (LPVOID)dataBlob.pbData, 
                     &(dataBlob.cbData)))
                {
                    if (GetLastError() != ERROR_MORE_DATA)
                        return MY_HRESULT_FROM_WIN32(GetLastError());
                }
                else 
                {
                    fDone = TRUE;
                }
            } while (!fDone); 

            return S_OK;
        }

        LPBYTE GetNextString(IN LPBYTE pbBlob, OUT DWORD *pcbSize, OUT LPBYTE pbString) { 
            DWORD dwSize; 

            memcpy(&dwSize, pbBlob, sizeof(DWORD)); 
            dwSize *= sizeof(WCHAR); 
            if (NULL != pcbSize) { 
                *pcbSize = dwSize; 
            }
            pbBlob += sizeof(DWORD); 

            if (NULL != pbString) { 
                memcpy(pbString, pbBlob, dwSize); 
            }
            pbBlob += dwSize;

            return pbBlob; 
        }

        HRESULT getRequestID(PCCERT_CONTEXT pCertContext, long *pplProperty) {
            HRESULT hr; 
            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
                *pplProperty = *((long *)dataBlob.pbData); 
            return hr;
        }

        HRESULT getCAName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            DWORD    dwSize; 
            HRESULT  hr; 
            LPBYTE   pb;

            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
            { 
                pb =  dataBlob.pbData + sizeof(DWORD);  
                pb =  GetNextString(pb, NULL, NULL);    
                GetNextString(pb, &dwSize, NULL);       

                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dwSize)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        GetNextString(pb, NULL, pDataBlobProperty->pbData); 
                    }   
                }

                pDataBlobProperty->cbData = dwSize; 
            }

            return hr; 
        }

        HRESULT getCADNSName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            DWORD    dwSize; 
            HRESULT  hr; 
            LPBYTE   pb;

            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
            { 
                pb = dataBlob.pbData + sizeof(DWORD); 
                GetNextString(pb, &dwSize, NULL);     

                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dwSize)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        GetNextString(pb, NULL, pDataBlobProperty->pbData); 
                    }   
                }

                pDataBlobProperty->cbData = dwSize; 
            }

            return hr; 
        }

        HRESULT getCAFriendlyName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            DWORD    dwSize; 
            HRESULT  hr; 
            LPBYTE   pb;

            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
            { 

                pb =  dataBlob.pbData + sizeof(DWORD);        
                pb =  GetNextString(pb, NULL, NULL);          
                pb =  GetNextString(pb, NULL, NULL);          

                GetNextString(pb, &dwSize, NULL);

                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dwSize)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        GetNextString(pb, NULL, pDataBlobProperty->pbData); 
                    }   
                }

                pDataBlobProperty->cbData = dwSize; 
            }

            return hr; 
        }

        HRESULT getHash(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            HRESULT hr;

            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_HASH_PROP_ID)))
            {

                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dataBlob.cbData)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        memcpy(pDataBlobProperty->pbData, dataBlob.pbData, dataBlob.cbData); 
                    }
                }

                pDataBlobProperty->cbData = dataBlob.cbData; 
            }

            return hr; 
        }

        HRESULT getDate(PCCERT_CONTEXT pCertContext, PFILETIME pftProperty) { 
            *pftProperty = pCertContext->pCertInfo->NotAfter; 
            return S_OK; 
        } 

        HRESULT getTemplateName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty)   { 
            CERT_NAME_VALUE   *pCertTemplateNameValue = NULL;
            DWORD             cbCertTemplateNameValue; 
            DWORD             cbRequired = 0; 
            HRESULT           hr                      = S_OK; 
            PCERT_EXTENSION   pCertTemplateExtension  = NULL; 

            if (NULL == (pCertTemplateExtension = CertFindExtension
                         (szOID_ENROLL_CERTTYPE_EXTENSION,
                          pCertContext->pCertInfo->cExtension,
                          pCertContext->pCertInfo->rgExtension)))
                return E_INVALIDARG; 

            if (!CryptDecodeObject
                (pCertContext->dwCertEncodingType,
                 X509_UNICODE_ANY_STRING,
                 pCertTemplateExtension->Value.pbData,
                 pCertTemplateExtension->Value.cbData,
                 0,
                 NULL, 
                 &cbCertTemplateNameValue) || (cbCertTemplateNameValue == 0))
                goto CryptDecodeObjectErr; 

            pCertTemplateNameValue = (CERT_NAME_VALUE *)LocalAlloc(LPTR, cbCertTemplateNameValue); 
            if (NULL == pCertTemplateNameValue)
                goto MemoryErr; 

            if (!CryptDecodeObject
                (pCertContext->dwCertEncodingType,
                 X509_UNICODE_ANY_STRING,
                 pCertTemplateExtension->Value.pbData,
                 pCertTemplateExtension->Value.cbData,
                 0,
                 (void *)(pCertTemplateNameValue), 
                 &cbCertTemplateNameValue))
                goto CryptDecodeObjectErr; 

            cbRequired = sizeof(WCHAR) * (DWORD)(wcslen((LPWSTR)(pCertTemplateNameValue->Value.pbData)) + 1);
            if (NULL != pDataBlobProperty->pbData)
            {

                if (pDataBlobProperty->cbData < cbRequired) { goto MoreDataErr; } 

                wcscpy((LPWSTR)pDataBlobProperty->pbData, (LPWSTR)(pCertTemplateNameValue->Value.pbData)); 
            }

            hr = S_OK;
        CommonReturn: 

            pDataBlobProperty->cbData = cbRequired; 

            if (NULL != pCertTemplateNameValue) { LocalFree(pCertTemplateNameValue); }
            return hr; 

        ErrorReturn: 
            goto CommonReturn; 

        SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
        SET_HRESULT(MoreDataErr,   MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA)); 
        SET_HRESULT(MemoryErr,     E_OUTOFMEMORY); 
        }

	HRESULT getTemplateOID(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty)    { 
            CERT_TEMPLATE_EXT  *pCertTemplateExt    = NULL; 
	    DWORD               cbCertTemplateExt   = 0; 
            DWORD               cbRequired = 0; 
	    HRESULT             hr;
            LPWSTR              pwszOID             = NULL; 
	    PCERT_EXTENSION     pCertExtension      = NULL; 

 	    if (NULL == (pCertExtension = CertFindExtension
			 (szOID_CERTIFICATE_TEMPLATE, 
			  pCertContext->pCertInfo->cExtension,
			  pCertContext->pCertInfo->rgExtension)))
		return E_INVALIDARG; 

            if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 NULL, 
		 &cbCertTemplateExt) || (cbCertTemplateExt == 0))
                goto CryptDecodeObjectErr;

	    pCertTemplateExt = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbCertTemplateExt); 
	    if (NULL == pCertTemplateExt)
		goto MemoryErr; 

	    if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 (void *)(pCertTemplateExt), 
		 &cbCertTemplateExt))
		goto CryptDecodeObjectErr;

            cbRequired = sizeof(WCHAR) * (DWORD)(strlen(pCertTemplateExt->pszObjId) + 1); 

	    if (NULL != pDataBlobProperty->pbData) 
            {

                if (pDataBlobProperty->cbData < cbRequired) { goto MoreDataErr; }

                pwszOID = WideFromMB(pCertTemplateExt->pszObjId); 
                if (NULL == pwszOID) 
                    goto WideFromMBErr; 

                wcscpy((LPWSTR)pDataBlobProperty->pbData, pwszOID); 
            }

            hr = S_OK; 
        CommonReturn:

            pDataBlobProperty->cbData = cbRequired; 

            if (NULL != pCertTemplateExt) { LocalFree(pCertTemplateExt); } 
            if (NULL != pwszOID)          { MyCoTaskMemFree(pwszOID); }

            return hr; 
        ErrorReturn:
            goto CommonReturn;

	SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
        SET_HRESULT(MemoryErr,            E_OUTOFMEMORY); 
        SET_HRESULT(MoreDataErr,          MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA)); 
        SET_HRESULT(WideFromMBErr,        MY_HRESULT_FROM_WIN32(GetLastError())); 
        } 

        HRESULT getVersion(PCCERT_CONTEXT pCertContext, long *plVersion) {
            CERT_TEMPLATE_EXT  *pCertTemplateExt    = NULL; 
	    DWORD               cbCertTemplateExt   = 0; 
	    HRESULT             hr;
	    PCERT_EXTENSION     pCertExtension      = NULL; 

 	    if (NULL == (pCertExtension = CertFindExtension
			 (szOID_CERTIFICATE_TEMPLATE, 
			  pCertContext->pCertInfo->cExtension,
			  pCertContext->pCertInfo->rgExtension)))
		return E_INVALIDARG; 

            if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 NULL, 
		 &cbCertTemplateExt) || (cbCertTemplateExt == 0))
		goto CryptDecodeObjectErr;

	    pCertTemplateExt = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbCertTemplateExt); 
	    if (NULL == pCertTemplateExt)
		goto MemoryErr; 

	    if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 (void *)(pCertTemplateExt), 
		 &cbCertTemplateExt))
		goto CryptDecodeObjectErr;

            *plVersion = (long)pCertTemplateExt->dwMajorVersion; 
            hr = S_OK; 
        CommonReturn:

            if (NULL != pCertTemplateExt) { LocalFree(pCertTemplateExt); } 

            return hr; 
        ErrorReturn:
            goto CommonReturn;

	SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
        SET_HRESULT(MemoryErr,            E_OUTOFMEMORY); 
        }

        void InitLocalScope() { 
            dataBlob.cbData = 0;
            dataBlob.pbData = NULL;
        }

        void FreeLocalScope() { if (dataBlob.pbData != NULL) { LocalFree(dataBlob.pbData); } }

    CEnrollEndLocalScope;

    HCERTSTORE                hStoreRequest     = NULL;
    HRESULT                   hr                = S_OK; 
    PCCERT_CONTEXT            pCertContext      = NULL;

    if (lIndex != XEPR_ENUM_FIRST && (lIndex < 0 || (ppProperty == NULL)))
        return E_INVALIDARG; 

    if (0 != m_dwEnabledSafteyOptions) 
	return E_ACCESSDENIED; 

    local.InitLocalScope(); 

    EnterCriticalSection(&m_csXEnroll);

    if ( NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
        goto ErrorCertOpenRequestStore; 

    if (lIndex == XEPR_ENUM_FIRST)
    {
        if (NULL != this->m_pPendingRequestTable) { delete this->m_pPendingRequestTable; } 

        this->m_pPendingRequestTable = new PendingRequestTable; 
        if (NULL == this->m_pPendingRequestTable)
            goto MemoryErr; 

        if (S_OK != (hr = this->m_pPendingRequestTable->construct(hStoreRequest)))
            goto ErrorConstructPendingTable; 

        goto CommonReturn; 
    }

    if (NULL == m_pPendingRequestTable)
        goto PointerErr; 

    if (this->m_pPendingRequestTable->size() <= (DWORD)lIndex)
    { 
        hr = MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND);
        goto ErrorReturn; 
    }

    pCertContext = (*this->m_pPendingRequestTable)[(DWORD)lIndex]; 

    switch (lDesiredProperty)
        {
        case XEPR_REQUESTID:       hr = local.getRequestID      (pCertContext, (long *)ppProperty);             break; 
        case XEPR_CANAME:          hr = local.getCAName         (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_CAFRIENDLYNAME:  hr = local.getCAFriendlyName (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_CADNS:           hr = local.getCADNSName      (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_DATE:            hr = local.getDate           (pCertContext, (PFILETIME)ppProperty);          break; 
        case XEPR_V1TEMPLATENAME:  hr = local.getTemplateName   (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_V2TEMPLATEOID:   hr = local.getTemplateOID    (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_VERSION:         hr = local.getVersion        (pCertContext, (long *)ppProperty);             break; 
        case XEPR_HASH:            hr = local.getHash           (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        default: 
            hr = E_INVALIDARG; 
        }

 CommonReturn: 
    local.FreeLocalScope(); 

    LeaveCriticalSection(&m_csXEnroll); 
    return hr; 

 ErrorReturn:
    goto CommonReturn; 

SET_HRESULT(MemoryErr, E_OUTOFMEMORY); 
SET_HRESULT(PointerErr, E_POINTER);
TRACE_ERROR(ErrorCertOpenRequestStore); 
TRACE_ERROR(ErrorConstructPendingTable);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::removePendingRequestWStr
  (IN CRYPT_DATA_BLOB thumbPrintBlob
   )
{
    EquivalentHashCertContextFilter equivHashFilter(thumbPrintBlob); 
    PendingCertContextFilter        pendingCertFilter; 

    CompositeCertContextFilter      combinedFilter(&equivHashFilter, &pendingCertFilter); 

    HCERTSTORE                      hStoreRequest = NULL;
    HRESULT                         hr;
    PCCERT_CONTEXT                  pCertContext  = NULL;

    EnterCriticalSection(&m_csXEnroll); 

    if (NULL == thumbPrintBlob.pbData)
    {
        hr = E_INVALIDARG; 
        goto ErrorReturn; 
    }

    if ( NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
    {
        hr = E_UNEXPECTED; 
        goto ErrorReturn;
    }

    if (S_OK != (hr = FilteredCertEnumCertificatesInStore
                 (hStoreRequest, 
                  NULL, 
                  &combinedFilter, 
                  &pCertContext))) 
        goto ErrorReturn; 

    if (!CertDeleteCertificateFromStore(pCertContext))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError()); 

        pCertContext = NULL; 
        goto ErrorReturn;
    }

    pCertContext = NULL; 
    hr = S_OK; 

 CommonReturn:
    LeaveCriticalSection(&m_csXEnroll); 

    return hr; 

 ErrorReturn:
    if (pCertContext != NULL) { CertFreeCertificateContext(pCertContext); } 
    goto CommonReturn; 
}

HRESULT FilteredCertEnumCertificatesInStore(IN  HCERTSTORE          hStore, 
                                            IN  PCCERT_CONTEXT      pCertContext, 
                                            IN  CertContextFilter  *pFilter, 
                                            OUT PCCERT_CONTEXT     *pCertContextNext)
{
    BOOL           fFilterResult; 
    HRESULT        hr = S_OK; 
    PCCERT_CONTEXT pCertContextPrev = pCertContext; 

    while (NULL != (pCertContext = CertEnumCertificatesInStore(hStore, pCertContextPrev)))
    {
        if (S_OK != (hr = pFilter->accept(pCertContext, &fFilterResult)))
            return hr;

        if (fFilterResult) 
        {
            *pCertContextNext = pCertContext; 
            return S_OK; 
        }
        pCertContextPrev = pCertContext; 
    }

    return MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND); 
}

static LPVOID MyLocalAlloc(ULONG cb) {
    return((LPVOID) LocalAlloc(LPTR, (UINT) cb));
}

static LPVOID MyLocalRealloc(LPVOID ptr, ULONG cb) {
    return((LPVOID) LocalReAlloc((HLOCAL) ptr, (UINT) cb, LMEM_MOVEABLE));
}

static void MyLocalFree(LPVOID ptr) {
    LocalFree((HLOCAL) ptr);
}

void * WINAPI PGetIEnrollNoCOM(const IID &iid) {

        void *      pvoid               = NULL;
        IClassFactory * pIClassFactory  = NULL;
        HRESULT         hr                                  = S_OK;

        MyCoTaskMemAlloc        = MyLocalAlloc;
        MyCoTaskMemFree         = MyLocalFree;
        MyCoTaskMemRealloc      = MyLocalRealloc;

        if( S_OK != (hr = DllGetClassObject(CLSID_CEnroll2, IID_IClassFactory,  (void **) &pIClassFactory)) ) {
                pIClassFactory = NULL;
        }
        else if( S_OK != (hr = pIClassFactory->CreateInstance(NULL, iid, &pvoid)) ) {
        pvoid = NULL;
    }

        if(pIClassFactory != NULL) {
            pIClassFactory->Release();
            pIClassFactory = NULL;
        }

    SetLastError(hr);
        return(pvoid);
}

IEnroll * WINAPI PIEnrollGetNoCOM(void) 
{
    return( (IEnroll *) PGetIEnrollNoCOM(IID_IEnroll) );
}

IEnroll2 * WINAPI PIEnroll2GetNoCOM(void) 
{
    return( (IEnroll2 *) PGetIEnrollNoCOM(IID_IEnroll2) );
}

IEnroll4 * WINAPI PIEnroll4GetNoCOM(void) 
{
    return( (IEnroll4 *) PGetIEnrollNoCOM(IID_IEnroll4) );
}

HRESULT PendingRequestTable::resize(DWORD dwNewSize)
{
    TableElem * newTable = NULL;

    if (dwNewSize <= 0)
        return E_INVALIDARG; 

    newTable = (TableElem *)LocalAlloc(LPTR, sizeof(TableElem) * dwNewSize); 
    if (NULL == newTable)
        return E_OUTOFMEMORY; 

    if (NULL != this->table)
    {
        memcpy(newTable, this->table, this->dwElemSize * sizeof(TableElem)); 
        LocalFree(this->table);
    }

    this->dwElemSize = dwNewSize; 
    this->table      = newTable; 

    return S_OK; 
}

HRESULT PendingRequestTable::add(TableElem tePendingRequest)
{
    HRESULT hr; 

    if        (this->dwElemCount >  this->dwElemSize) { return E_UNEXPECTED; } 
    else if   (this->dwElemCount == this->dwElemSize) 
    { 

        DWORD dwNewSize = this->dwElemSize < 100 ? 100 : this->dwElemSize * 2; 

        if (S_OK != (hr = this->resize(dwNewSize)))
            return hr; 
    }

    this->table[this->dwElemCount++] = tePendingRequest; 
    return S_OK; 
}

PendingRequestTable::PendingRequestTable() : table(NULL), dwElemSize(0), dwElemCount(0)
{ }

PendingRequestTable::~PendingRequestTable()
{
    if (NULL != this->table)
    {
        for (DWORD dwIndex = 0; dwIndex < dwElemCount; dwIndex++)
        {
            CertFreeCertificateContext(this->table[dwIndex].pCertContext); 
        }

        LocalFree(this->table);
    }
}

HRESULT PendingRequestTable::construct(HCERTSTORE hStore)
{
    HRESULT                  hr                 = S_OK; 
    PendingCertContextFilter pendingFilter; 
    PCCERT_CONTEXT           pCertContext       = NULL;
    PCCERT_CONTEXT           pCertContextPrev   = NULL;
    TableElem                tePendingRequest; 

    for (DWORD dwIndex = 0; TRUE; dwIndex++)
    {
        if (S_OK != (hr = FilteredCertEnumCertificatesInStore
                     (hStore, 
                      pCertContextPrev, 
                      &pendingFilter, 
                      &pCertContext)))
            break; 

        tePendingRequest.pCertContext = CertDuplicateCertificateContext(pCertContext); 
        this->add(tePendingRequest); 

        pCertContextPrev = pCertContext; 
    }

    return hr == MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND) ? S_OK : hr; 
}
//depot/Lab03_DEV/Ds/security/cryptoapi/pki/activex/xenroll/cenroll.cpp#4 - edit change 19979 (text)
//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       cenroll.cpp
//
//--------------------------------------------------------------------------

// CEnroll.cpp : Implementation of CCEnroll


#include "stdafx.h"

#include <windows.h>
#include <wincrypt.h>
#include <unicode.h>
#define SECURITY_WIN32
#include <security.h>
#include <aclapi.h>
#include <pvk.h>
#include <wintrust.h>
#include <xasn.h>
#include <autoenr.h>
#include <sddl.h>

#include "xenroll.h"
#include "cenroll.h"
#include "xelib.h"
#include "sfscript.h"

#define NO_OSS_DEBUG
#include <dbgdef.h>

#include <string.h>

#include <assert.h>

static LPVOID (* MyCoTaskMemAlloc)(ULONG) = NULL;
static LPVOID (* MyCoTaskMemRealloc)(LPVOID, ULONG) = NULL;
static void (* MyCoTaskMemFree)(LPVOID) = NULL;

#define MY_HRESULT_FROM_WIN32(a) ((a >= 0x80000000) ? a : HRESULT_FROM_WIN32(a))

#ifndef NTE_TOKEN_KEYSET_STORAGE_FULL
#define NTE_TOKEN_KEYSET_STORAGE_FULL _HRESULT_TYPEDEF_(0x80090023L)
#endif

#define CEnrollLocalScope(ScopeName) struct ScopeName##TheLocalScope { public
#define CEnrollEndLocalScope } local

#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))

#pragma warning(disable:4213) // nonstandard extension used : cast on l-value


static LPSTR MBFromWide(LPCWSTR wsz) {

    LPSTR   sz = NULL;
    DWORD   cb = 0;

    assert(wsz != NULL);
    if(wsz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    if( (cb = WideCharToMultiByte(0, 0, wsz, -1, NULL, 0, NULL, NULL)) == 0   ||
        (sz = (char *) MyCoTaskMemAlloc(cb)) == NULL  ||
        (cb = WideCharToMultiByte(0, 0, wsz, -1, sz, cb, NULL, NULL)) == 0 ) {

        if(GetLastError() == ERROR_SUCCESS)
            SetLastError(ERROR_OUTOFMEMORY);

        return(NULL);
    }

    return(sz);
}

static LPWSTR WideFromMB(LPCSTR sz) {

    DWORD   cch     = 0;
    LPWSTR  wsz     = NULL;

    assert(sz != NULL);
    if(sz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    if( (cch = MultiByteToWideChar(0, 0, sz, -1, NULL, 0)) == 0   ||
        (wsz = (WCHAR *) MyCoTaskMemAlloc(cch * sizeof(WCHAR))) == NULL  ||
        (cch = MultiByteToWideChar(0, 0, sz, -1, wsz, cch)) == 0) {

        if(GetLastError() == ERROR_SUCCESS)
            SetLastError(ERROR_OUTOFMEMORY);

        return(NULL);
    }

    return(wsz);
}

static BSTR
BSTRFromMB(LPCSTR sz)
{
    BSTR    bstr    = NULL;
    DWORD   cch     = 0;
    WCHAR  *pwsz     = NULL;
    BOOL    fFail = FALSE;

    assert(sz != NULL);
    if(sz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }
 
    while (TRUE)
    {
        if(0 == (cch = MultiByteToWideChar(0, 0, sz, -1, pwsz, cch)))
        {
            //error
            fFail = TRUE;
            break;
        }
        if (NULL != pwsz)
        {
            //done
            break;
        }
        pwsz = (WCHAR *)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
        if (NULL == pwsz)
        {
            //error
            if(GetLastError() == ERROR_SUCCESS)
                SetLastError(ERROR_OUTOFMEMORY);
            break;
        }
    }

    if (!fFail && NULL != pwsz)
    {
        bstr = SysAllocString(pwsz);
        if (NULL == bstr)
        {
            if(GetLastError() == ERROR_SUCCESS)
                SetLastError(ERROR_OUTOFMEMORY);
        }
    }

    if (NULL != pwsz)
    {
        LocalFree(pwsz);
    }
    return(bstr);
}

static LPWSTR CopyWideString(LPCWSTR wsz) {

    size_t  cch     = 0;
    LPWSTR  wszOut  = NULL;

    // shouldn't send in a NULL
    assert(wsz != NULL);
    if(wsz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    cch = wcslen(wsz) + 1;
    if (cch*sizeof(WCHAR) > (ULONG)-1) { 
	// prevent errors caused by conversion from size_t --> ULONG
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }
	

    if( (wszOut = (LPWSTR) MyCoTaskMemAlloc((ULONG)(sizeof(WCHAR) * cch))) == NULL ) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(NULL);
    }

    wcscpy(wszOut, wsz);

    return(wszOut);
}

static LPSTR CopyAsciiString(LPCSTR sz) {

    size_t  cch     = 0;
    LPSTR   szOut   = NULL;

    // shouldn't send in a NULL
    assert(sz != NULL);
    if(sz == NULL) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    cch = strlen(sz) + 1;
    if (cch > (ULONG)-1) { 
	// prevent errors caused by conversion from size_t --> ULONG
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }
	

    if( (szOut = (LPSTR) MyCoTaskMemAlloc((ULONG)cch)) == NULL ) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(NULL);
    }

    strcpy(szOut, sz);

    return(szOut);
}

static DWORD KeyLocationFromStoreLocation(DWORD dwStoreFlags) {

    if(
        ((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_CURRENT_USER) ||
        ((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_USERS) ||
        ((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)
      ) {
        return(0);
    }

    // CERT_SYSTEM_STORE_LOCAL_MACHINE
    // CERT_SYSTEM_STORE_DOMAIN_POLICY
    // CERT_SYSTEM_STORE_CURRENT_SERVICE
    // CERT_SYSTEM_STORE_SERVICES
    // CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY

    return(CRYPT_MACHINE_KEYSET);
}

//modified from myLoadRCString from ca
HRESULT
xeLoadRCString(
    IN HINSTANCE   hInstance,
    IN int         iRCId,
    OUT WCHAR    **ppwsz)
{
#define REALLOCATEBLOCK 512
    HRESULT   hr;
    WCHAR    *pwszTemp = NULL;
    int       sizeTemp;
    int       size = 0;
    int       cBlocks = 1;

    *ppwsz = NULL;

    while (NULL == pwszTemp)
    {
        sizeTemp = cBlocks * REALLOCATEBLOCK;
        pwszTemp = (WCHAR*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
                        sizeTemp * sizeof(WCHAR));
        if (NULL == pwszTemp)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }

        size = LoadStringU(
                   hInstance,
                   iRCId,
                   pwszTemp,
                   sizeTemp);
        if (0 == size)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto LoadStringError;
        }

        if (size < sizeTemp - 1)
        {
            // ok, size is big enough
            break;
        }
        ++cBlocks;
        LocalFree(pwszTemp);
        pwszTemp = NULL;
    }

    *ppwsz = (WCHAR*) LocalAlloc(LPTR, (size+1) * sizeof(WCHAR));
    if (NULL == *ppwsz)
    {
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    }
    // copy it
    wcscpy(*ppwsz, pwszTemp);

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszTemp)
    {
        LocalFree(pwszTemp);
    }
    return hr;

TRACE_ERROR(LocalAllocError)
TRACE_ERROR(LoadStringError)
}

HANDLE CCEnroll::CreateOpenFileSafely2(
    LPCWSTR pwszFileName,
    DWORD   idsCreate,
    DWORD   idsOverwrite)
{
    HANDLE      hFile = NULL;
    WCHAR      *pwszMsg = NULL;
    WCHAR      *pwszFormat = NULL;
    WCHAR      *pwszTitle = NULL;
    WCHAR      *pwszSafety = NULL;
    DWORD       dwAttribs = 0;
    BOOL        fNotProperFile;
    
    LPCWSTR     apwszInsertArray[2];
    BOOL        fNo;
    BOOL        fMsgBox;
    int         idPrefix = IDS_NOTSAFE_WRITE_PREFIX; //default to write prefix
    BOOL        fCreate = (0xFFFFFFFF != idsCreate) &&
                          (0xFFFFFFFF != idsOverwrite);
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);

    fMsgBox = (m_dwEnabledSafteyOptions != 0);
    dwAttribs = GetFileAttributesU(pwszFileName);

    if(0xFFFFFFFF == dwAttribs)
    {
        //file doesn't exist
        if (!fCreate)
        {
            //try to read a non-existing file
            //for safety reasons, don't return system error
            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
        //if got here, write a new file
        if (fMsgBox)
        {
            hr = xeLoadRCString(hInstanceXEnroll, idsCreate, &pwszFormat);
            if (S_OK != hr)
            {
                goto xeLoadRCStringError;
            }
        }
    }
    else
    {
        //file exists, check if a proper file to write or read
        //in either write or read, the following file attrib not proper
        fNotProperFile = 
              (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
              (dwAttribs & FILE_ATTRIBUTE_HIDDEN)    ||
              (dwAttribs & FILE_ATTRIBUTE_SYSTEM);

        if (!fNotProperFile)
        {
            //so far so good
            if (fCreate)
            {
                //write a file
                if (0x0 != (dwAttribs & FILE_ATTRIBUTE_READONLY))
                {
                    //don't take read-only and archive
                    fNotProperFile = TRUE;
                }
                else
                {
                    //try to overwrite existing file
                    hr = xeLoadRCString(hInstanceXEnroll, idsOverwrite, &pwszFormat);
                    if (S_OK != hr)
                    {
                        goto xeLoadRCStringError;
                    }
                    //enforce popup if overwrite
                    fMsgBox = TRUE;
                }
            }
            else
            {
		//read an existing file always violate scripting safety
		//it allows detecting file existence
		//put out a warning
		fMsgBox = TRUE;
		hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_OPEN, &pwszFormat);
		if (S_OK != hr)
                {
		    goto xeLoadRCStringError;
		}
		idPrefix = IDS_NOTSAFE_OPEN_PREFIX;
            }
        }

        if (fNotProperFile)
        {
            //for safety reasons, don't return system error
            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
    }

    if (fMsgBox)
    {
        hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }
        hr = xeLoadRCString(hInstanceXEnroll, idPrefix, &pwszSafety);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }

        apwszInsertArray[0] = pwszSafety;
        apwszInsertArray[1] = pwszFileName;

        if (!FormatMessageU(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_STRING |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pwszFormat,
            0,
            0,
            (LPWSTR) &pwszMsg,
            0,
            (va_list *)apwszInsertArray)) 
	{ 
	    goto FormatMessageError; 
	}

        fNo = (MessageBoxU(
                    NULL,
                    pwszMsg,
                    pwszTitle,
                    MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES);
                
        if(fNo)
        {
            SetLastError(ERROR_CANCELLED);
            goto CancelError;
        }
    }
    
    hFile = CreateFileU(
            pwszFileName,
            fCreate ? GENERIC_WRITE : GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

    if (hFile == INVALID_HANDLE_VALUE  ||  hFile == NULL)
    {
        //don't return system error so keep xenroll relative safe for scripting
        SetLastError(ERROR_ACCESS_DENIED);
        hFile = NULL;
        goto CreateFileUError;
    }

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);

    if(NULL != pwszMsg)
    {
        LocalFree(pwszMsg);
    }
    if(NULL != pwszFormat)
    {
        LocalFree(pwszFormat);
    }
    if(NULL != pwszTitle)
    {
        LocalFree(pwszTitle);
    }
    if(NULL != pwszSafety)
    {
        LocalFree(pwszSafety);
    }
    return(hFile);

TRACE_ERROR(CreateFileUError)
TRACE_ERROR(FormatMessageError);
TRACE_ERROR(CancelError)
TRACE_ERROR(InvalidFileError)
TRACE_ERROR(xeLoadRCStringError)
}

HANDLE CCEnroll::CreateOpenFileSafely(
    LPCWSTR pwszFileName,
    BOOL    fCreate)
{
    HANDLE      hFile = NULL;
    WCHAR      *pwszMsg = NULL;
    DWORD       dwAttribs = 0;
    BOOL        fNotProperFile;
    
    WCHAR      *pwszFormat = NULL;
    WCHAR      *pwszTitle = NULL;
    LPCWSTR     apwszInsertArray[] = {pwszFileName};
    BOOL        fNo;
    BOOL        fMsgBox = 0 != m_dwEnabledSafteyOptions;
    BOOL        fOverWrite = FALSE;
    HRESULT     hr;

    EnterCriticalSection(&m_csXEnroll);

    dwAttribs = GetFileAttributesU(pwszFileName);
    if(0xFFFFFFFF == dwAttribs)
    {
        //file doesn't exist
        if (!fCreate)
        {
            //try to read a non-existing file
            //for safety reasons, don't return system error
            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
    }
    else
    {
        //file exists, check if a proper file to write or read
        //in either write or read, the following file attrib not proper
        fNotProperFile = 
              (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
              (dwAttribs & FILE_ATTRIBUTE_HIDDEN)    ||
              (dwAttribs & FILE_ATTRIBUTE_SYSTEM);

        if (!fNotProperFile)
        {
            //so far so good
            if (fCreate)
            {
                //write a file
                if (0x0 != (dwAttribs & FILE_ATTRIBUTE_READONLY))
                {
                    //don't take read-only and archive
                    fNotProperFile = TRUE;
                }
                else
                {
                    //try to overwrite existing file
                    fOverWrite = TRUE;
                }
            }
        }

        if (fNotProperFile)
        {
            //for safety reasons, don't return system error
            SetLastError(ERROR_ACCESS_DENIED);
            goto InvalidFileError;
        }
    }

    if (fMsgBox)
    {
        hr = xeLoadRCString(hInstanceXEnroll, IDS_CERTENROLL, &pwszTitle);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }
        hr = xeLoadRCString(
                hInstanceXEnroll,
                fCreate ? IDS_NOTSAFE_WRITE_FORMAT : IDS_NOTSAFE_OPEN_FORMAT,
                &pwszFormat);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }

        if (!FormatMessageU(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_STRING |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pwszFormat,
            0,
            0,
            (LPWSTR) &pwszMsg,
            0,
            (va_list *)apwszInsertArray))
	{
	    goto FormatMessageUError;
	}

        fNo = (MessageBoxU(
                    NULL,
                    pwszMsg,
                    pwszTitle,
                    MB_DEFBUTTON2 | MB_YESNO | MB_ICONWARNING) == IDNO);
        if(fNo)
        {
            SetLastError(ERROR_CANCELLED);
            goto CancelError;
        }
    }

    if (fCreate && fOverWrite)
    {
        if (!fMsgBox)
        {
            hr = xeLoadRCString(hInstanceXEnroll, IDS_CERTENROLL, &pwszTitle);
            if (S_OK != hr)
            {
                goto xeLoadRCStringError;
            }
        }

        //popup overwrite confirmation
        hr = xeLoadRCString(hInstanceXEnroll, IDS_OVERWRITE_FORMAT, &pwszFormat);
        if (S_OK != hr)
        {
            goto xeLoadRCStringError;
        }

        //make sure free before alloc again
        if (NULL != pwszMsg)
        {
            LocalFree(pwszMsg);
            pwszMsg = NULL;
        }
        FormatMessageU(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_STRING |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pwszFormat,
            0,
            0,
            (LPWSTR) &pwszMsg,
            0,
            (va_list *)apwszInsertArray);

        fNo = (MessageBoxU(
                    NULL,
                    pwszMsg,
                    pwszTitle,
                    MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES);
        if(fNo)
        {
            SetLastError(ERROR_CANCELLED);
            goto CancelError;
        }
    }
    
    hFile = CreateFileU(
            pwszFileName,
            fCreate ? GENERIC_WRITE : GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

    if (hFile == INVALID_HANDLE_VALUE  ||  hFile == NULL)
    {
        //don't return system error so keep xenroll relative safe for scripting
        SetLastError(ERROR_ACCESS_DENIED);
        hFile = NULL;
        goto CreateFileUError;
    }

ErrorReturn:
    if(NULL != pwszMsg)
    {
        LocalFree(pwszMsg);
    }
    if(NULL != pwszTitle)
    {
        LocalFree(pwszTitle);
    }
    if(NULL != pwszFormat)
    {
        LocalFree(pwszFormat);
    }
    LeaveCriticalSection(&m_csXEnroll);

    return(hFile);

TRACE_ERROR(CreateFileUError)
TRACE_ERROR(CancelError)
TRACE_ERROR(FormatMessageUError);
TRACE_ERROR(InvalidFileError)
TRACE_ERROR(xeLoadRCStringError)
}

HANDLE CCEnroll::CreateFileSafely(
    LPCWSTR pwszFileName)
{
    return CreateOpenFileSafely(pwszFileName, TRUE); //write
}

HANDLE CCEnroll::OpenFileSafely(
    LPCWSTR pwszFileName)
{
    return CreateOpenFileSafely(pwszFileName, FALSE); //open
}

void DwordToWide(DWORD dw, LPWSTR lpwstr) {

    DWORD   i = 0;
    DWORD   j;
    WCHAR   wch;

    while(dw > 0) {
        j = dw % 10;
        dw /= 10;
        lpwstr[i++] = (WCHAR) (j + L'\0');
    }

    if( i == 0 )
        lpwstr[i++] = L'\0';

    lpwstr[i] = 0;

    for(j=0, i--; i > j; i--, j++) {
        wch = lpwstr[i];
        lpwstr[i] = lpwstr[j];
        lpwstr[j] = wch;
    }
}

//take a name value pair info and return encoded value
HRESULT
xeEncodeNameValuePair(
    IN PCRYPT_ENROLLMENT_NAME_VALUE_PAIR pNameValuePair,
    OUT BYTE                           **ppbData,
    OUT DWORD                           *pcbData)
{
    HRESULT hr = S_OK;

    //init
    *ppbData = NULL;
    *pcbData = 0;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                szOID_ENROLLMENT_NAME_VALUE_PAIR,
                pNameValuePair,
                *ppbData,
                pcbData))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto error;
        }

        if (NULL != *ppbData)
        {
            break;
        }

        *ppbData = (BYTE*)MyCoTaskMemAlloc(*pcbData);
        if (NULL == *ppbData)
        {
            hr = E_OUTOFMEMORY;
            goto error;
        }
    }

error:
    if (S_OK != hr && NULL != *ppbData)
    {
        MyCoTaskMemFree(*ppbData);
	*ppbData = NULL; 
    }
    return hr;
}

//convert wsz to sz and allocate mem
HRESULT
xeWSZToSZ(
    IN LPCWSTR    pwsz,
    OUT LPSTR    *ppsz)
{
    HRESULT hr = S_OK;
    LONG    cc = 0;

    //init
    *ppsz = NULL;

    while (TRUE)
    {
        cc = WideCharToMultiByte(
                    GetACP(),
                    0,
                    pwsz,
                    -1,
                    *ppsz,
                    cc,
                    NULL,
                    NULL);
        if (0 >= cc)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto error;
        }

        if (NULL != *ppsz)
        {
            break;
        }
        *ppsz= (CHAR*)MyCoTaskMemAlloc(cc);
        if (NULL == *ppsz)
        {
            hr = E_OUTOFMEMORY;
            goto error;
        }
    }
error:
    if (S_OK != hr && NULL != *ppsz)
    {
        MyCoTaskMemFree(*ppsz);
	*ppsz = NULL; 
    }
    return hr;
}

//modified from DecodeFile on certsrv
HRESULT
CCEnroll::xeStringToBinaryFromFile(
    IN  WCHAR const *pwszfn,
    OUT BYTE       **ppbOut,
    OUT DWORD       *pcbOut,
    IN  DWORD        Flags)
{
    HANDLE hFile;
    HRESULT hr;
    CHAR *pchFile = NULL;
    BYTE *pbOut = NULL;
    DWORD cchFile;
    DWORD cbRead;
    DWORD cbOut = 0;

    hFile = OpenFileSafely(pwszfn);
    if (NULL == hFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto OpenFileSafelyError;
    }

    cchFile = GetFileSize(hFile, NULL);
    if ((DWORD) -1 == cchFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetFileSizeError;
    }

    pchFile = (CHAR *) LocalAlloc(LMEM_FIXED, cchFile);
    if (NULL == pchFile)
    {
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    }

    if (!ReadFile(hFile, pchFile, cchFile, &cbRead, NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto ReadFileError;
    }

    assert(cbRead <= cchFile);
    if (cbRead != cchFile)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        goto ReadFileError;
    }

    if (CRYPT_STRING_BINARY == Flags)
    {
        pbOut = (BYTE *) pchFile;
        cbOut = cchFile;
        pchFile = NULL;
    }
    else
    {
        // Decode file contents.
        while (TRUE)
        {
            if (!MyCryptStringToBinaryA(
                        pchFile,
                        cchFile,
                        Flags,
                        pbOut,
                        &cbOut,
                        NULL,
                        NULL))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto CyrptStringToBinaryError;
            }
            if (NULL != pbOut)
            {
                //done
                break;
            }
            pbOut = (BYTE*)LocalAlloc(LMEM_FIXED, cbOut);
            if (NULL == pbOut)
            {
                hr = E_OUTOFMEMORY;
                goto LocalAllocError;
            }
        }
    }
    *pcbOut = cbOut;
    *ppbOut = pbOut;
    pbOut = NULL;

    hr = S_OK;
error:
    if (INVALID_HANDLE_VALUE != hFile)
    {
        CloseHandle(hFile);
    }
    if (NULL != pchFile)
    {
        LocalFree(pchFile);
    }
    if (NULL != pbOut)
    {
        LocalFree(pbOut);
    }
    return(hr);

ErrorReturn:
    goto error;

TRACE_ERROR(CyrptStringToBinaryError)
TRACE_ERROR(ReadFileError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(GetFileSizeError)
TRACE_ERROR(OpenFileSafelyError)
}

//following two functions handle some APIs not available
//in downlevel client crypt32.dll
typedef VOID
(WINAPI * PFNCertFreeCertificateChain)
   (IN PCCERT_CHAIN_CONTEXT pChainContext);

typedef BOOL
(WINAPI * PFNCertGetCertificateChain)
   (IN OPTIONAL HCERTCHAINENGINE hChainEngine,
    IN PCCERT_CONTEXT pCertContext,
    IN OPTIONAL LPFILETIME pTime,
    IN OPTIONAL HCERTSTORE hAdditionalStore,
    IN PCERT_CHAIN_PARA pChainPara,
    IN DWORD dwFlags,
    IN LPVOID pvReserved,
    OUT PCCERT_CHAIN_CONTEXT* ppChainContext);

typedef BOOL (WINAPI *PFNCertVerifyCertificateChainPolicy) (
  LPCSTR pszPolicyOID,
  PCCERT_CHAIN_CONTEXT pChainContext,
  PCERT_CHAIN_POLICY_PARA pPolicyPara,
  PCERT_CHAIN_POLICY_STATUS pPolicyStatus
);

typedef BOOL (*PFNCheckTokenMembership) (
  HANDLE TokenHandle,  // handle to access token
  PSID SidToCheck,     // SID
  PBOOL IsMember       // result
);

typedef BOOL (*PFNSetSecurityDescriptorControl) (
  PSECURITY_DESCRIPTOR pSecurityDescriptor,          // SD
  SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, // control bits
  SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet       // new control bits
);

VOID
MyCertFreeCertificateChain (
    IN PCCERT_CHAIN_CONTEXT pChainContext
    )
{
    PFNCertFreeCertificateChain pfnCertFreeCertificateChain = NULL;
    HMODULE  hModule = NULL;

    hModule = GetModuleHandle("crypt32.dll");
    if (NULL != hModule)
    {
        pfnCertFreeCertificateChain = (PFNCertFreeCertificateChain)
                GetProcAddress(hModule,
                               "CertFreeCertificateChain");
        if (NULL != pfnCertFreeCertificateChain)
        {
            pfnCertFreeCertificateChain(pChainContext);
        }
    }
}

BOOL
MyCertGetCertificateChain (
    IN OPTIONAL HCERTCHAINENGINE hChainEngine,
    IN PCCERT_CONTEXT pCertContext,
    IN OPTIONAL LPFILETIME pTime,
    IN OPTIONAL HCERTSTORE hAdditionalStore,
    IN PCERT_CHAIN_PARA pChainPara,
    IN DWORD dwFlags,
    IN LPVOID pvReserved,
    OUT PCCERT_CHAIN_CONTEXT* ppChainContext
    )
{
    PFNCertGetCertificateChain pfnCertGetCertificateChain = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("crypt32.dll");
    if (NULL != hModule)
    {
        pfnCertGetCertificateChain = (PFNCertGetCertificateChain)
                GetProcAddress(hModule,
                               "CertGetCertificateChain");
        if (NULL != pfnCertGetCertificateChain)
        {
            return pfnCertGetCertificateChain(
                hChainEngine,
                pCertContext,
                pTime,
                hAdditionalStore,
                pChainPara,
                dwFlags,
                pvReserved,
                ppChainContext);
        }
    }
    return FALSE;
}

BOOL
MyCertVerifyCertificateChainPolicy(
  LPCSTR pszPolicyOID,
  PCCERT_CHAIN_CONTEXT pChainContext,
  PCERT_CHAIN_POLICY_PARA pPolicyPara,
  PCERT_CHAIN_POLICY_STATUS pPolicyStatus
)
{
    PFNCertVerifyCertificateChainPolicy pfnCertVerifyCertificateChainPolicy = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("crypt32.dll");
    if (NULL != hModule)
    {
        pfnCertVerifyCertificateChainPolicy = (PFNCertVerifyCertificateChainPolicy)
                GetProcAddress(hModule,
                               "CertVerifyCertificateChainPolicy");
        if (NULL != pfnCertVerifyCertificateChainPolicy)
        {
            return pfnCertVerifyCertificateChainPolicy(
                            pszPolicyOID,
                            pChainContext,
                            pPolicyPara,
                            pPolicyStatus);
        }
    }
    return FALSE;
}

BOOL
MyCheckTokenMembership(
  HANDLE TokenHandle,  // handle to access token
  PSID SidToCheck,     // SID
  PBOOL IsMember       // result
)
{
    PFNCheckTokenMembership pfnCheckTokenMembership = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("advapi32.dll");
    if (NULL != hModule)
    {
        pfnCheckTokenMembership = (PFNCheckTokenMembership)
                GetProcAddress(hModule,
                               "CheckTokenMembership");
        if (NULL != pfnCheckTokenMembership)
        {
            return pfnCheckTokenMembership(
                        TokenHandle,
                        SidToCheck,
                        IsMember);
        }
    }
    return FALSE;
}

BOOL
MySetSecurityDescriptorControl(
  PSECURITY_DESCRIPTOR pSecurityDescriptor,          // SD
  SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, // control bits
  SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet       // new control bits
)
{
    PFNSetSecurityDescriptorControl pfnSetSecurityDescriptorControl = NULL;
    HMODULE hModule = NULL;

    hModule = GetModuleHandle("advapi32.dll");
    if (NULL != hModule)
    {
        pfnSetSecurityDescriptorControl = (PFNSetSecurityDescriptorControl)
                GetProcAddress(hModule,
                               "SetSecurityDescriptorControl");
        if (NULL != pfnSetSecurityDescriptorControl)
        {
            return pfnSetSecurityDescriptorControl(
                        pSecurityDescriptor,
                        ControlBitsOfInterest,
                        ControlBitsToSet);
        }
    }
    return FALSE;
}

HRESULT __stdcall CCEnroll::GetInterfaceSafetyOptions( 
            /* [in]  */ REFIID riid,
            /* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
            /* [out] */ DWORD __RPC_FAR *pdwEnabledOptions) {

    RPC_STATUS rpcStatus;          

    if(0 != UuidCompare((GUID *) &riid, (GUID *) &IID_IDispatch, &rpcStatus) )
        return(E_NOINTERFACE);

    *pdwEnabledOptions   = m_dwEnabledSafteyOptions;
    *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;


    return(S_OK);
}

HRESULT __stdcall CCEnroll::SetInterfaceSafetyOptions( 
            /* [in] */ REFIID riid,
            /* [in] */ DWORD dwOptionSetMask,
            /* [in] */ DWORD dwEnabledOptions) {

    RPC_STATUS rpcStatus;          
    DWORD dwSupport = 0;            

    if(0 != UuidCompare((GUID *) &riid, (GUID *) &IID_IDispatch, &rpcStatus) )
        return(E_NOINTERFACE);

    dwSupport = dwOptionSetMask & ~(INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA);        
    if(dwSupport != 0)
        return(E_FAIL);

    (DWORD)m_dwEnabledSafteyOptions &= ~dwOptionSetMask;
    (DWORD)m_dwEnabledSafteyOptions |= dwEnabledOptions; 
            
return(S_OK);
}


HRESULT
CCEnroll::GetVerifyProv()
{
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == m_hVerifyProv)
    {
        if (!CryptAcquireContextU(
                    &m_hVerifyProv,
                    NULL,
                    m_keyProvInfo.pwszProvName,
                    m_keyProvInfo.dwProvType,
                    CRYPT_VERIFYCONTEXT))
        {
#if DBG
            assert(NULL == m_hVerifyProv);
#endif //DBG
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptAcquireContextUError;
        }
    }

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(CryptAcquireContextUError)
}

BOOL CCEnroll::GetCapiHashAndSigAlgId(ALG_ID rgAlg[2]) {


    DWORD   iHashBest       = 0;
    ALG_ID  arDefaultHash[] = {m_HashAlgId, CALG_SHA1, CALG_MD5};
    DWORD   cDefaultHash    = sizeof(arDefaultHash) / sizeof(DWORD);

    HCRYPTPROV  hProvU      = NULL;

    DWORD       dwFlags     = CRYPT_FIRST;

    DWORD       i;
    PROV_ENUMALGS               enumAlgs;
    DWORD       cb          = sizeof(enumAlgs);

    BOOL        fRet        = TRUE;

    rgAlg[0] = 0;
    rgAlg[1] = 0;

    EnterCriticalSection(&m_csXEnroll);

    // only get a prov if one wasn't passed in.
    if(m_hProv == NULL)
    {
        HRESULT hr;
        hr = GetVerifyProv();
        if (S_OK != hr)
        {
            goto GetVerifyProvError;
        }
        hProvU = m_hVerifyProv;
    }
    else
    {
        // otherwise use the current m_hProv, SCard only likes on
        // CryptAcquireContext to be used.
        hProvU = m_hProv;
    }

    cb = sizeof(enumAlgs);
    while( CryptGetProvParam(
        hProvU,
            PP_ENUMALGS,
        (BYTE *) &enumAlgs,
        &cb,
        dwFlags
        ) ) {

        cb = sizeof(enumAlgs);

        // not first pass anymore
        dwFlags = 0;

        // see if this is a hash alg
        if( ALG_CLASS_HASH == GET_ALG_CLASS(enumAlgs.aiAlgid) ) {

            // get things init with the first hash alg
            if(rgAlg[0] == 0) {
                rgAlg[0] = enumAlgs.aiAlgid;
                iHashBest = cDefaultHash;
            }

            // pick the best one
            for(i=0; i<iHashBest; i++) {

                if(arDefaultHash[i] == enumAlgs.aiAlgid) {
                    rgAlg[0] = enumAlgs.aiAlgid;
                    iHashBest   = i;
                    break;
                }
            }
        }

        // we will only pick up the first signature type
        // in general there is only 1 per csp (Ref: JeffSpel)
        else if( ALG_CLASS_SIGNATURE == GET_ALG_CLASS(enumAlgs.aiAlgid) ) {

            if(rgAlg[1] == 0)
                rgAlg[1] = enumAlgs.aiAlgid;
        }
    }

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);

    // some CSPs say they can't sign, but they really can
    // so if we have not hashalg or sigalg we will put a default
    // in and if the CSP really can't do it, it will error
    // this is for backwards compatibility

    // default hash to sha1
    if(rgAlg[0] == 0)
        rgAlg[0] = CALG_SHA1;

    // default sig to RSA
    if(rgAlg[1] == 0)
        rgAlg[1] = CALG_RSA_SIGN;

#if 0
    if(rgAlg[0] == 0 || rgAlg[1] == 0) {
        SetLastError((DWORD)NTE_BAD_ALGID);
        fRet = FALSE;
    }
#endif

    return(fRet);

TRACE_ERROR(GetVerifyProvError)
}

BOOL CreatePvkProperty(
    CRYPT_KEY_PROV_INFO *pKeyProvInfo,
    PCRYPT_DATA_BLOB    pBlob)
{
    WCHAR   wszKeySpec[11];
    WCHAR   wszProvType[11];
    DWORD   cbContainer;
    DWORD   cbKeySpec;
    DWORD   cbProvType;
    DWORD   cbProvName;

    assert(pBlob != NULL);
    assert(pKeyProvInfo != NULL);

    // convert dwords to strings
    DwordToWide(pKeyProvInfo->dwKeySpec, wszKeySpec);
    DwordToWide(pKeyProvInfo->dwProvType, wszProvType);

    // get total length of string
    cbContainer = (DWORD)(wcslen(pKeyProvInfo->pwszContainerName) + 1) * sizeof(WCHAR);
    cbKeySpec   = (DWORD)(wcslen(wszKeySpec) + 1) * sizeof(WCHAR);
    cbProvType  = (DWORD)(wcslen(wszProvType) + 1) * sizeof(WCHAR);

    cbProvName  = (DWORD)(wcslen(pKeyProvInfo->pwszProvName) + 1) * sizeof(WCHAR);

    pBlob->cbData =
        cbContainer +
        cbKeySpec   +
        cbProvType  +
        cbProvName  +
        sizeof(WCHAR);

    // allocate the string
    if( (pBlob->pbData = (BYTE *) MyCoTaskMemAlloc(pBlob->cbData)) == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(FALSE);
    }

    // copy the strings
    memset(pBlob->pbData, 0, pBlob->cbData);
    memcpy(pBlob->pbData, pKeyProvInfo->pwszContainerName, cbContainer);
    memcpy(&pBlob->pbData[cbContainer], wszKeySpec, cbKeySpec);
    memcpy(&pBlob->pbData[cbContainer + cbKeySpec], wszProvType, cbProvType);
    memcpy(&pBlob->pbData[cbContainer + cbKeySpec + cbProvType], pKeyProvInfo->pwszProvName, cbProvName);

    return(TRUE);
}

static LPWSTR wszEmpty      = L"";
static LPWSTR wszMY         = L"MY";
static LPWSTR wszCA         = L"CA";
static LPWSTR wszROOT       = L"ROOT";
static LPWSTR wszREQUEST    = L"REQUEST";
static LPSTR  szSystemStore = sz_CERT_STORE_PROV_SYSTEM;
// static LPSTR  szSystemStore = sz_CERT_STORE_PROV_SYSTEM_REGISTRY;
/////////////////////////////////////////////////////////////////////////////
// CCEnroll


CCEnroll::~CCEnroll(void) {
    Destruct();
    DeleteCriticalSection(&m_csXEnroll);
}

void CCEnroll::Destruct(void) {

    if(NULL != m_PrivateKeyArchiveCertificate)
    {
        CertFreeCertificateContext(m_PrivateKeyArchiveCertificate);
    }
    if(NULL != m_pCertContextSigner)
    {
        CertFreeCertificateContext(m_pCertContextSigner);
    }
    if(m_pCertContextRenewal != NULL)
        CertFreeCertificateContext(m_pCertContextRenewal);
    if(m_pCertContextStatic != NULL)
        CertFreeCertificateContext(m_pCertContextStatic);

    if(m_keyProvInfo.pwszContainerName != wszEmpty)
        MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);

    if(m_keyProvInfo.pwszProvName != wszEmpty)
        MyCoTaskMemFree(m_keyProvInfo.pwszProvName);

    if(m_MyStore.wszName != wszMY)
        MyCoTaskMemFree(m_MyStore.wszName);

    if(m_CAStore.wszName != wszCA)
        MyCoTaskMemFree(m_CAStore.wszName);

    if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
        MyCoTaskMemFree(m_RootStore.wszName);

    if(m_RequestStore.wszName != wszREQUEST)
        MyCoTaskMemFree(m_RequestStore.wszName);

    if(m_MyStore.szType  != szSystemStore)
        MyCoTaskMemFree(m_MyStore.szType);

    if(m_CAStore.szType != szSystemStore)
        MyCoTaskMemFree(m_CAStore.szType);

    if(m_RootStore.szType != szSystemStore)
        MyCoTaskMemFree(m_RootStore.szType);

    if(m_RequestStore.szType != szSystemStore)
        MyCoTaskMemFree(m_RequestStore.szType);

    if(m_wszSPCFileName != wszEmpty)
        MyCoTaskMemFree(m_wszSPCFileName);

    if(m_wszPVKFileName != wszEmpty)
        MyCoTaskMemFree(m_wszPVKFileName);

    if (NULL != m_pCertContextPendingRequest)
        CertFreeCertificateContext(m_pCertContextPendingRequest);

    if (NULL != m_pPendingRequestTable)
        delete m_pPendingRequestTable; 

    // store handles
    if(m_RootStore.hStore != NULL)
        CertCloseStore(m_RootStore.hStore, 0);
    m_RootStore.hStore = NULL;

    if(m_CAStore.hStore != NULL)
        CertCloseStore(m_CAStore.hStore, 0);
    m_CAStore.hStore = NULL;

    if(m_MyStore.hStore != NULL)
        CertCloseStore(m_MyStore.hStore, 0);
    m_MyStore.hStore = NULL;

    if(m_RequestStore.hStore != NULL)
        CertCloseStore(m_RequestStore.hStore, 0);
    m_RequestStore.hStore = NULL;

    // remove provider handles
    if(m_hProv != NULL)
        CryptReleaseContext(m_hProv, 0);
    m_hProv = NULL;

    if(m_hVerifyProv != NULL)
        CryptReleaseContext(m_hVerifyProv, 0);
    m_hVerifyProv = NULL;

    if (NULL != m_hCachedKey)
    {
        //this should be destroyed early but just in case
        CryptDestroyKey(m_hCachedKey);
    }

    if (NULL != m_pPublicKeyInfo)
    {
        LocalFree(m_pPublicKeyInfo);
        m_pPublicKeyInfo = NULL;
    }

    FreeAllStackExtension();
    FreeAllStackAttribute();
    resetBlobProperties();
}

static LPVOID CoTaskMemAllocTrap(ULONG cb) {

    __try {
        return(CoTaskMemAlloc(cb));

    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_DLL_NOT_FOUND);
        return(NULL);
    }
}

static LPVOID CoTaskMemReallocTrap(LPVOID ptr, ULONG cb) {
    __try {
        return(CoTaskMemRealloc(ptr, cb));
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_DLL_NOT_FOUND);
        return(NULL);
    }
}

static void CoTaskMemFreeTrap(LPVOID ptr) {
    __try {
        CoTaskMemFree(ptr);
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_DLL_NOT_FOUND);
    }
    return;
}


// Initialize the safety options in the constructor, so they won't
// be clobbered when we do a reset(). 
CCEnroll::CCEnroll(void) : m_dwEnabledSafteyOptions(0) {
    __try
    {
        InitializeCriticalSection(&m_csXEnroll);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    Init();
}

HRESULT
CCEnroll::Init(void)
{
    HRESULT hr;
    GUID    guidContainerName;
    char *  sz = NULL;
    RPC_STATUS  rpc_status;

    // set default mem allocators
    if(MyCoTaskMemAlloc == NULL)
    {

        MyCoTaskMemAlloc    = CoTaskMemAllocTrap;
        MyCoTaskMemRealloc  = CoTaskMemReallocTrap;
        MyCoTaskMemFree     = CoTaskMemFreeTrap;
    }

    // get a container based on a guid
    rpc_status = UuidCreate(&guidContainerName);
    if (RPC_S_OK != rpc_status && RPC_S_UUID_LOCAL_ONLY != rpc_status)
    {
        hr = rpc_status;
        goto UuidCreateError;
    }
    rpc_status = UuidToStringA(&guidContainerName, (unsigned char **) &sz);
    if (RPC_S_OK != rpc_status)
    {
        hr = rpc_status;
        goto UuidToStringAError;
    }
    assert(sz != NULL);
    m_keyProvInfo.pwszContainerName = WideFromMB(sz);
    RpcStringFree((unsigned char **) &sz);

    m_keyProvInfo.pwszProvName        = wszEmpty;
    m_keyProvInfo.dwProvType          = PROV_RSA_FULL;
    m_keyProvInfo.dwFlags             = 0;
    m_keyProvInfo.cProvParam          = 0;
    m_keyProvInfo.rgProvParam         = NULL;
    m_keyProvInfo.dwKeySpec           = AT_SIGNATURE;
    m_fEnableSMIMECapabilities =
                    (m_keyProvInfo.dwKeySpec == AT_KEYEXCHANGE);
    m_fSMIMESetByClient               = FALSE;
    m_fKeySpecSetByClient             = FALSE;
    m_hProv                           = NULL;
    m_hVerifyProv                     = NULL;

    m_fDeleteRequestCert              = TRUE;
    m_fUseExistingKey                 = FALSE;
    m_fWriteCertToCSPModified         = FALSE;
    m_fWriteCertToCSP                 = TRUE;     // always want to try
    m_fWriteCertToUserDSModified      = FALSE;
    m_fWriteCertToUserDS              = FALSE;
    m_fReuseHardwareKeyIfUnableToGenNew = TRUE;
    m_fLimitExchangeKeyToEncipherment = FALSE;
    m_dwT61DNEncoding                 = 0;        // or CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG
    m_dwGenKeyFlags                   = 0;
    m_wszSPCFileName                  = wszEmpty;
    m_wszPVKFileName                  = wszEmpty;
    m_HashAlgId                       = 0;

    m_fMyStoreOpenFlagsModified   = FALSE;
    m_MyStore.wszName             = wszMY;
    m_MyStore.szType              = szSystemStore;
    m_MyStore.dwFlags             = CERT_SYSTEM_STORE_CURRENT_USER;
    m_MyStore.hStore              = NULL;

    m_fCAStoreOpenFlagsModified   = FALSE;
    m_CAStore.wszName             = wszCA;
    m_CAStore.szType              = szSystemStore;
    m_CAStore.dwFlags             = CERT_SYSTEM_STORE_CURRENT_USER;
    m_CAStore.hStore              = NULL;

    m_fRootStoreOpenFlagsModified = FALSE;
    m_RootStore.wszName           = wszROOT;
    m_RootStore.szType            = szSystemStore;
    m_RootStore.dwFlags           = CERT_SYSTEM_STORE_CURRENT_USER;
    m_RootStore.hStore            = NULL;

    m_fRequestStoreOpenFlagsModified = FALSE;
    m_RequestStore.wszName        = wszREQUEST ;
    m_RequestStore.szType         = szSystemStore;
    m_RequestStore.dwFlags        = CERT_SYSTEM_STORE_CURRENT_USER;
    m_RequestStore.hStore         = NULL;

    m_PrivateKeyArchiveCertificate= NULL;
    m_pCertContextRenewal         = NULL;
    m_pCertContextSigner         = NULL;
    m_pCertContextStatic          = NULL;
    memset(m_arHashBytesNewCert, 0, sizeof(m_arHashBytesNewCert));
    memset(m_arHashBytesOldCert, 0, sizeof(m_arHashBytesOldCert));
    m_fArchiveOldCert             = FALSE;

    m_pExtStack                 = NULL;
    m_cExtStack                 = 0;

    m_pAttrStack                = NULL;
    m_cAttrStack                = 0;

    m_pExtStackNew              = NULL;
    m_cExtStackNew              = 0;

    m_pAttrStackNew             = NULL;
    m_cAttrStackNew             = 0;

    m_pPropStack                = NULL;
    m_cPropStack                = 0;

    m_fNewRequestMethod         = FALSE;
    m_fCMCFormat                = FALSE;
    m_fHonorRenew               = TRUE; //critical, if passing XECR_PKCS10*
    m_fOID_V2                   = FALSE; //critical
    m_hCachedKey                = NULL;
    m_fUseClientKeyUsage        = FALSE;
    m_lClientId                 = XECI_XENROLL;
    m_dwLastAlgIndex            = MAXDWORD;
    m_fIncludeSubjectKeyID      = TRUE;
    m_fHonorIncludeSubjectKeyID = FALSE;
    m_pPublicKeyInfo            = NULL;

    m_dwSigKeyLenMax = 0;
    m_dwSigKeyLenMin = 0;
    m_dwSigKeyLenDef = 0;
    m_dwSigKeyLenInc = 0;
    m_dwXhgKeyLenMax = 0;
    m_dwXhgKeyLenMin = 0;
    m_dwXhgKeyLenDef = 0;
    m_dwXhgKeyLenInc = 0;

    // Initialize pending info data:
    m_pCertContextPendingRequest       = NULL;
    m_pCertContextLastEnumerated       = NULL;
    m_dwCurrentPendingRequestIndex     = 0; 
    m_pPendingRequestTable             = NULL; 
    memset(&m_hashBlobPendingRequest, 0, sizeof(CRYPT_DATA_BLOB)); 
    ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));

    hr = S_OK;
ErrorReturn:
    return hr;
TRACE_ERROR(UuidToStringAError)
TRACE_ERROR(UuidCreateError)
}

void CCEnroll::FlushStore(StoreType storeType) {
    PSTOREINFO   pStoreInfo = NULL;

    // get store struct
    switch(storeType) {

        case StoreMY:
            pStoreInfo = &m_MyStore;
            break;

        case StoreCA:
            pStoreInfo = &m_CAStore;
            break;

        case StoreROOT:
            pStoreInfo = &m_RootStore;
            break;

        case StoreREQUEST:
            pStoreInfo = &m_RequestStore;
            break;
    }

    EnterCriticalSection(&m_csXEnroll);

    // if store already open, return it
    if(pStoreInfo->hStore != NULL) {

        CertCloseStore(pStoreInfo->hStore, 0);
        pStoreInfo->hStore = NULL;
    }

    // we may have something or not, but return it
    // the errors will be correct.
    LeaveCriticalSection(&m_csXEnroll);
}

HCERTSTORE CCEnroll::GetStore(StoreType storeType) {

    PSTOREINFO   pStoreInfo = NULL;
    HCERTSTORE   hStore     = NULL;

    // get store struct
    switch(storeType) {

        case StoreMY:
            pStoreInfo = &m_MyStore;
            break;

        case StoreCA:
            pStoreInfo = &m_CAStore;
            break;

        case StoreROOT:
            pStoreInfo = &m_RootStore;
            break;

        case StoreREQUEST:
            pStoreInfo = &m_RequestStore;
            break;

        default:
            SetLastError(ERROR_BAD_ARGUMENTS);
            return(NULL);
            break;
    }

    EnterCriticalSection(&m_csXEnroll);

    // if store already open, return it
    if(pStoreInfo->hStore == NULL) {

        // otherwise attempt to open the store
        pStoreInfo->hStore = CertOpenStore(
                pStoreInfo->szType,
                PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                NULL,
                pStoreInfo->dwFlags,
                pStoreInfo->wszName);
    }

    // we may have something or not, but return it
    // the errors will be correct.
    hStore = pStoreInfo->hStore;
    LeaveCriticalSection(&m_csXEnroll);

    return(hStore);
}

HCRYPTPROV CCEnroll::GetProv(DWORD dwFlags) {

    HCRYPTPROV  hProvT          = NULL;
    DWORD       cb              = 0;
    char *      pszProvName     = NULL;
    char *      pszContainerName = NULL;

    EnterCriticalSection(&m_csXEnroll);
    DWORD       dwProvType      = m_keyProvInfo.dwProvType;

    switch(dwFlags) {

        case CRYPT_NEWKEYSET:
            dwFlags = dwFlags | m_keyProvInfo.dwFlags;
            break;

        case CRYPT_DELETEKEYSET:
            if( m_hProv != NULL ) {

                CryptReleaseContext(m_hProv, 0);
                m_hProv = NULL;

                CryptAcquireContextU(&m_hProv,
                     m_keyProvInfo.pwszContainerName,
                     m_keyProvInfo.pwszProvName,
                     m_keyProvInfo.dwProvType,
                     CRYPT_DELETEKEYSET);
            }
            m_hProv = NULL;
            goto CommonReturn;
            break;

        default:
            dwFlags = m_keyProvInfo.dwFlags;
            break;
   }

    if(m_hProv == NULL) {

            if( CryptAcquireContextU(&m_hProv,
             m_keyProvInfo.pwszContainerName,
             m_keyProvInfo.pwszProvName,
             m_keyProvInfo.dwProvType,
             dwFlags) ) {

                // we have the m_hProv, now set the provider name
                // Since this is secondary to the task, don't do error checking
                // nothing here should really fail anyway
                pszProvName = NULL;
                while (TRUE)
                {
                    if(!CryptGetProvParam( m_hProv,
                                    PP_NAME,
                                    (BYTE*)pszProvName,
                                    &cb,
                                    0))
                    {
                        break;
                    }
                    if (NULL != pszProvName)
                    {
                        if(m_keyProvInfo.pwszProvName != wszEmpty)
                            MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
                        m_keyProvInfo.pwszProvName = WideFromMB(pszProvName);
                        break;
                    }
                    pszProvName = (char *)LocalAlloc(LMEM_FIXED, cb);
                    if (NULL == pszProvName)
                    {
                        goto CommonReturn;
                    }
                }

                // Here we just try and get the unique container name
                // If not, just go on

                BOOL fTryAnother = FALSE;
                cb = 0;
                pszContainerName = NULL;
                while (TRUE)
                {
                    if(!CryptGetProvParam( m_hProv,
                                    PP_UNIQUE_CONTAINER,
                                    (BYTE*)pszContainerName,
                                    &cb,
                                    0))
                    {
                        if (NULL == pszContainerName)
                        {
			    fTryAnother = TRUE;
                        }
                        else
                        {
			    LocalFree(pszContainerName); 
			    pszContainerName = NULL;
                        }
                        break;
                    }
                    else
                    {
                        if (NULL != pszContainerName)
                        {
                            //got it, done
                            break;
                        }
                    }
                    pszContainerName = (char *)LocalAlloc(LMEM_FIXED, cb);
                    if (NULL == pszContainerName)
                    {
                        goto CommonReturn;
                    }
                }

                if (fTryAnother)
                {
                    // so we can't get the unique container name,
                    // lets just go for the container name (may not be unique).
                    cb = 0;
                    pszContainerName = NULL;
                    while (TRUE)
                    {
                        if(!CryptGetProvParam(m_hProv,
                                    PP_CONTAINER,
                                    (BYTE*)pszContainerName,
                                    &cb,
                                    0))
                        {
                            if (NULL != pszContainerName)
                            {
				LocalFree(pszContainerName); 
                                pszContainerName = NULL;
                            }
                            break;
                        }
                        else
                        {
                            if (NULL != pszContainerName)
                            {
                                //got it, done
                                break;
                            }
                            pszContainerName = (char *)LocalAlloc(LMEM_FIXED, cb);
                            if (NULL == pszContainerName)
                            {
                                goto CommonReturn;
                            }
                        }
                    }
                }

                // set the container, otherwise use what was there
                if(pszContainerName != NULL) {
                    if( m_keyProvInfo.pwszContainerName != wszEmpty )
                        MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
                    m_keyProvInfo.pwszContainerName = WideFromMB(pszContainerName);
                }

                // now because some providers double duty for provider types
                // get what the the provider thinks its type is
                cb = sizeof(DWORD);
                if(CryptGetProvParam(   m_hProv,
                                        PP_PROVTYPE,
                                        (BYTE *) &dwProvType,
                                        &cb,
                                        0) ) {
                    m_keyProvInfo.dwProvType = dwProvType;
                }
                
        } else {
            m_hProv = NULL;
        }

    }

CommonReturn:
    hProvT = m_hProv;
    LeaveCriticalSection(&m_csXEnroll);
    if (NULL != pszProvName)
    {
        LocalFree(pszProvName);
    }
    if (NULL != pszContainerName)
    {
        LocalFree(pszContainerName);
    }
    return(hProvT);
}


BOOL CCEnroll::SetKeyParams(
    PCRYPT_KEY_PROV_INFO pKeyProvInfo
) {

    EnterCriticalSection(&m_csXEnroll);

    // remove provider handles
    if(m_hProv != NULL)
        CryptReleaseContext(m_hProv, 0);
    m_hProv = NULL;

    if(m_hVerifyProv != NULL)
        CryptReleaseContext(m_hVerifyProv, 0);
    m_hVerifyProv = NULL;

    put_ContainerNameWStr(pKeyProvInfo->pwszContainerName);
    put_ProviderNameWStr(pKeyProvInfo->pwszProvName);
    put_ProviderFlags(pKeyProvInfo->dwFlags);
    put_KeySpec(pKeyProvInfo->dwKeySpec);
    put_ProviderType(pKeyProvInfo->dwProvType);

    // someday we will have to pay attention to this too.
    m_keyProvInfo.cProvParam          = 0;
    m_keyProvInfo.rgProvParam         = NULL;

    LeaveCriticalSection(&m_csXEnroll);

    return(TRUE);
}

HRESULT STDMETHODCALLTYPE CCEnroll::createPKCS10(
            /* [in] */ BSTR DNName,
            /* [in] */ BSTR wszPurpose,
            /* [retval][out] */ BSTR __RPC_FAR *pPKCS10) {

    return(createPKCS10WStrBStr(DNName, wszPurpose, pPKCS10));
}

HRESULT  CCEnroll::createPKCS10WStrBStr(
            LPCWSTR DNName,
            LPCWSTR wszPurpose,
            BSTR __RPC_FAR *pPKCS10) {

    HRESULT                     hr              = S_OK;
    CRYPT_DATA_BLOB             blobPKCS10;

    memset(&blobPKCS10, 0, sizeof(CRYPT_DATA_BLOB));

    hr = createPKCS10WStr(DNName, wszPurpose, &blobPKCS10);
    if(S_OK != hr)
    {
        goto createPKCS10Error;
    }

    // BASE64 encode pkcs 10, no header for backward compatible
    hr = BlobToBstring(&blobPKCS10, CRYPT_STRING_BASE64, pPKCS10);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

CommonReturn:

    if(NULL != blobPKCS10.pbData)
    {
        MyCoTaskMemFree(blobPKCS10.pbData);
    }
    return(hr);

ErrorReturn:
    if(*pPKCS10 != NULL)
        SysFreeString(*pPKCS10);
    *pPKCS10 = NULL;

    goto CommonReturn;

TRACE_ERROR(createPKCS10Error);
TRACE_ERROR(BlobToBstringError);
}

HRESULT CCEnroll::AddCertsToStores(
    HCERTSTORE    hStoreMsg,
    LONG         *plCertInstalled
    ) {

    HCERTSTORE                  hStoreRoot              = NULL;
    HCERTSTORE                  hStoreCA                = NULL;
    PCCERT_CONTEXT              pCertContext            = NULL;
    PCCERT_CONTEXT              pCertContextLast        = NULL;
    LONG                        lCertInstalled = 0;
    HRESULT hr = S_OK;

    //init
    if (NULL != plCertInstalled)
    {
        *plCertInstalled = 0;
    }

    EnterCriticalSection(&m_csXEnroll);

    if( (hStoreCA = GetStore(StoreCA)) == NULL )
        goto ErrorCertOpenCAStore;

    if( (hStoreRoot = GetStore(StoreROOT)) == NULL )
        goto ErrorCertOpenRootStore;

    // now just place the rest of the cert in either the ROOT or CA store
    // we know we removed the end-entity cert from the msg store already
    // put all certs that came in the message into the appropriate store
    while( (pCertContext = CertEnumCertificatesInStore(
                        hStoreMsg,
                        pCertContextLast)) != NULL ) {

        // if it is a self sign, it is a ROOT
        if( CertCompareCertificateName(
                CRYPT_ASN_ENCODING,
                &pCertContext->pCertInfo->Subject,
                &pCertContext->pCertInfo->Issuer) ) {

            // to root store could invoke a pop up, check cancel button
            // but don't error out from any fail
            if (MySafeCertAddCertificateContextToStore(
                    hStoreRoot,
                    pCertContext,
                    CERT_STORE_ADD_USE_EXISTING,
                    NULL, 
		    m_dwEnabledSafteyOptions))
            {
                ++lCertInstalled;
            }
            else
            {
                if (S_OK == hr)
                {
                    //save the 1st error as return
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hr)
                    {
                        //map generic access deny to xenroll error
                        hr = XENROLL_E_CANNOT_ADD_ROOT_CERT;
                    }
                }
                //don't goto error here and finish the loop
            }
        }

        // if it is not the MY cert, it must go in the CA store
        // do nothing with the MY cert as we already handled it
        else  {

            // likewise we don't care if these get added to the
            // CA store
            if (MySafeCertAddCertificateContextToStore(
                    hStoreCA,
                    pCertContext,
                    CERT_STORE_ADD_USE_EXISTING,
                    NULL, 
		    m_dwEnabledSafteyOptions))
            {
                //no error code check
                ++lCertInstalled;
            }
        }

        pCertContextLast = pCertContext;
    }
    pCertContextLast = NULL;
    if (NULL != plCertInstalled)
    {
        *plCertInstalled = lCertInstalled;
    }

CommonReturn:

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);

    goto CommonReturn;

TRACE_ERROR(ErrorCertOpenCAStore);
TRACE_ERROR(ErrorCertOpenRootStore);
}

BOOL
IsDesiredProperty(DWORD  dwPropertyId)
{
    DWORD  DesiredIds[] = {
        CERT_PVK_FILE_PROP_ID,
        CERT_FRIENDLY_NAME_PROP_ID,
        CERT_DESCRIPTION_PROP_ID,
        CERT_RENEWAL_PROP_ID,
    };
    DWORD i;

    for (i = 0; i < ARRAYSIZE(DesiredIds); ++i)
    {
        if (dwPropertyId == DesiredIds[i])
        {
            return TRUE;
        }
    }
    return FALSE;
}

BOOL
IsFilteredOutProperty(DWORD  dwPropertyId)
{
    DWORD  FilteredIds[] = {
        XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
        XENROLL_PASS_THRU_PROP_ID,
        CERT_KEY_PROV_INFO_PROP_ID,
        CERT_ENROLLMENT_PROP_ID, //pending property
    };
    DWORD i;

    for (i = 0; i < ARRAYSIZE(FilteredIds); ++i)
    {
        if (dwPropertyId == FilteredIds[i])
        {
            return TRUE;
        }
    }
    return FALSE;
}

HRESULT CCEnroll::GetEndEntityCert(
    PCRYPT_DATA_BLOB    pBlobPKCS7,
    BOOL                fSaveToStores,
    PCCERT_CONTEXT     *ppCert
    )
{
    HRESULT                     hr = S_OK;
    HCERTSTORE                  hStoreMsg               = NULL;
    HCERTSTORE                  hStoreMy                = NULL;
    HCERTSTORE                  hStoreRequest           = NULL;

    PCCERT_CONTEXT              pCertContextLast        = NULL;
    PCCERT_CONTEXT              pCertContextRequest     = NULL;
    PCCERT_CONTEXT              pCertContextMsg         = NULL;
    PCCERT_CONTEXT              pCertContextArchive     = NULL;

    PCRYPT_KEY_PROV_INFO                pKeyProvInfo            = NULL;
    DWORD                       cb                      = 0;
    CRYPT_DATA_BLOB             blobData;

    CRYPT_HASH_BLOB             blobHash                = {sizeof(m_arHashBytesNewCert), m_arHashBytesNewCert};
    CRYPT_HASH_BLOB             blobHashRenew           = {sizeof(m_arHashBytesOldCert), m_arHashBytesOldCert};

    RequestFlags                requestFlags;
    CRYPT_HASH_BLOB             requestFlagsBlob;

    CRYPT_HASH_BLOB             renewalCertBlob;

    //Bug #202557 for IE3.02 upd clients (xiaohs)
    HCRYPTPROV                  hProv=NULL;
    BOOL  fSetting;
    DWORD                       dwPropertyId;
    CRYPT_DATA_BLOB             blobProp;
    BYTE                        *pbArchivedKeyHash = NULL;
    DWORD                        cbArchivedKeyHash = 0;

    EnterCriticalSection(&m_csXEnroll);

    memset(&requestFlags, 0, sizeof(RequestFlags));
    memset(&blobData, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&requestFlagsBlob, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&renewalCertBlob, 0, sizeof(CRYPT_DATA_BLOB));
    ZeroMemory(&blobProp, sizeof(blobProp));

    if (NULL == ppCert)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    //init return
    *ppCert = NULL;

    if(!MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                       pBlobPKCS7,
                       (CERT_QUERY_CONTENT_FLAG_CERT |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
                       CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED) ,
                       CERT_QUERY_FORMAT_FLAG_ALL,
                       0,
                       NULL,
                       NULL,
                       NULL,
                       &hStoreMsg,
                       NULL,
                       NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto ErrorCryptQueryObject;
    }

    // check to see if this hash is in the message
    if (m_pCertContextStatic == NULL  ||
        (NULL == (pCertContextMsg = CertFindCertificateInStore(
                                hStoreMsg,
                                X509_ASN_ENCODING,
                                0,
                                CERT_FIND_HASH,
                                &blobHash,
                                NULL))))
    {
        // open the request store
        if (NULL == (hStoreRequest = GetStore(StoreREQUEST)))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenRequestStore;
        }
        // find cert in request store that matches cert
        // in message, by public key
        while (NULL != (pCertContextMsg = CertEnumCertificatesInStore(
                            hStoreMsg,
                            pCertContextLast)))
        {
            // check to see if this is in the request store
            if (NULL != (pCertContextRequest = CertFindCertificateInStore(
                    hStoreRequest,
                    CRYPT_ASN_ENCODING,
                    0,
                    CERT_FIND_PUBLIC_KEY,
                    (void *) &pCertContextMsg->pCertInfo->SubjectPublicKeyInfo,
                    NULL)))
            {
                // found a match, get out
                break;
            }

            pCertContextLast = pCertContextMsg;
        }
        pCertContextLast = NULL;

        // if we didn't find one, then GetLastError was set either
        // by CertEnumCerificatesInStore or CertEnumCerificatesInStore
        if (NULL == pCertContextRequest)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorNoCertFound;
        }

        if (fSaveToStores)
        {
        // check archived key hash property first
        // if the property exists, means key archival was in the request
        cb = 0;
        while (TRUE)
        {
            if(!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    CERT_ARCHIVED_KEY_HASH_PROP_ID,
                    pbArchivedKeyHash,
                    &cbArchivedKeyHash))
            {
                if (NULL == pbArchivedKeyHash)
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    if (MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND) == hr)
                    {
                        //no such property, so we are done
                        break;
                    }
                    // some other error
                    goto ErrorCertGetCertificateContextProperty;
                }
                else
                {
                    //if pbArchivedKeyHash non-null, error
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertGetCertificateContextProperty;
                }
            }
            if (NULL != pbArchivedKeyHash)
            {
                //got it, done
                break;
            }
            pbArchivedKeyHash = (BYTE*)LocalAlloc(
                                    LMEM_FIXED, cbArchivedKeyHash);
            if (NULL == pbArchivedKeyHash)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }

        if (NULL != pbArchivedKeyHash && NULL == m_blobResponseKAHash.pbData)
        {
            //request cert has archived key hash but response
            //doesn't contain key hash for verification. maybe
            //a spoofing response?
            hr = XENROLL_E_RESPONSE_KA_HASH_NOT_FOUND;
            goto ResponseKAHashNotFoundError;
        }
        if (NULL == pbArchivedKeyHash && NULL != m_blobResponseKAHash.pbData)
        {
            //request cert doesn't have archived key hash but
            //response does. confliciting. seems no security harm
            hr = XENROLL_E_RESPONSE_UNEXPECTED_KA_HASH;
            goto ResponseUnexpectedKAHashError;
        }
        if (NULL != pbArchivedKeyHash && NULL != m_blobResponseKAHash.pbData)
        {
            //now we should check if they match
            //compare size and hash
            if (cbArchivedKeyHash != m_blobResponseKAHash.cbData ||
                0 != memcmp(pbArchivedKeyHash,
                            m_blobResponseKAHash.pbData,
                            cbArchivedKeyHash))
            {
                //oh, potential attack
                hr = XENROLL_E_RESPONSE_KA_HASH_MISMATCH;
                //should remove the request cert?
                goto ResponseKAMismatchError;
            }
        }
        }

        // get those request cert properties that are,
        // either the property not blob property
        // or blob property needs special handling
        // Important: remember to add these Ids in IsFilteredOutProperty
        fSetting = TRUE;
        cb = 0;
        while (TRUE)
        {
            if(!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    CERT_KEY_PROV_INFO_PROP_ID,
                    pKeyProvInfo,
                    &cb))
            {
                if (NULL == pKeyProvInfo)
                {
                    //skip setting
                    fSetting = FALSE;
                    break;
                }
                else
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertGetCertificateContextProperty;
                }
            }
            if (NULL != pKeyProvInfo)
            {
                //got it, done
                break;
            }
            pKeyProvInfo = (PCRYPT_KEY_PROV_INFO)LocalAlloc(LMEM_FIXED, cb);
            if (NULL == pKeyProvInfo)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        if (fSetting)
        {
            // put the property on the returned cert
            if( !CertSetCertificateContextProperty(
                    pCertContextMsg,
                    CERT_KEY_PROV_INFO_PROP_ID,
                    0,
                    pKeyProvInfo) )
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorSetMyCertPropError;
            }

            // Set the provider info
            SetKeyParams(pKeyProvInfo);
        }

        fSetting = TRUE;
        while (TRUE)
        {
            if(!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    XENROLL_PASS_THRU_PROP_ID,
                    requestFlagsBlob.pbData,
                    &requestFlagsBlob.cbData) )
            {
                if (NULL == requestFlagsBlob.pbData)
                {
                    //do nothing
                    fSetting = FALSE;
                    break;
                }
                else
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorDecodeRequestFlags;
                }
            }
            if (NULL != requestFlagsBlob.pbData)
            {
                //got it, done
                break;
            }
            requestFlagsBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                                requestFlagsBlob.cbData);
            if (NULL == requestFlagsBlob.pbData)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }

        if (fSetting)
        {
            // get the encoded blob
            cb = sizeof(requestFlags);
            // since this is a private data structure, its size should be
            // known and this should aways pass
            if (!CryptDecodeObject(
                    CRYPT_ASN_ENCODING,
                    XENROLL_REQUEST_INFO,
                    requestFlagsBlob.pbData,
                    requestFlagsBlob.cbData,
                    0,
                    &requestFlags,
                    &cb))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorDecodeRequestFlags;
            }
            // now set the flags
            if(!m_fWriteCertToCSPModified)
                m_fWriteCertToCSP     = requestFlags.fWriteToCSP;
            if(!m_fWriteCertToUserDSModified)
                m_fWriteCertToUserDS  = requestFlags.fWriteToDS;
            if(!m_fRequestStoreOpenFlagsModified)
                m_RequestStore.dwFlags = requestFlags.openFlags;
            if(!m_fMyStoreOpenFlagsModified)
                m_MyStore.dwFlags =   (m_MyStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
                                    (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
            if(!m_fCAStoreOpenFlagsModified)
                m_CAStore.dwFlags =   (m_CAStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
                                    (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
            if(!m_fRootStoreOpenFlagsModified) { 
		//
		// POTENTIAL SCRIPTING VIOLATION: we're mapping request store flags directly to root store flags. 
		// If they have set request store flags to local machine, propagate this setting to the root store flags, 
		// but set the root store name to "CA". 
		// 
		if (0 != m_dwEnabledSafteyOptions) { 
		    if (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCAL_MACHINE) { 
			m_RootStore.wszName = wszCA; 
		    }
		}
                m_RootStore.dwFlags = (m_RootStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
		    (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
	    }
        }

        // see if this is a renewal request
        m_fArchiveOldCert = FALSE;
        fSetting = TRUE;
        while (TRUE)
        {
            // get the encoded blob
            if (!CertGetCertificateContextProperty(
                    pCertContextRequest,
                    XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
                    renewalCertBlob.pbData,
                    &renewalCertBlob.cbData))
            {
                if (NULL == renewalCertBlob.pbData)
                {
                    fSetting = FALSE;
                    break;
                }
                else
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertGetCertificateContextProperty;
                }
            }
            if (NULL != renewalCertBlob.pbData)
            {
                //got it, done
                break;
            }
            renewalCertBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                                renewalCertBlob.cbData);
            if (NULL == renewalCertBlob.pbData)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        if (fSetting)
        {
            //Bug #202557 for IE3.02 upd clients (xiaohs)
            if (NULL==hProv)
            {
                if(!CryptAcquireContext(
                        &hProv,
                        NULL,
                        MS_DEF_PROV,
                        PROV_RSA_FULL,
                        CRYPT_VERIFYCONTEXT))
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorAcquireContext;
                }
            }

            if (!CryptHashCertificate(
                hProv,  //NULL,         Bug #202557 for IE3.02 upd clients (xiaohs)
                0,      //alg
                X509_ASN_ENCODING,      //0 dwFlags
                renewalCertBlob.pbData,
                renewalCertBlob.cbData,
                blobHashRenew.pbData,
                &blobHashRenew.cbData))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorCryptHashCertificate;
            }
            m_fArchiveOldCert = TRUE;
        }

        //get rest of blob properties from request store and set to the cert
        dwPropertyId = CertEnumCertificateContextProperties(
                            pCertContextRequest, 0);  //enum from 1st
        while (0 != dwPropertyId)
        {
//            if (!IsFilteredOutProperty(dwPropertyId))
//because iis cert install doesn't like to copy all properties from
//request cert to install cert we just copy selected properties for now
            if (IsDesiredProperty(dwPropertyId))
            {
                fSetting = TRUE;
                while (TRUE)
                {
                    if (!CertGetCertificateContextProperty(
                            pCertContextRequest,
                            dwPropertyId,
                            blobProp.pbData,
                            &blobProp.cbData))
                    {
                        //no get, no set, go on
                        fSetting = FALSE;
                        break;
                    }
                    if (NULL != blobProp.pbData)
                    {
                        //done
                        break;
                    }
                    blobProp.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, 
                                                        blobProp.cbData);
                    if (NULL == blobProp.pbData)
                    {
                        goto OutOfMemoryError;
                    }
                }
                if (fSetting)
                {
                    //should get the property from the request cert
                    if (!CertSetCertificateContextProperty(
                                pCertContextMsg,
                                dwPropertyId,
                                0,
                                &blobProp))
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto ErrorSetMyCertPropError;
                    }
                }
                if (NULL != blobProp.pbData)
                {
                    //set for the next enum
                    LocalFree(blobProp.pbData);
                    blobProp.pbData = NULL;
                }
            }
            dwPropertyId = CertEnumCertificateContextProperties(
                                  pCertContextRequest,
                                  dwPropertyId);
        }

        // save this away in the cache
        if(m_pCertContextStatic != NULL)
            CertFreeCertificateContext(m_pCertContextStatic);

        m_pCertContextStatic = CertDuplicateCertificateContext(pCertContextMsg);

        //Bug #202557 for IE3.02 upd clients (xiaohs)
        if(NULL==hProv)
        {
            if(!CryptAcquireContext(
                &hProv,
                NULL,
                MS_DEF_PROV,
                PROV_RSA_FULL,
                CRYPT_VERIFYCONTEXT))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto ErrorAcquireContext;
            }
        }

        if( !CryptHashCertificate(
            hProv,             //NULL Bug #202557 for IE3.02 upd clients (xiaohs)
            0,
            X509_ASN_ENCODING,
            pCertContextMsg->pbCertEncoded,
            pCertContextMsg->cbCertEncoded,
            blobHash.pbData,
            &blobHash.cbData) )
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCryptHashCertificate;
        }
    }

    // at this point we have 2 context m_pCertContextStatic which we want to return to the user
    // and pCertContextMsg which we want to delete from the Msg store
    assert(pCertContextMsg != NULL);
    CertDeleteCertificateFromStore(pCertContextMsg);
    pCertContextMsg = NULL; // freed by the delete

    // we want to return our static, so make a dup and this is what we will return
    assert(m_pCertContextStatic != NULL);
    pCertContextMsg = CertDuplicateCertificateContext(m_pCertContextStatic);

    // put these in the stores if asked
    if(fSaveToStores) {

        // open the stores
        if( (hStoreMy = GetStore(StoreMY)) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenMYStore;
        }

        // we know that the pCertContextMsg is a dup of the end-entity cert in m_pCertContextStatic
        // and we want to put this in the MY store
        assert(pCertContextMsg != NULL);
        if( !MySafeCertAddCertificateContextToStore(
                hStoreMy,
                pCertContextMsg,
                CERT_STORE_ADD_USE_EXISTING,
                NULL,
		m_dwEnabledSafteyOptions) )
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertAddToMyStore;
        }

        // If we have renewal, then mark the old cert as an archive
        if(m_fArchiveOldCert &&
            ((pCertContextArchive = CertFindCertificateInStore(
                    hStoreMy,
                    X509_ASN_ENCODING,
                    0,
                    CERT_FIND_HASH,
                    &blobHashRenew,
                    NULL)) != NULL) ) {

            // Set the Archive property on the cert.
            // crypt32 in IE3.02upd does not support this prop, so don't fail on error
            CertSetCertificateContextProperty(
                                pCertContextArchive,
                                CERT_ARCHIVED_PROP_ID,
                                0,
                                &blobData);

            //set new cert hash on old archived cert
            //ignore error if it fails
            CertSetCertificateContextProperty(
                                pCertContextArchive,
                                CERT_RENEWAL_PROP_ID,
                                0,
                                &blobHash);
        }

        // add the rest of the certs to the stores
        hr = AddCertsToStores(hStoreMsg, NULL);
        //ignore cancel error since it from root cert install
        //ignore XENROLL_E_CANNOT_ADD_ROOT_CERT also
        if (S_OK != hr &&
            MY_HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr &&
            XENROLL_E_CANNOT_ADD_ROOT_CERT != hr)
        {
            goto ErrorAddCertsToStores;
        }
    }

    *ppCert = pCertContextMsg;

CommonReturn:

    //Bug #202557 for IE3.02 upd clients (xiaohs)
    if(hProv)
        CryptReleaseContext(hProv, 0);

    if(pCertContextRequest != NULL)
        CertFreeCertificateContext(pCertContextRequest);

    if(pCertContextArchive != NULL)
        CertFreeCertificateContext(pCertContextArchive);

    // it really should be NULL
    assert(pCertContextLast == NULL);

    if(hStoreMsg != NULL)
        CertCloseStore(hStoreMsg, 0);

    // we need to do this because the store that may be opened is the systemstore, but
    // the store we may need is the local machine store, but we don't know that until the
    // system store finds the request cert in the local machine physical store.
    // Later when we do the delete, we want the local machine store open.
    FlushStore(StoreREQUEST);

    if (NULL != requestFlagsBlob.pbData)
    {
        LocalFree(requestFlagsBlob.pbData);
    }
    if (NULL != renewalCertBlob.pbData)
    {
        LocalFree(renewalCertBlob.pbData);
    }
    if (NULL != blobProp.pbData)
    {
        LocalFree(blobProp.pbData);
    }
    if (NULL != pKeyProvInfo)
    {
        LocalFree(pKeyProvInfo);
    }
    if (NULL != pbArchivedKeyHash)
    {
        LocalFree(pbArchivedKeyHash);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return (hr);

ErrorReturn:
    if(NULL != pCertContextMsg)
    {
        CertFreeCertificateContext(pCertContextMsg);
    }
    goto CommonReturn;

TRACE_ERROR(ErrorCryptHashCertificate);
TRACE_ERROR(ErrorCertOpenMYStore);
TRACE_ERROR(ErrorCertAddToMyStore);
TRACE_ERROR(ErrorCryptQueryObject);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorNoCertFound);
TRACE_ERROR(ErrorCertGetCertificateContextProperty);
TRACE_ERROR(ErrorSetMyCertPropError);
TRACE_ERROR(ErrorDecodeRequestFlags);
TRACE_ERROR(ErrorAcquireContext);      //Bug #202557 for IE3.02 upd clients (xiaohs)
TRACE_ERROR(ErrorAddCertsToStores);
TRACE_ERROR(OutOfMemoryError);
TRACE_ERROR(InvalidParameterError);
TRACE_ERROR(ResponseKAMismatchError)
TRACE_ERROR(ResponseUnexpectedKAHashError)
TRACE_ERROR(ResponseKAHashNotFoundError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::getCertFromPKCS7(
                        /* [in] */ BSTR wszPKCS7,
                        /* [retval][out] */ BSTR __RPC_FAR *pbstrCert
) {

    HRESULT     hr;                     
    CRYPT_DATA_BLOB             blobPKCS7;
    CRYPT_DATA_BLOB             blobX509;
    PCCERT_CONTEXT              pCertContextMy          = NULL;

    assert(wszPKCS7 != NULL && pbstrCert != NULL);
    if (NULL == wszPKCS7 || NULL == pbstrCert)
	goto PointerError; 

    // just put into a blob
    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    // Get a Cert Context for the end-entity
    if( (pCertContextMy = getCertContextFromPKCS7(&blobPKCS7)) == NULL)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto getCertContextFromPKCS7Error;
    }

    blobX509.pbData = pCertContextMy->pbCertEncoded;
    blobX509.cbData = pCertContextMy->cbCertEncoded;
    //base64 with no header for backward compatible
    hr = BlobToBstring(&blobX509, CRYPT_STRING_BASE64, pbstrCert);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if(pCertContextMy != NULL)
        CertFreeCertificateContext(pCertContextMy);

    return(hr);

SET_HRESULT(PointerError, E_POINTER); 
TRACE_ERROR(getCertContextFromPKCS7Error);
TRACE_ERROR(BlobToBstringError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptPKCS7(
                        /* [in] */ BSTR wszPKCS7) {

    CRYPT_DATA_BLOB             blobPKCS7;
    HRESULT                     hr; 

    assert(wszPKCS7 != NULL);
    if (NULL == wszPKCS7) 
	goto PointerError; 

    // just put into a blob
    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    // accept the blob
    hr = acceptPKCS7Blob(&blobPKCS7);
 ErrorReturn:
    return hr; 

SET_HRESULT(PointerError, E_POINTER); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::createFilePKCS10(
    /* [in] */ BSTR DNName,
    /* [in] */ BSTR Usage,
    /* [in] */ BSTR wszPKCS10FileName) {
    return(createFilePKCS10WStr(DNName, Usage, wszPKCS10FileName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::addCertTypeToRequest(
            /* [in] */ BSTR CertType) {
    return(AddCertTypeToRequestWStr(CertType));
}


HRESULT STDMETHODCALLTYPE CCEnroll::addCertTypeToRequestEx( 
    IN  LONG            lType,
    IN  BSTR            bstrOIDOrName,
    IN  LONG            lMajorVersion,
    IN  BOOL            fMinorVersion,
    IN  LONG            lMinorVersion)
{
    return AddCertTypeToRequestWStrEx(
                        lType,
                        bstrOIDOrName,
                        lMajorVersion,
                        fMinorVersion,
                        lMinorVersion);
                    
}

HRESULT STDMETHODCALLTYPE CCEnroll::getProviderType( 
    IN  BSTR  strProvName,
    OUT LONG *plProvType)
{
    return getProviderTypeWStr(strProvName, plProvType);
}

HRESULT STDMETHODCALLTYPE CCEnroll::addNameValuePairToSignature(
    /* [in] */ BSTR Name,
    /* [in] */ BSTR Value) {
    return(AddNameValuePairToSignatureWStr(Name, Value));
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptFilePKCS7(
    /* [in] */ BSTR wszPKCS7FileName) {
    return(acceptFilePKCS7WStr(wszPKCS7FileName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::freeRequestInfo(
    /* [in] */ BSTR bstrPKCS7OrPKCS10)
{
    HRESULT  hr;
    CRYPT_DATA_BLOB blob; 
    BYTE *pbData = NULL;
    DWORD cbData = 0;

    // could be base64
    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                        (WCHAR*)bstrPKCS7OrPKCS10,
                        SysStringLen(bstrPKCS7OrPKCS10),
                        CRYPT_STRING_ANY,
                        pbData,
                        &cbData,
                        NULL,
                        NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbData)
        {
            break; //done
        }
        pbData = (BYTE*)LocalAlloc(LMEM_FIXED, cbData);
        if (NULL == pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    blob.cbData = cbData;
    blob.pbData = pbData;

    hr = freeRequestInfoBlob(blob); 
    if (S_OK != hr)
    {
        goto freeRequestInfoBlobError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pbData)
    {
        LocalFree(pbData);
    }
    return hr;

TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(freeRequestInfoBlobError)
}


//
// MY STORE
//
HCERTSTORE STDMETHODCALLTYPE CCEnroll::getMyStore( void)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
    return NULL; 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_MyStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*pbstrName = SysAllocString(m_MyStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreName(
    /* [in] */ BSTR bstrName) {
    return(put_MyStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreType(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_MyStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreType(
    /* [in] */ BSTR bstrType) {
    return(put_MyStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreFlags(
    /* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_MyStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe. 
// See VerifyStoreFlagsSafeForScripting(). 
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreFlags(
    /* [in] */ LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    // If we're marked as safe for scripting, see if the flags passed in are safe:
    if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError; 

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {

        // set the my store flags
        m_MyStore.dwFlags = dwFlags;
        m_fMyStoreOpenFlagsModified = TRUE;
        m_keyProvInfo.dwFlags |= KeyLocationFromStoreLocation(dwFlags);

        // track the request store location to the my store, only if the request store has not been modified
        // do NOT set the modify bit for the request store, this is a default
        if(!m_fRequestStoreOpenFlagsModified) {
            m_RequestStore.dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
            m_RequestStore.dwFlags |= (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
        }
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_MyStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*szwName = CopyWideString(m_MyStore.wszName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreNameWStr(
    /* [in] */ LPWSTR szwName) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);
     
    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_MyStore.wszName != wszMY)
            MyCoTaskMemFree(m_MyStore.wszName);
        if( (m_MyStore.wszName = CopyWideString(szwName)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }
   
 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreTypeWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_MyStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}


//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreTypeWStr(
    /* [in] */ LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_MyStore.szType != szSystemStore)
            MyCoTaskMemFree(m_MyStore.szType);
        if( (m_MyStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

//
// CA STORE
//
HCERTSTORE STDMETHODCALLTYPE CCEnroll::getCAStore( void)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
    return NULL; 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_CAStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*pbstrName = SysAllocString(m_CAStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreName(
    /* [in] */ BSTR bstrName) {
    return(put_CAStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreType(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_CAStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreType(
    /* [in] */ BSTR bstrType) {
    return(put_CAStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreFlags(
    /* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_CAStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe. 
// See VerifyStoreFlagsSafeForScripting(). 
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreFlags(
    /* [in] */ LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    // If we're marked as safe for scripting, see if the flags passed in are safe:
    if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError; 

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        m_fCAStoreOpenFlagsModified = TRUE;
        m_CAStore.dwFlags = dwFlags;
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_CAStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*szwName = CopyWideString(m_CAStore.wszName)) == NULL )
         hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreNameWStr(
    /* [in] */ LPWSTR szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_CAStore.wszName != wszCA)
            MyCoTaskMemFree(m_CAStore.wszName);
        if( (m_CAStore.wszName = CopyWideString(szwName)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreTypeWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_CAStore.szType)) == NULL )
         hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreTypeWStr(
    /* [in] */ LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_CAStore.szType != szSystemStore)
            MyCoTaskMemFree(m_CAStore.szType);
        if( (m_CAStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

//
// ROOT STORE
//
HCERTSTORE STDMETHODCALLTYPE CCEnroll::getROOTHStore( void)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
    return NULL; 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_RootStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*pbstrName = SysAllocString(m_RootStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreName(
    /* [in] */ BSTR bstrName) {
    return(put_RootStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreType(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_RootStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreType(
    /* [in] */ BSTR bstrType) {
    return(put_RootStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreFlags(
    /* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_RootStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe. 
// See VerifyStoreFlagsSafeForScripting(). 
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreFlags(
    /* [in] */ LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    // If we're marked as safe for scripting, see if the flags passed in are safe:
    if (0 != m_dwEnabledSafteyOptions) { 
	// see if the caller passed safe flags:
	if (!VerifyStoreFlagsSafeForScripting(dwFlags))
	    goto AccessDeniedError; 

	// extra check for root store:  don't allow CERT_SYSTEM_STORE_LOCAL_MACHINE for the root store. 
	// if they want to install a machine cert through script, it'll go to the CA store:
	// NOTE: we don't modify the store name if m_RootStore.hStore is NULL, as we'll return E_ACCESSDENIED
	// anyway, and we shouldn't modify this on error. 
	if (NULL == m_RootStore.hStore) { 
	    if (dwFlags & CERT_SYSTEM_STORE_LOCAL_MACHINE) { 
		m_RootStore.wszName = wszCA; 
	    } else { 
		m_RootStore.wszName = wszROOT; 
	    }
	}
    }

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        m_fRootStoreOpenFlagsModified = TRUE;
        m_RootStore.dwFlags = dwFlags;
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
   
    if(m_RootStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*szwName = CopyWideString(m_RootStore.wszName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreNameWStr(
    /* [in] */ LPWSTR szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
            MyCoTaskMemFree(m_RootStore.wszName);
        if( (m_RootStore.wszName = CopyWideString(szwName)) == NULL )
             hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}


HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreTypeWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_RootStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreTypeWStr(
    /* [in] */ LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RootStore.szType != szSystemStore)
            MyCoTaskMemFree(m_RootStore.szType);
        if( (m_RootStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

//
// REQUEST STORE
//
HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_RequestStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*pbstrName = SysAllocString(m_RequestStore.wszName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreName(
    /* [in] */ BSTR bstrName) {
    return(put_RequestStoreNameWStr(bstrName));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreType(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrType = BSTRFromMB(m_RequestStore.szType)) == NULL )
         hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreType(
    /* [in] */ BSTR bstrType) {
    return(put_RequestStoreTypeWStr(bstrType));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreFlags(
    /* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_RequestStore.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe. 
// See VerifyStoreFlagsSafeForScripting(). 
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreFlags(
    /* [in] */ LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    // If we're marked as safe for scripting, see if the flags passed in are safe:
    if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError; 

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {

        // set the request store flags
        m_RequestStore.dwFlags = dwFlags;
        m_fRequestStoreOpenFlagsModified = TRUE;

        // track the My store location to the request store, only if the my store has not been modified
        // do NOT set the modify bit for the my store, this is a default
        if(!m_fMyStoreOpenFlagsModified) {
            m_MyStore.dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
            m_MyStore.dwFlags |= (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
            m_keyProvInfo.dwFlags |= KeyLocationFromStoreLocation(m_MyStore.dwFlags);
        }
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwName) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_RequestStore.wszName == NULL) 
        return(ERROR_UNKNOWN_PROPERTY);
        
    if( (*szwName = CopyWideString(m_RequestStore.wszName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreNameWStr(
    /* [in] */ LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RequestStore.wszName != wszREQUEST)
            MyCoTaskMemFree(m_RequestStore.wszName);
        if( (m_RequestStore.wszName = CopyWideString(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreTypeWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwType = WideFromMB(m_RequestStore.szType)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreTypeWStr(
    /* [in] */ LPWSTR szwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RequestStore.szType != szSystemStore)
            MyCoTaskMemFree(m_RequestStore.szType);
        if( (m_RequestStore.szType = MBFromWide(szwType)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

 ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED); 
}

//
// Provider Stuff
//

HRESULT STDMETHODCALLTYPE CCEnroll::get_ContainerName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrContainer) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrContainer = SysAllocString(m_keyProvInfo.pwszContainerName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ContainerName(
    /* [in] */ BSTR bstrContainer) {
    return(put_ContainerNameWStr(bstrContainer));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstrProvider) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstrProvider = SysAllocString(m_keyProvInfo.pwszProvName)) == NULL )
         hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderName(
    /* [in] */ BSTR bstrProvider) {
    return(put_ProviderNameWStr(bstrProvider));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderType(
    /* [retval][out] */ LONG __RPC_FAR *pdwType) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwType = m_keyProvInfo.dwProvType;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderType(
    /* [in] */ LONG dwType) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else
        m_keyProvInfo.dwProvType = dwType;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_KeySpec(
    /* [retval][out] */ LONG __RPC_FAR *pdw) {
    EnterCriticalSection(&m_csXEnroll);
    *pdw = m_keyProvInfo.dwKeySpec;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_KeySpec(
    /* [in] */ LONG dwKeySpec) {
    HRESULT hr;
    EnterCriticalSection(&m_csXEnroll);

    if(m_hProv != NULL)
    {
        hr = E_ACCESSDENIED;
        goto NullProvError;
    }

    if (m_fSMIMESetByClient)
    {
        //SMIME is set by the client
        if (m_fEnableSMIMECapabilities && AT_SIGNATURE == dwKeySpec)
        {
            //try to set signature key spec also SMIME
            hr = XENROLL_E_KEYSPEC_SMIME_MISMATCH;
            goto MismatchError;
        }
    }
    else
    {
        //currently smime is not set by user
        //turn on SMIME for according to key spec
        m_fEnableSMIMECapabilities = (dwKeySpec == AT_KEYEXCHANGE);
    }
    m_keyProvInfo.dwKeySpec = dwKeySpec;
    m_fKeySpecSetByClient = TRUE;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(NullProvError)
TRACE_ERROR(MismatchError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ClientId(
    /* [retval][out] */ LONG __RPC_FAR *pdw) {
    EnterCriticalSection(&m_csXEnroll);
    *pdw = m_lClientId;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ClientId(
    /* [in] */ LONG dw) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    m_lClientId = dw;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_IncludeSubjectKeyID(
    /* [retval][out] */ BOOL __RPC_FAR *pfInclude) {
    EnterCriticalSection(&m_csXEnroll);
    *pfInclude = m_fIncludeSubjectKeyID;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_IncludeSubjectKeyID(
    /* [in] */ BOOL fInclude) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    m_fIncludeSubjectKeyID = fInclude;
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderFlags(
    /* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_keyProvInfo.dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe. 
// See VerifyProviderFlagsSafeForScripting(). 
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderFlags(
    /* [in] */ LONG dwFlags) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    
    // If we're marked as safe for scripting, see if the flags passed in are safe:
    if (0 != m_dwEnabledSafteyOptions && !VerifyProviderFlagsSafeForScripting(dwFlags))
	goto AccessDeniedError;

    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else
       m_keyProvInfo.dwFlags = dwFlags;

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ContainerNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwContainer) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwContainer = CopyWideString(m_keyProvInfo.pwszContainerName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ContainerNameWStr(
    /* [in] */ LPWSTR szwContainer) {
    HRESULT hr = S_OK;

    if(szwContainer == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
        
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else {
        if( m_keyProvInfo.pwszContainerName != wszEmpty)
            MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
        if( (m_keyProvInfo.pwszContainerName = CopyWideString(szwContainer)) == NULL )
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szwProvider) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szwProvider = CopyWideString(m_keyProvInfo.pwszProvName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderNameWStr(
    /* [in] */ LPWSTR szwProvider) {
    HRESULT hr = S_OK;
    
    if(szwProvider == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
        
    EnterCriticalSection(&m_csXEnroll);
        
    if(m_hProv != NULL)
        hr = E_ACCESSDENIED;
    else {
        if (0 != wcscmp(m_keyProvInfo.pwszProvName, szwProvider))
        {
            if( m_keyProvInfo.pwszProvName != wszEmpty )
                MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
            if( (m_keyProvInfo.pwszProvName = CopyWideString(szwProvider)) == NULL )
                hr = MY_HRESULT_FROM_WIN32(GetLastError());

            //one last thing, free/null cached prov handle
            if (NULL != m_hVerifyProv)
            {
                CryptReleaseContext(m_hVerifyProv, 0);
                m_hVerifyProv = NULL;
            }
            // csp is changed, reset key size cache
            m_dwXhgKeyLenMax = 0;
            m_dwXhgKeyLenMin = 0;
            m_dwXhgKeyLenDef = 0;
            m_dwXhgKeyLenInc = 0;
            m_dwSigKeyLenMax = 0;
            m_dwSigKeyLenMin = 0;
            m_dwSigKeyLenDef = 0;
            m_dwSigKeyLenInc = 0;
        }
    }
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

//
// Other Stuff
//

HRESULT STDMETHODCALLTYPE CCEnroll::get_UseExistingKeySet(
    /* [retval][out] */ BOOL __RPC_FAR *fUseExistingKeys) {

    EnterCriticalSection(&m_csXEnroll);
    *fUseExistingKeys = m_fUseExistingKey;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_UseExistingKeySet(
    /* [in] */ BOOL fUseExistingKeys) {

    EnterCriticalSection(&m_csXEnroll);
    m_fUseExistingKey = fUseExistingKeys;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_GenKeyFlags(
    /* [retval][out] */ LONG __RPC_FAR * pdwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    *pdwFlags = m_dwGenKeyFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_GenKeyFlags(
    /* [in] */ LONG dwFlags) {
    EnterCriticalSection(&m_csXEnroll);
    m_dwGenKeyFlags = dwFlags;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_DeleteRequestCert(
    /* [retval][out] */ BOOL __RPC_FAR *fBool) {
    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fDeleteRequestCert;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_DeleteRequestCert(
    /* [in] */ BOOL fBool) {
    EnterCriticalSection(&m_csXEnroll);
    m_fDeleteRequestCert = fBool;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_WriteCertToCSP(
    /* [retval][out] */ BOOL __RPC_FAR *fBool) {
    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fWriteCertToCSP;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_WriteCertToCSP(
    /* [in] */ BOOL fBool) {
    EnterCriticalSection(&m_csXEnroll);
    m_fWriteCertToCSP = fBool;
    m_fWriteCertToCSPModified = TRUE;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_WriteCertToUserDS(
    /* [retval][out] */ BOOL __RPC_FAR *fBool) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fWriteCertToUserDS;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_WriteCertToUserDS(
    /* [in] */ BOOL fBool) {
    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	return E_ACCESSDENIED;

    EnterCriticalSection(&m_csXEnroll);
    m_fWriteCertToUserDS = fBool;
    m_fWriteCertToUserDSModified = TRUE;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_EnableT61DNEncoding(
    /* [retval][out] */ BOOL __RPC_FAR *fBool) {
    EnterCriticalSection(&m_csXEnroll);
    *fBool = (m_dwT61DNEncoding == CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG);
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_EnableT61DNEncoding(
    /* [in] */ BOOL fBool) {

    EnterCriticalSection(&m_csXEnroll);
    if(fBool)
        m_dwT61DNEncoding = CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
    else
        m_dwT61DNEncoding = 0;
        
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_SPCFileName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstr) {

    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstr = SysAllocString(m_wszSPCFileName)) == NULL )
        hr = E_OUTOFMEMORY;
        
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_SPCFileName(
    /* [in] */ BSTR bstr) {
    return(put_SPCFileNameWStr(bstr));
}


HRESULT STDMETHODCALLTYPE CCEnroll::get_PVKFileName(
    /* [retval][out] */ BSTR __RPC_FAR *pbstr) {

    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);
    if( (*pbstr = SysAllocString(m_wszPVKFileName)) == NULL )
        hr = E_OUTOFMEMORY;
    LeaveCriticalSection(&m_csXEnroll);
    
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_PVKFileName(
    /* [in] */ BSTR bstr) {
    return(put_PVKFileNameWStr(bstr));
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgorithm(
    /* [retval][out] */ BSTR __RPC_FAR *pbstr) {

    LPWSTR  wszAlg  = NULL;
    HRESULT hr      = S_OK;

    assert(pbstr != NULL);
    *pbstr          = NULL;

    if( (hr = get_HashAlgorithmWStr(&wszAlg)) == S_OK ) {

        if( (*pbstr = SysAllocString(wszAlg)) == NULL )
            hr = E_OUTOFMEMORY;
    }

    if(wszAlg != NULL)
        MyCoTaskMemFree(wszAlg);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgorithm(
    /* [in] */ BSTR bstr) {
    return(put_HashAlgorithmWStr(bstr));
}

HRESULT STDMETHODCALLTYPE CCEnroll::enumContainers(
            /* [in] */ LONG                     dwIndex,
            /* [out][retval] */ BSTR __RPC_FAR *pbstr) {

    LPWSTR      pwsz        = NULL;
    HRESULT     hr;

    assert(pbstr != NULL);

    if((hr = enumContainersWStr(dwIndex, &pwsz)) != S_OK)
        goto EnumContainerError;

    if( (*pbstr = SysAllocString(pwsz)) == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto ErrorSysAllocString;
    }

    hr = S_OK;
ErrorReturn:
    if(pwsz != NULL)
        MyCoTaskMemFree(pwsz);
    return(hr);

TRACE_ERROR(EnumContainerError);
TRACE_ERROR(ErrorSysAllocString);
}


HRESULT STDMETHODCALLTYPE CCEnroll::enumProviders(
            /* [in] */ LONG  dwIndex,
            /* [in] */ LONG  dwFlags,
            /* [out][retval] */ BSTR __RPC_FAR *pbstrProvName) {
    HRESULT hr;
    LPWSTR pwszProvName  = NULL;

    assert(pbstrProvName != NULL);
    *pbstrProvName = NULL;

    if( (hr = enumProvidersWStr(dwIndex, dwFlags, &pwszProvName)) != S_OK)
        goto EnumProvidersError;

    if( (*pbstrProvName = SysAllocString(pwszProvName)) == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto ErrorSysAllocString;
    }

    hr = S_OK;
ErrorReturn:

    if(pwszProvName != NULL)
        MyCoTaskMemFree(pwszProvName);

    return(hr);

TRACE_ERROR(EnumProvidersError);
TRACE_ERROR(ErrorSysAllocString);
}

HRESULT STDMETHODCALLTYPE CCEnroll::createFilePKCS10WStr(
    /* [in] */ LPCWSTR DNName,
    /* [in] */ LPCWSTR Usage,
    /* [in] */ LPCWSTR wszPKCS10FileName) {

    HRESULT     hr;                     
    BSTR        bstrPKCS10  = NULL;

    // get the pkcs 10
    if( (hr = createPKCS10WStrBStr(
            DNName,
            Usage,
            &bstrPKCS10)) != S_OK)
    {
        goto ErrorCreatePKCS10;
    }

    // save it to file
    hr = BStringToFile(bstrPKCS10, wszPKCS10FileName);
    if (S_OK != hr)
    {
        goto ErrorBStringToFile;
    }

    hr = S_OK;
ErrorReturn:
    if(bstrPKCS10 != NULL)
        SysFreeString(bstrPKCS10);

    return(hr);


TRACE_ERROR(ErrorBStringToFile);
TRACE_ERROR(ErrorCreatePKCS10);
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptFilePKCS7WStr(
    /* [in] */ LPCWSTR wszPKCS7FileName)
{
    HRESULT     hr;
    CRYPT_DATA_BLOB  blob;

    ZeroMemory(&blob, sizeof(blob));

    hr = xeStringToBinaryFromFile(
                wszPKCS7FileName,
                &blob.pbData,
                &blob.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    // accept the blob
    hr = acceptPKCS7Blob(&blob);

ErrorReturn:
    if (NULL != blob.pbData)
    {
        MyCoTaskMemFree(blob.pbData);
    }
    return(hr);

TRACE_ERROR(xeStringToBinaryFromFileError)
}

BOOL GetAlgAndBitLen(
    HCRYPTPROV hProv,
    ALG_ID *    pAlg,
    DWORD  *    pdwBitLen,
    DWORD       dwFlags)
{
    static BOOL fNew = TRUE;
    PROV_ENUMALGS_EX    enumAlgsEx;
    PROV_ENUMALGS       enumAlgs;
    DWORD               cb = 0;

    *pAlg = 0;
    *pdwBitLen = 0;

    if(fNew) {

        cb = sizeof(enumAlgsEx);
        if(CryptGetProvParam(
            hProv,
            PP_ENUMALGS_EX,
            (BYTE *) &enumAlgsEx,
            &cb,
            dwFlags)) {

            *pAlg       = enumAlgsEx.aiAlgid;
            *pdwBitLen  = enumAlgsEx.dwMaxLen;

            return(TRUE);        

        } else if(dwFlags != 0)
            fNew = FALSE;
        else
            return(FALSE);
    }

    // otherwise do the old stuff
    cb = sizeof(PROV_ENUMALGS);
    if(CryptGetProvParam(
        hProv,
            PP_ENUMALGS,
        (BYTE *) &enumAlgs,
        &cb,
        dwFlags) ) {

        *pAlg       = enumAlgs.aiAlgid;
        *pdwBitLen  = enumAlgs.dwBitLen;

        return(TRUE);

    }

    return(FALSE);
}    

HRESULT
CreateSMimeExtension(
    IN  HCRYPTPROV   hProv, 
    OUT BYTE       **ppbSMime,
    OUT DWORD       *pcbSMime)
{
#define                     CINCSMIMECAP    20
    HRESULT  hr;

    DWORD                      dwBitLen;
    DWORD                      i;
    DWORD                      cbE;
    BYTE                      *pbE = NULL;
    DWORD                      dwFlags;
    PCCRYPT_OID_INFO           pOidInfo = NULL;
    CRYPT_SMIME_CAPABILITIES   smimeCaps;
    DWORD                      crgsmimeCap = 0;
    ALG_ID                     AlgID;
    BYTE                      *pb = NULL;
    DWORD                      cb = 0;

    memset(&smimeCaps, 0, sizeof(CRYPT_SMIME_CAPABILITIES));

    smimeCaps.rgCapability = (PCRYPT_SMIME_CAPABILITY) LocalAlloc(LPTR, CINCSMIMECAP * sizeof(CRYPT_SMIME_CAPABILITY));
    if (NULL == smimeCaps.rgCapability)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    crgsmimeCap = CINCSMIMECAP;

    dwFlags = CRYPT_FIRST; //first item
    while (GetAlgAndBitLen(hProv, &AlgID, &dwBitLen, dwFlags))
    {
        pbE = NULL;
        cbE = 0;
        dwFlags = 0; //next item

        if(ALG_CLASS_DATA_ENCRYPT == GET_ALG_CLASS(AlgID))
        {
            if(AlgID == CALG_RC2  || AlgID == CALG_RC4)
            {
                // encode the usage
                while (TRUE)
                {
                    if(!CryptEncodeObject(
                            CRYPT_ASN_ENCODING,
                            X509_INTEGER,
                            &dwBitLen,
                            pbE,           // pbEncoded
                            &cbE))
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto CryptEncodeObjectError;
                    }
                    if (NULL != pbE)
                    {
                        break;
                    }
                    pbE = (BYTE *)LocalAlloc(LPTR, cbE);
                    if (NULL == pbE)
                    {
                        hr = E_OUTOFMEMORY;
                        goto OutOfMemoryError;
                    }
                }
            }
        } else {
            continue;
        }
        // convert to an oid,
        pOidInfo = xeCryptFindOIDInfo(
                        CRYPT_OID_INFO_ALGID_KEY,
                        (void *) &AlgID,
                        CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
        if(NULL == pOidInfo)
        {
            // don't crash on an error, just say we don't known it.
	    if (NULL != pbE) { 
		LocalFree(pbE); 
	    }
	    pbE = NULL; 
            continue;
        }
      
        // make sure we have enough room
        if(smimeCaps.cCapability >= crgsmimeCap)
        {
	    PCRYPT_SMIME_CAPABILITY pSmimeCapsTmp; 

            //increment the size
            crgsmimeCap += CINCSMIMECAP;
	    pSmimeCapsTmp = (PCRYPT_SMIME_CAPABILITY)LocalReAlloc(
                                smimeCaps.rgCapability,
                                crgsmimeCap * sizeof(CRYPT_SMIME_CAPABILITY),
                                LMEM_MOVEABLE | LMEM_ZEROINIT);
            if(NULL == pSmimeCapsTmp)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
	    smimeCaps.rgCapability = pSmimeCapsTmp; 
        }

        smimeCaps.rgCapability[smimeCaps.cCapability].pszObjId = (char *) pOidInfo->pszOID;
        smimeCaps.rgCapability[smimeCaps.cCapability].Parameters.pbData = pbE;
        smimeCaps.rgCapability[smimeCaps.cCapability].Parameters.cbData = cbE;
        smimeCaps.cCapability++;
	pbE = NULL;  // We'll free pbE through the struct we just assigned.  NULL out so we don't double-free. 
    }

    // encode the capabilities
    while (TRUE)
    {
        if (!CryptEncodeObject(
                        CRYPT_ASN_ENCODING,
                        PKCS_SMIME_CAPABILITIES,
                        &smimeCaps,
                        pb,
                        &cb)) 
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptEncodeObjectError;
        }
        if (NULL != pb)
        {
            break;
        }
        pb = (BYTE *)LocalAlloc(LPTR, cb);
    }
    *ppbSMime = pb;
    *pcbSMime = cb;
    pb = NULL;

    hr = S_OK;

ErrorReturn:
    if(NULL != smimeCaps.rgCapability)
    {
        for (i = 0; i < smimeCaps.cCapability; ++i)
        {
            if (NULL != smimeCaps.rgCapability[i].Parameters.pbData)
            {
                LocalFree(smimeCaps.rgCapability[i].Parameters.pbData);
            }
        }
        LocalFree(smimeCaps.rgCapability);
    }
    if(NULL != pb)
    {
        LocalFree(pb);
    }
    if (NULL != pbE) 
    { 
	LocalFree(pbE); 
    }
    return hr;

TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(OutOfMemoryError)
}


#if DBG
void DebugGetContainerSD(HCRYPTPROV hProv)
{
    PSECURITY_DESCRIPTOR  pSD = NULL;
    DWORD                 cbSD;

    while (TRUE)
    {
        if (!CryptGetProvParam(
                hProv,
                PP_KEYSET_SEC_DESCR,
                (BYTE*)pSD,
                &cbSD,
                DACL_SECURITY_INFORMATION))
        {
            break;
        }
        if (NULL != pSD)
        {
            break;
        }
        pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD);
        if (NULL == pSD)
        {
            break;
        }
    }
    if (NULL != pSD)
    {
	LocalFree(pSD);
    }
}
#endif //DBG

//get the current user sids
HRESULT
GetCurrentUserInfo(
    OUT PTOKEN_USER *ppUserInfo,
    OUT BOOL        *pfAdmin)
{
    HRESULT  hr;
    PTOKEN_USER   pUserInfo = NULL;
    DWORD         dwSize = 0;
    HANDLE        hToken = NULL;
    HANDLE        hDupToken = NULL;
    PSID          psidAdministrators = NULL;
    SID_IDENTIFIER_AUTHORITY    siaNtAuthority = SECURITY_NT_AUTHORITY;
    HANDLE hThread;
    HANDLE hProcess;

    //init
    *pfAdmin = FALSE;

    if (!AllocateAndInitializeSid(
                            &siaNtAuthority,
                            2,
                            SECURITY_BUILTIN_DOMAIN_RID,
                            DOMAIN_ALIAS_RID_ADMINS,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            &psidAdministrators))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto AllocateAndInitializeSidError;
    }

    hThread = GetCurrentThread();
    if (NULL == hThread)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetCurrentThreadError;
    }

    // Get the access token for current thread
    if (!OpenThreadToken(
            hThread, 
            TOKEN_QUERY | TOKEN_DUPLICATE, 
            FALSE,
            &hToken))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        if(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) != hr)
        {
            goto OpenThreadTokenError;
        }
        //get process token instead
        hProcess = GetCurrentProcess();
        if (NULL == hProcess)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto GetCurrentProcessError;
        }

        hToken = NULL;
        if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &hToken))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto OpenProcessTokenError;
        }
    }

    // CheckTokenMembership must operate on impersonation token, so make one
    if (!DuplicateToken(hToken, SecurityIdentification, &hDupToken))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto DuplicateTokenError;
    }

    if (!MyCheckTokenMembership(hDupToken, psidAdministrators, pfAdmin))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CheckTokenMembershipError;
    }

    //get current user sid
    while (TRUE)
    {
        if (!GetTokenInformation(
                hToken,
                TokenUser,
                pUserInfo,
                dwSize,
                &dwSize))
        {
            if (NULL != pUserInfo ||
                ERROR_INSUFFICIENT_BUFFER != GetLastError())
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto GetTokenInformationError;
            }
        }

        if (NULL != pUserInfo)
        {
            //done
            break;
        }
        pUserInfo = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, dwSize);
        if (NULL == pUserInfo)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    if (NULL != ppUserInfo)
    {
        *ppUserInfo = pUserInfo;
        pUserInfo = NULL;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pUserInfo)
    {
        LocalFree(pUserInfo);
    }
    if (NULL != hToken)
    {
        CloseHandle(hToken);
    }
    if (NULL != hDupToken)
    {
        CloseHandle(hDupToken);
    }
    if (NULL != psidAdministrators)
    {
        FreeSid(psidAdministrators);
    }

    return hr;

TRACE_ERROR(LocalAllocError)
TRACE_ERROR(GetTokenInformationError)
TRACE_ERROR(OpenProcessTokenError)
TRACE_ERROR(GetCurrentProcessError)
TRACE_ERROR(CheckTokenMembershipError)
TRACE_ERROR(DuplicateTokenError)
TRACE_ERROR(OpenThreadTokenError)
TRACE_ERROR(GetCurrentThreadError)
TRACE_ERROR(AllocateAndInitializeSidError)
}


HRESULT
SetKeyContainerSecurityForNULLDacl(
    HCRYPTPROV   hProv,
    DWORD        dwFlags, 
    PTOKEN_USER  pUserInfo)
{
    DWORD                 ccNeeded; 
    HRESULT               hr;
    LPWSTR                wszSD       = NULL; 
    LPWSTR                wszUserSid  = NULL;
    PSECURITY_DESCRIPTOR  pSD         = NULL;

    UNREFERENCED_PARAMETER(dwFlags);

    // We want the security descriptor for the new keyset to look like so:
    //
    // ACES: 
    // NT AUTHORITY\SYSTEM:F
    // BUILTIN\Administrators:F
    // 
    // 
#define SDDL_NEW_KEYSET_START L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;"
#define SDDL_NEW_KEYSET_END   L")"

    if (!ConvertSidToStringSidW(pUserInfo->User.Sid, &wszUserSid)) { 
	goto ConvertSidToStringSidError; 
    }
	
    ccNeeded = (DWORD)(wcslen(SDDL_NEW_KEYSET_START) + wcslen(wszUserSid) + wcslen(SDDL_NEW_KEYSET_END) + 1); 
    wszSD = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*ccNeeded);
    if (NULL == wszSD) { 
	goto MemoryError;
    }
    
    wcscpy(wszSD, SDDL_NEW_KEYSET_START); 
    wcscat(wszSD, wszUserSid);
    wcscat(wszSD, SDDL_NEW_KEYSET_END); 
    

    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(wszSD, SDDL_REVISION_1, &pSD, NULL)) { 
	goto ConvertStringSecurityDescriptorToSecurityDescriptorWError; 
    }

    if (!IsValidSecurityDescriptor(pSD)) { 
	goto IsValidSecurityDescriptorError;
    }

#if DBG
    DebugGetContainerSD(hProv); //just for ntsd debug
#endif

    if (!CryptSetProvParam(hProv, PP_KEYSET_SEC_DESCR, (BYTE*)pSD, DACL_SECURITY_INFORMATION)) { 
        goto CryptSetProvParamError;
    }

#if DBG
    DebugGetContainerSD(hProv); //just for ntsd debug
#endif

    hr = S_OK;
ErrorReturn:
    if (NULL != wszSD)
    {
	LocalFree(wszSD);
    }
    if (NULL != wszUserSid)
    {
	LocalFree(wszUserSid); 
    }
    if (NULL != pSD)
    {
        LocalFree(pSD);
    }
    return hr;


SET_HRESULT(ConvertSidToStringSidError,                                 HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(ConvertStringSecurityDescriptorToSecurityDescriptorWError,  HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(CryptSetProvParamError,                                     HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(IsValidSecurityDescriptorError,                             HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR)); 
SET_HRESULT(MemoryError,                                                E_OUTOFMEMORY); 
}


// This function ACLs new keysets (as 3rd party CSPs and downlevel CSPs may not do this correctly.  
// Should *not* call this function on existing keysets (as ACLs may have been set differently by admins)
// 
HRESULT
SetKeyContainerSecurity(
    HCRYPTPROV hProv,
    DWORD      dwFlags)
{
    HRESULT               hr;
    PSECURITY_DESCRIPTOR  pNewSD = NULL;
    PSECURITY_DESCRIPTOR  pSD = NULL;

    DWORD                 cbSD;
    ACL_SIZE_INFORMATION  AclInfo;

    PTOKEN_USER           pUserInfo = NULL;
    PACL                  pNewAcl = NULL;
    LPVOID pAce;
    DWORD  dwIndex;
    BYTE   AceType;
    PACL   pAcl;
    BOOL   fDacl = TRUE;
    BOOL   fDef = FALSE;
    BOOL   fAdmin;
    BOOL   fKeepSystemSid;
    BOOL   fMachineKeySet = (0x0 != (dwFlags & CRYPT_MACHINE_KEYSET)) ?
                            TRUE : FALSE;

    PSID                      pSidSystem = NULL;
    PSID                      pSidAdministrators = NULL; 
    SID_IDENTIFIER_AUTHORITY  siaNtAuthority = SECURITY_NT_AUTHORITY;

    //get the current user info
    hr = GetCurrentUserInfo(&pUserInfo, &fAdmin);
    if (S_OK != hr)
    {
        goto GetCurrentUserInfoError;
    }

    //get the current sd from key container
    while (TRUE)
    {
        if (!CryptGetProvParam(
                hProv,
                PP_KEYSET_SEC_DESCR,
                (BYTE*)pSD,
                &cbSD,
                DACL_SECURITY_INFORMATION))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptGetProvParamError;
        }
        if (NULL != pSD)
        {
            break;
        }
        pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD);
        if (NULL == pSD)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    //get acl from sd
    if (!GetSecurityDescriptorDacl(
            pSD,
            &fDacl,
            &pAcl,
            &fDef))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetSecurityDescriptorDaclError;
    }
    if (!fDacl)
    {
        //if no dacl, quit
        hr = MY_HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
        goto GetSecurityDescriptorDaclError;
    }
    if (NULL == pAcl)
    {
#if 0
        //this means allow everyone access the key which is unexpected,
	hr = SetKeyContainerSecurityForNULLDacl(hProv, dwFlags, pUserInfo); 
#endif
	// BUGBUG: The NULL DACL stuff doesn't work on downlevels because it requires SDDL.  We don't
	// have time to fix for windows update, but we should revisit this later
	hr = S_OK; 
	goto done; 
    }

    //get acl info
    if (!GetAclInformation(
            pAcl,
            &AclInfo,
            sizeof(AclInfo),
            AclSizeInformation))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto GetAclInformationError;
    }

    //allocate enough for new dacl since we just remove aces
    pNewAcl = (PACL)LocalAlloc(LMEM_ZEROINIT, AclInfo.AclBytesInUse);
    if (NULL == pNewAcl)
    {
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    }
    if (!InitializeAcl(pNewAcl, AclInfo.AclBytesInUse, ACL_REVISION))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto InitializeAclError;
    }

    fKeepSystemSid = fAdmin && fMachineKeySet;
    if (fKeepSystemSid)
    {
        //get system sid to later use
        if (!AllocateAndInitializeSid(
                            &siaNtAuthority,
                            1,
                            SECURITY_LOCAL_SYSTEM_RID,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            &pSidSystem))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto AllocateAndInitializeSidError;
        }
    }

    if (!AllocateAndInitializeSid(&siaNtAuthority,
				  2,
				  SECURITY_BUILTIN_DOMAIN_RID,
				  DOMAIN_ALIAS_RID_ADMINS,
				  0, 0, 0, 0, 0, 0,
				  &pSidAdministrators
				  ))
    {
	hr = MY_HRESULT_FROM_WIN32(GetLastError());
	goto AllocateAndInitializeSidError;
    }
	
    //go through each ace, get only current user aces
    for (dwIndex = 0; dwIndex < AclInfo.AceCount; ++dwIndex)
    {
        if (!GetAce(pAcl, dwIndex, &pAce))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto GetAceError;
        }
        AceType = ((ACCESS_ALLOWED_ACE*)pAce)->Header.AceType;
        if (ACCESS_ALLOWED_ACE_TYPE == AceType)
        {
            if (EqualSid(pUserInfo->User.Sid, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)) 
		|| (fKeepSystemSid && EqualSid(pSidSystem, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
		|| EqualSid(pSidAdministrators, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
            {
                //add current user ace or system ace into new acl
                if (!AddAccessAllowedAce(
                        pNewAcl,
                        ACL_REVISION,
                        ((PACCESS_ALLOWED_ACE)pAce)->Mask,
                        (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto AddAccessAllowedAceError;
                }
            }
        }
        else if (ACCESS_DENIED_ACE_TYPE == AceType)
        {
            //add all deny ace into new acl
            if (!AddAccessDeniedAce(
                    pNewAcl,
                    ACL_REVISION,
                    ((PACCESS_ALLOWED_ACE)pAce)->Mask,
                    (PSID)&(((PACCESS_DENIED_ACE)pAce)->SidStart)))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto AddAccessDeniedAceError;
            }
        }
    }

    // initialize a security descriptor.  
    pNewSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, 
                         SECURITY_DESCRIPTOR_MIN_LENGTH); 
    if (pNewSD == NULL)
    { 
        hr = E_OUTOFMEMORY;
        goto LocalAllocError;
    } 
 
    if (!InitializeSecurityDescriptor(pNewSD, SECURITY_DESCRIPTOR_REVISION))
    {  
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto InitializeSecurityDescriptorError;
    } 
 
    // add the ACL to the security descriptor. 
    if (!SetSecurityDescriptorDacl(
            pNewSD, 
            TRUE,     // fDaclPresent flag   
            pNewAcl, 
            FALSE))   // not a default DACL 
    {  
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto SetSecurityDescriptorDaclError;
    } 

    //ok, set sd to be protected
    if (!MySetSecurityDescriptorControl(
            pNewSD,
            SE_DACL_PROTECTED,
            SE_DACL_PROTECTED))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto SetSecurityDescriptorControlError;
    }

    if (!IsValidSecurityDescriptor(pNewSD))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
	goto IsValidSecurityDescriptorError;
    }

#if DBG
    DebugGetContainerSD(hProv); //just for ntsd debug
#endif

    //now we just set it
    if (!CryptSetProvParam(
            hProv,
            PP_KEYSET_SEC_DESCR,
            (BYTE*)pNewSD,
            DACL_SECURITY_INFORMATION))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CryptSetProvParamError;
    }

#if DBG
    DebugGetContainerSD(hProv); //just for ntsd debug
#endif

done:
    hr = S_OK;
ErrorReturn:
    if (NULL != pSD)
    {
        LocalFree(pSD);
    }
    if (NULL != pUserInfo) 
    {
        LocalFree(pUserInfo);
    }
    if (NULL != pNewAcl) 
    {
        LocalFree(pNewAcl);
    }
    if (NULL != pNewSD) 
    {
        LocalFree(pNewSD);
    }
    if (NULL != pSidSystem)
    {
        FreeSid(pSidSystem);
    }
    if (NULL != pSidAdministrators)
    {
	FreeSid(pSidAdministrators);
    }
    return hr;

TRACE_ERROR(CryptSetProvParamError)
TRACE_ERROR(SetSecurityDescriptorDaclError)
TRACE_ERROR(InitializeSecurityDescriptorError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(AddAccessAllowedAceError)
TRACE_ERROR(AddAccessDeniedAceError)
TRACE_ERROR(GetAceError)
TRACE_ERROR(GetCurrentUserInfoError)
TRACE_ERROR(InitializeAclError)
TRACE_ERROR(GetAclInformationError)
TRACE_ERROR(GetSecurityDescriptorDaclError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptGetProvParamError)
TRACE_ERROR(SetSecurityDescriptorControlError)
TRACE_ERROR(IsValidSecurityDescriptorError)
TRACE_ERROR(AllocateAndInitializeSidError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::createPKCS10WStr(
    /* [in] */ LPCWSTR DNName,
    /* [in] */ LPCWSTR wszPurpose,
    /* [out] */ PCRYPT_DATA_BLOB pPkcs10Blob)
{
    #define EndExt      5

    #define EndAttr     6

    HCRYPTPROV                  hProv           = NULL;
    HCRYPTKEY                   hKey            = NULL;

    CERT_REQUEST_INFO           reqInfo;

    CERT_EXTENSIONS             Extensions;
    PCERT_EXTENSION             pExtCur         = NULL;
    PCERT_EXTENSION             rgExtension     = NULL;
    CRYPT_ATTRIBUTE             rgAttribute[EndAttr];
    CRYPT_ATTR_BLOB             blobExt;
    CRYPT_ATTR_BLOB             blobCSPAttr;
    CRYPT_CSP_PROVIDER          CSPProvider;
    CRYPT_ATTR_BLOB             blobOSVAttr;
    CRYPT_ATTR_BLOB             blobSMIMEPKCS7;
    CERT_NAME_VALUE             cnvOSVer;
    OSVERSIONINFO               osvInfo;

    DWORD                       iExt            = 0;
    CRYPT_BIT_BLOB              bbKeyUsage;
    BYTE                        bKeyUsage;

    CERT_SIGNED_CONTENT_INFO    SignatureInfo;

    HRESULT                     hr              = S_OK;
    DWORD                       errBefore       = GetLastError();

    PCCERT_CONTEXT              pCertContext    = NULL;
    HCERTSTORE                  hStore          = NULL;
    DWORD                       ssFlags         = 0;

    HANDLE                      hFile           = NULL;
    CRYPT_DATA_BLOB             blobData;

    DWORD                       cb              = 0;
    char *                      pszPurpose       = NULL;
    char *                      szStart         = NULL;
    char *                      szEnd           = NULL;
    char                        szVersion[45]   = {0};

    BOOL                        fAddCodeSign    = FALSE;
    DWORD                       cPassedEKU      = 0;
    DWORD                       i               = 0;
    BOOL                        fRet;
    BYTE                       *pbSMime = NULL;
    BYTE                       *pbKU = NULL;
    BYTE                       *pbEKU = NULL;
    PPROP_STACK                 pProp;
    CRYPT_ATTR_BLOB             blobClientId;
    DWORD                       cPublicKeyInfo = 0;
    BYTE                       *pbSubjectKeyHashExtension = NULL;
    DWORD                       cbSubjectKeyHashExtension = 0;
    DWORD                       dwErr;

    LPWSTR                      pwszNotSafeRequesting     = NULL; 
    LPWSTR                      pwszTitle                 = NULL; 

    //
    // Declaration of extensions we need.  The extensions with matching OIDs will be added
    // to the temporary cert context created by this method. 
    // 
    LPSTR rgszExtensionOIDs[] = { 
        szOID_ENROLL_CERTTYPE_EXTENSION,
        szOID_CERTIFICATE_TEMPLATE
    }; 

    // An array of the extensions we need to add to the certificate 
    CERT_EXTENSION  rgNeededExtensions[sizeof(rgszExtensionOIDs) / sizeof(LPSTR)]; 

    // Need to put the array in a CERT_EXTENSIONS struct. 
    CERT_EXTENSIONS ceExtensions; 
    ceExtensions.rgExtension = &rgNeededExtensions[0]; 
    ceExtensions.cExtension  = 0; 

    CRYPT_KEY_PROV_INFO         keyProvInfoT;
        CERT_ENHKEY_USAGE                   enhKeyUsage;

        CRYPT_DATA_BLOB             blobPKCS7;
        CRYPT_DATA_BLOB             blobRenewAttr;
        RequestFlags                requestFlags;
        CRYPT_DATA_BLOB             requestInfoBlob;
        CRYPT_DATA_BLOB             blobRenewalCert;

        ALG_ID                      rgAlg[2];
        PCCRYPT_OID_INFO            pOidInfo        = NULL;

    EnterCriticalSection(&m_csXEnroll);

    // for the life of our procedure.
    SetLastError(ERROR_SUCCESS);

    assert(pPkcs10Blob != NULL);

    // clean out the PKCS 10
    memset(&Extensions, 0, sizeof(CERT_EXTENSIONS));
    memset(&rgAttribute, 0, sizeof(rgAttribute));
    memset(&reqInfo, 0, sizeof(CERT_REQUEST_INFO));
    memset(&SignatureInfo, 0, sizeof(SignatureInfo));
    memset(&blobData, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&enhKeyUsage, 0, sizeof(CERT_ENHKEY_USAGE ));
    memset(pPkcs10Blob, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&blobRenewAttr, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&requestFlags, 0, sizeof(RequestFlags));
    memset(&requestInfoBlob, 0, sizeof(CRYPT_DATA_BLOB));
    memset(&CSPProvider, 0, sizeof(CRYPT_CSP_PROVIDER));
    memset(&cnvOSVer, 0, sizeof(CERT_NAME_VALUE));
    memset(&osvInfo, 0, sizeof(OSVERSIONINFO));
    memset(&blobSMIMEPKCS7, 0, sizeof(CRYPT_ATTR_BLOB));
    memset(&rgNeededExtensions[0], 0, sizeof(rgNeededExtensions)); 
    ZeroMemory(&blobExt, sizeof(blobExt));
    memset(&blobCSPAttr, 0, sizeof(CRYPT_ATTR_BLOB));
    memset(&blobOSVAttr, 0, sizeof(CRYPT_ATTR_BLOB));
    memset(&blobClientId, 0, sizeof(CRYPT_ATTR_BLOB));

    reqInfo.dwVersion = CERT_REQUEST_V1;

    // Creating a request is not safe for scripting:  pop up a warning dialog if called from script
    if (0 != m_dwEnabledSafteyOptions) { 
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
	if (S_OK != hr) { 
	    SetLastError(hr);
	    goto xeLoadRCStringError;
	}
	
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_REQUESTING_CERT, &pwszNotSafeRequesting);
	if (S_OK != hr) { 
	    SetLastError(hr);
	    goto xeLoadRCStringError;
	}
	
	if (IDYES != MessageBoxU(NULL, pwszNotSafeRequesting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) { 
	    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); 
	    SetLastError(hr); 
	    goto CancelledError; 
	}
    }

    if(!m_fUseExistingKey)
    {
        // attempt to get a new keyset
        if((hProv = GetProv(CRYPT_NEWKEYSET)) == NULL) {

            // in the hardware token case, there may only be a finite number of containers
            // if you run out, then use the default container. The Default container can
            // be specified by either a NULL or empty container name.
            // this is behavior requested by the smart cards, in particular smart card enrollment.
            if( m_fReuseHardwareKeyIfUnableToGenNew &&
                GetLastError() == NTE_TOKEN_KEYSET_STORAGE_FULL) {

                    // set it to the default container name
                    if( m_keyProvInfo.pwszContainerName != wszEmpty )
                        MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
                    m_keyProvInfo.pwszContainerName = wszEmpty;

                    // say we want to use an exiting key.
                    m_fUseExistingKey = TRUE;
            }
            else
                goto ErrorCryptAcquireContext;
        }
    }

    // if we are to use an existing key
    if(m_fUseExistingKey) {

        if((hProv = GetProv(0)) == NULL)
            goto ErrorCryptAcquireContext;
    }

    // we have the keyset, now make sure we have the key gen'ed
    if(!CryptGetUserKey(
                hProv,
                m_keyProvInfo.dwKeySpec,
                &hKey))
    {
        //in case of smartcard csp, above call could be failed from
        //PIN Cancel button, don't go next to try genkey
        //also notice different csp could return different cancel errors
        dwErr = GetLastError();
        if (SCARD_W_CANCELLED_BY_USER == dwErr ||
            ERROR_CANCELLED == dwErr ||
            ERROR_ACCESS_DENIED == dwErr)
        {
            goto CryptGetUserKeyCancelError;
        }
            
        // doesn't exist so gen it
        assert(hKey == NULL);

	// if the cached key is non-NULL, free it to prevent memory leaks
	if (NULL != m_hCachedKey)
	{
	    CryptDestroyKey(m_hCachedKey); 
	    m_hCachedKey = NULL; 
	}

        if(!CryptGenKey(    hProv,
                            m_keyProvInfo.dwKeySpec,
                            m_dwGenKeyFlags | CRYPT_ARCHIVABLE,
                            &m_hCachedKey) )
        {
            //could be cancelled by user? don't make next try
            dwErr = GetLastError();
            if (SCARD_W_CANCELLED_BY_USER == dwErr ||
                ERROR_CANCELLED == dwErr ||
                ERROR_ACCESS_DENIED == dwErr)
            {
		goto ErrorCryptGenKey;
            }

            //this error may be caused by not supporting CRYPT_ARCHIVABLE
            //we should check against error NTE_BAD_FLAGS but I doubt all
            //csps return consistent error code
            //let's try one more time without archivable flag
            assert(NULL == m_hCachedKey);
            DWORD dwGenKeyFlags = m_dwGenKeyFlags;
            if (NULL != m_PrivateKeyArchiveCertificate && m_fNewRequestMethod && (0 == (dwGenKeyFlags & CRYPT_EXPORTABLE)))
            {
		// We want ARCHIVABLE but not EXPORTABLE, so we needed the CRYPT_ARCHIVAL bit.  Give up.  
		goto ErrorCryptGenKey;
            }

            if (!CryptGenKey(
                        hProv,
                        m_keyProvInfo.dwKeySpec,
                        dwGenKeyFlags,
                        &hKey))
            {
                goto ErrorCryptGenKey;
            }
	}

	//try to set key container ACL with owner ACE only (NOTE: only do this for creation!)
	hr = SetKeyContainerSecurity(hProv, m_keyProvInfo.dwFlags);
#if DBG
	if (S_OK != hr)
	{
	    goto SetKeyContainerSecurityError;
	}
#endif //DBG
	hr = S_OK; //free build, no error checking here, if fails, live with it
    }


    if (NULL != hKey)
    {
        // don't need the hKey on existing key, so get rid of it
        CryptDestroyKey(hKey);
    }
    if ((NULL == m_PrivateKeyArchiveCertificate || !m_fNewRequestMethod) &&
        NULL != m_hCachedKey)
    {
        //we don't need cache it, destroy it as soon as key is gen(ed)
        CryptDestroyKey(m_hCachedKey);
        m_hCachedKey = NULL;
    }

    // now get the public key out into m_pPublicKeyInfo
    // m_pPublicKeyInfo is internal use for cache
    if (NULL != m_pPublicKeyInfo)
    {
        LocalFree(m_pPublicKeyInfo);
        m_pPublicKeyInfo = NULL;
    }
    while (TRUE)
    {
        if(!CryptExportPublicKeyInfo(hProv,
                            m_keyProvInfo.dwKeySpec,
                            X509_ASN_ENCODING,
                            m_pPublicKeyInfo,
                            &cPublicKeyInfo))
        {
            goto ErrorCryptExportPublicKeyInfo;
        }
        if (NULL != m_pPublicKeyInfo)
        {
            break;
        }
        m_pPublicKeyInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(
                                LMEM_FIXED, cPublicKeyInfo);
        if (NULL == m_pPublicKeyInfo)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    reqInfo.SubjectPublicKeyInfo = *m_pPublicKeyInfo;

    // get the Subject DN only if one is specified
    reqInfo.Subject.pbData = NULL;
    while (TRUE)
    {
        if( !MyCertStrToNameW(
                CRYPT_ASN_ENCODING,
                DNName,
                0 | m_dwT61DNEncoding,
                NULL,
                reqInfo.Subject.pbData,
                &reqInfo.Subject.cbData,
                NULL))
        {
            if (CRYPT_E_INVALID_X500_STRING == GetLastError() &&
                L'\0' == DNName[0])
            {
                //this is likely on W95, W98, or NT4 with some IEs
                //crypt32 doesn't support empty DN conversion
                //hard code here
                reqInfo.Subject.cbData = 2;
                reqInfo.Subject.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                                reqInfo.Subject.cbData);
                if (NULL == reqInfo.Subject.pbData)
                {        
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                reqInfo.Subject.pbData[0] = 0x30;
                reqInfo.Subject.pbData[1] = 0x0;
                //done
                break;
            }
            else
            {
               goto ErrorCertStrToNameW;
            }
        }
        if (NULL != reqInfo.Subject.pbData)
        {
            break;
        }
        reqInfo.Subject.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
                                            reqInfo.Subject.cbData);
        if (NULL == reqInfo.Subject.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    // allocate room for the extensions
    cb = (CountStackExtension(m_fNewRequestMethod) + EndExt) * sizeof(CERT_EXTENSION);
    rgExtension = (PCERT_EXTENSION)LocalAlloc(LMEM_FIXED, cb);
    if (NULL == rgExtension)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    memset(rgExtension, 0, cb);
    cb = 0;

    if (!m_fUseClientKeyUsage)
    {
        // Make Key Usage
        rgExtension[iExt].pszObjId = szOID_KEY_USAGE;
        rgExtension[iExt].fCritical = TRUE;

        // AT_SIGNATURE
        if( m_keyProvInfo.dwKeySpec == AT_SIGNATURE)
            bKeyUsage =
                CERT_DIGITAL_SIGNATURE_KEY_USAGE |
                CERT_NON_REPUDIATION_KEY_USAGE;

        //AT_KEYEXCHANGE, limited for EMAIL single use
        // email may not work if signature is present
        else if(m_fLimitExchangeKeyToEncipherment)
	    bKeyUsage =
                CERT_KEY_ENCIPHERMENT_KEY_USAGE |
                CERT_DATA_ENCIPHERMENT_KEY_USAGE;

        // AT_KEYEXCHANGE and AT_SIGNATURE dual key 
        // This is the normal case for AT_KEYEXCHANGE since CAPI will sign with this.
        else 
            bKeyUsage =
                CERT_KEY_ENCIPHERMENT_KEY_USAGE     |
                CERT_DATA_ENCIPHERMENT_KEY_USAGE    |
                CERT_DIGITAL_SIGNATURE_KEY_USAGE    |
                CERT_NON_REPUDIATION_KEY_USAGE;

        bbKeyUsage.pbData = &bKeyUsage;
        bbKeyUsage.cbData = 1;
        bbKeyUsage.cUnusedBits = 1;

        // encode the usage
        rgExtension[iExt].Value.pbData = NULL;
        while (TRUE)
        {
            if(!CryptEncodeObject(
                    CRYPT_ASN_ENCODING,
                    X509_KEY_USAGE,
                    &bbKeyUsage,
                    pbKU,
                    &rgExtension[iExt].Value.cbData))
            {
                goto ErrorEncodeKeyUsage;
            }
            if (NULL != pbKU)
            {
                rgExtension[iExt].Value.pbData = pbKU;
                //done
                break;
            }
            pbKU = (BYTE *)LocalAlloc(LMEM_FIXED, rgExtension[iExt].Value.cbData);
            if (NULL == pbKU)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        iExt++;
    }

    if(m_fEnableSMIMECapabilities)
    {
        // add SMIME extension for symmetric algorithms
        rgExtension[iExt].pszObjId = szOID_RSA_SMIMECapabilities;
        rgExtension[iExt].fCritical = FALSE;
        hr = CreateSMimeExtension(
                    hProv,
                    &pbSMime,
                    &rgExtension[iExt].Value.cbData);
        if (S_OK != hr)
        {
            goto CreateSMimeExtensionError;
        }
        rgExtension[iExt].Value.pbData = pbSMime;
        iExt++;
    }

    if (m_fHonorIncludeSubjectKeyID && m_fIncludeSubjectKeyID)
    {
        hr = myCreateSubjectKeyIdentifierExtension(
                    m_pPublicKeyInfo,
                    &pbSubjectKeyHashExtension,
                    &cbSubjectKeyHashExtension);
        if (S_OK != hr)
        {
            goto myCreateSubjectKeyIdentifierExtensionError;
        }
        //add subject key ID hash extension into PKCS10
        rgExtension[iExt].pszObjId = szOID_SUBJECT_KEY_IDENTIFIER;
        rgExtension[iExt].fCritical = FALSE;
        rgExtension[iExt].Value.pbData = pbSubjectKeyHashExtension;
        rgExtension[iExt].Value.cbData = cbSubjectKeyHashExtension;
        iExt++;
    }

    if(wszPurpose != NULL) {
        cb = 0;
        while (TRUE)
	{
            if(0 == (cb = WideCharToMultiByte(
                            0, 0, wszPurpose, -1, pszPurpose, cb, NULL, NULL)))
            {
                SetLastError(ERROR_OUTOFMEMORY);
                goto ErrorCantConvertPurpose;
            }
            if (NULL != pszPurpose)
            {
                break;
            }
            pszPurpose = (CHAR*)LocalAlloc(LMEM_FIXED, cb);
            if (NULL == pszPurpose)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        szStart = pszPurpose;

        // remove leading blanks
        while(*szStart == ',' || *szStart == ' ')
            *szStart++ = '\0';

        while( szStart[0] != '\0' ) {

            // find the next string
            szEnd = szStart;
            while(*szEnd != ',' && *szEnd != ' ' && *szEnd != '\0')
                szEnd++;

            // remove trailing blanks
            while(*szEnd == ',' || *szEnd == ' ')
                *szEnd++ = '\0';

            enhKeyUsage.cUsageIdentifier++;

            // see if this implies codesigning
            fAddCodeSign |= !strcmp(szStart, SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID) ||
                            !strcmp(szStart, SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID);

            // go to next string
            szStart = szEnd;
        }

        // count the codesign EKU once
        cPassedEKU = enhKeyUsage.cUsageIdentifier;
        if(fAddCodeSign)
            enhKeyUsage.cUsageIdentifier++;

        // encode the extension
        if(enhKeyUsage.cUsageIdentifier != 0) {

            // allocate the EKU array
            enhKeyUsage.rgpszUsageIdentifier = (LPSTR *)LocalAlloc(LMEM_FIXED,
                            enhKeyUsage.cUsageIdentifier * sizeof(LPSTR));
            if (NULL == enhKeyUsage.rgpszUsageIdentifier)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }

            // add the EKU's
            szStart = pszPurpose;
            for(i=0; i<cPassedEKU; i++) {

                while(*szStart == '\0')
                    szStart++;

                enhKeyUsage.rgpszUsageIdentifier[i] = szStart;

                while(*szStart != '\0')
                    szStart++;

            }

            // add the code sign EKU
            if(fAddCodeSign)
                enhKeyUsage.rgpszUsageIdentifier[enhKeyUsage.cUsageIdentifier - 1] = szOID_PKIX_KP_CODE_SIGNING;

            // Deal with the policy, or purpose
            rgExtension[iExt].pszObjId = szOID_ENHANCED_KEY_USAGE ;
            rgExtension[iExt].fCritical = FALSE;

            // encode the enhanced key usage
            rgExtension[iExt].Value.cbData = 0;
            while (TRUE)
            {
                if(!CryptEncodeObject(
                        CRYPT_ASN_ENCODING, X509_ENHANCED_KEY_USAGE,
                        &enhKeyUsage,
                        pbEKU,           // pbEncoded
                        &rgExtension[iExt].Value.cbData))
                {
                    goto ErrorEncodeEnhKeyUsage;
                }
                if (NULL != pbEKU)
                {
                    //got it, done
                    rgExtension[iExt].Value.pbData = pbEKU;
                    break;
                }
                pbEKU = (BYTE *)LocalAlloc(LMEM_FIXED,
                                           rgExtension[iExt].Value.cbData);
                if (NULL == pbEKU)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
            }
            iExt++;
        }
    }

    assert(EndExt >= iExt);

    // now add all of the user defined extensions
    pExtCur = NULL;
    while(NULL != (pExtCur =  EnumStackExtension(pExtCur, m_fNewRequestMethod)) ) {
        rgExtension[iExt] = *pExtCur;
        iExt++;
    }

    // fill in the extensions structure
    Extensions.cExtension = iExt;
    Extensions.rgExtension = rgExtension;

    // encode the extensions
    reqInfo.cAttribute = 0;
    reqInfo.rgAttribute = rgAttribute;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING, X509_EXTENSIONS,
                &Extensions,
                blobExt.pbData,           // pbEncoded
                &blobExt.cbData))
        {
            goto ErrorEncodeExtensions;
        }
        if (NULL != blobExt.pbData)
        {
            //got it, done
            break;
        }
        blobExt.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobExt.cbData);
        if (NULL == blobExt.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    
    if (m_fOID_V2)
    {
        //use new rsa oid
        rgAttribute[reqInfo.cAttribute].pszObjId = szOID_RSA_certExtensions;
    }
    else
    {
        //use microsoft oid for w2k clients
        rgAttribute[reqInfo.cAttribute].pszObjId = szOID_CERT_EXTENSIONS;
    }
    rgAttribute[reqInfo.cAttribute].cValue = 1;
    rgAttribute[reqInfo.cAttribute].rgValue = &blobExt;

    // put in the CSP attribute
    if( !GetSignatureFromHPROV(
       hProv,
       &CSPProvider.Signature.pbData,
       &CSPProvider.Signature.cbData
       ) )
        goto ErrorGetSignatureFromHPROV;

    CSPProvider.pwszProviderName    = m_keyProvInfo.pwszProvName;
    CSPProvider.dwKeySpec           = m_keyProvInfo.dwKeySpec;

    while (TRUE)
    {
        if( !CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                szOID_ENROLLMENT_CSP_PROVIDER,
                &CSPProvider,
                blobCSPAttr.pbData,           // pbEncoded
                &blobCSPAttr.cbData))
        {
            goto ErrorEncodeCSPAttr;
        }
        if (NULL != blobCSPAttr.pbData)
        {
            //got it, done
            break;
        }
        blobCSPAttr.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobCSPAttr.cbData);
        if (NULL == blobCSPAttr.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    reqInfo.cAttribute++;
    rgAttribute[reqInfo.cAttribute].pszObjId = szOID_ENROLLMENT_CSP_PROVIDER;
    rgAttribute[reqInfo.cAttribute].cValue = 1;
    rgAttribute[reqInfo.cAttribute].rgValue = &blobCSPAttr;

    // get the OSVersion
    osvInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if(!GetVersionExA(&osvInfo))
        goto ErrorGetVersionEx;
        
    wsprintfA(szVersion, "%d.%d.%d.%d", 
 	      osvInfo.dwMajorVersion,
        osvInfo.dwMinorVersion,
        osvInfo.dwBuildNumber,
        osvInfo.dwPlatformId);

    cnvOSVer.dwValueType = CERT_RDN_IA5_STRING;
    cnvOSVer.Value.cbData = (DWORD)strlen(szVersion);
    cnvOSVer.Value.pbData = (BYTE *) szVersion;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                X509_ANY_STRING,
                &cnvOSVer,
                blobOSVAttr.pbData,           // pbEncoded
                &blobOSVAttr.cbData))
        {
            goto ErrorEncodeOSVAttr;
        }
        if (NULL != blobOSVAttr.pbData)
        {
            //got it, done
            break;
        }
        blobOSVAttr.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobOSVAttr.cbData);
        if (NULL == blobOSVAttr.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    reqInfo.cAttribute++;
    rgAttribute[reqInfo.cAttribute].pszObjId = szOID_OS_VERSION;
    rgAttribute[reqInfo.cAttribute].cValue = 1;
    rgAttribute[reqInfo.cAttribute].rgValue = &blobOSVAttr;

    // put in the renewal cert if present
    if(m_pCertContextRenewal != NULL && m_fHonorRenew) {

        reqInfo.cAttribute++;

        blobRenewAttr.pbData = m_pCertContextRenewal->pbCertEncoded;
        blobRenewAttr.cbData = m_pCertContextRenewal->cbCertEncoded;

        rgAttribute[reqInfo.cAttribute].pszObjId = szOID_RENEWAL_CERTIFICATE;
        rgAttribute[reqInfo.cAttribute].cValue = 1;
        rgAttribute[reqInfo.cAttribute].rgValue = &blobRenewAttr;
    }

    if (m_fNewRequestMethod && XECI_DISABLE != m_lClientId)
    {
        //put client id as attribute
        hr = myEncodeRequestClientAttributeFromClientId(
                    m_lClientId,
                    &blobClientId.pbData,
                    &blobClientId.cbData);
        if (S_OK != hr)
        {
            //for any reasons, don't include client ID
            hr = put_ClientId(XECI_DISABLE);
            if (S_OK != hr)
            {
                goto putClientIdError;
            }
        }
        else
        {
            reqInfo.cAttribute++;
            rgAttribute[reqInfo.cAttribute].pszObjId = szOID_REQUEST_CLIENT_INFO;
            rgAttribute[reqInfo.cAttribute].cValue = 1;
            rgAttribute[reqInfo.cAttribute].rgValue = &blobClientId;
        }
    }

    // NOTE: On error we always return BAD ALGID
    // this is because sometimes we get an no more data enum error
    // that doesn't help.
    // get the signature oid
    if( !GetCapiHashAndSigAlgId(rgAlg) ) {
        SetLastError((DWORD)NTE_BAD_ALGID);
        goto ErrorGetCapiHashAndSigAlgId;
    }

    // Convert to an oid
    if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_SIGN_KEY,
        (void *) rgAlg,
        CRYPT_SIGN_ALG_OID_GROUP_ID)) ) ) {
        SetLastError((DWORD)NTE_BAD_ALGID);
        goto ErrorCryptFindOIDInfo;
    }

    // we always know we have at least 1 attribute, and we have been zero based, now go to 1 based.
    reqInfo.cAttribute++;
    SignatureInfo.SignatureAlgorithm.pszObjId = (char *) pOidInfo->pszOID;
#if DBG
    //SignatureInfo.ToBeSigned.pbData should be null at the first
    assert(NULL == SignatureInfo.ToBeSigned.pbData);
#endif
    // encode PKCS10
    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED,
                &reqInfo,
                SignatureInfo.ToBeSigned.pbData,           // pbEncoded
                &SignatureInfo.ToBeSigned.cbData))
        {
            goto ErrorEncodePKCS10ToBeSigned;
        }
        if (NULL != SignatureInfo.ToBeSigned.pbData)
        {
            //done
            break;
        }
        SignatureInfo.ToBeSigned.pbData = (BYTE *)
            LocalAlloc(LMEM_FIXED, SignatureInfo.ToBeSigned.cbData);
        if (NULL == SignatureInfo.ToBeSigned.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    // create the signature Info
    // Don't care if xchange or signature key in dwkeySpec because
    // we are signing with the key that is in the PKCS10
#if DBG
    assert(NULL == SignatureInfo.Signature.pbData);
#endif
    while (TRUE)
    {
        if(!CryptSignCertificate(
                hProv,
                m_keyProvInfo.dwKeySpec,
                CRYPT_ASN_ENCODING,
                SignatureInfo.ToBeSigned.pbData,
                SignatureInfo.ToBeSigned.cbData,
                &SignatureInfo.SignatureAlgorithm,
                NULL,                   // reserved
                SignatureInfo.Signature.pbData, // pbSignature
                &SignatureInfo.Signature.cbData))
        {
            goto ErrorCryptSignCertificatePKCS10;
        }
        if (NULL != SignatureInfo.Signature.pbData)
        {
            //done
            break;
        }
        SignatureInfo.Signature.pbData = (BYTE *)
            LocalAlloc(LMEM_FIXED, SignatureInfo.Signature.cbData);
        if (NULL == SignatureInfo.Signature.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    // encode the final signed request
    if( !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_CERT,
            &SignatureInfo,
            NULL,
            &pPkcs10Blob->cbData
            )                               ||
        (pPkcs10Blob->pbData = (BYTE *)
            MyCoTaskMemAlloc(pPkcs10Blob->cbData)) == NULL     ||
        !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_CERT,
            &SignatureInfo,
            pPkcs10Blob->pbData,
            &pPkcs10Blob->cbData
            ) ) {
        goto ErrorEncodePKCS10Request;
    }

    // go ahead and make the pkcs 7
    if((m_pCertContextRenewal != NULL ||
        m_pCertContextSigner  != NULL) &&
       m_fHonorRenew &&
       !m_fCMCFormat) //if CMC, don't make pkcs7
    {

        // create a pkcs7 signed by the old cert
        if(S_OK != CreatePKCS7RequestFromRequest(
            pPkcs10Blob,
            (NULL != m_pCertContextRenewal) ? m_pCertContextRenewal :
                                              m_pCertContextSigner,
            &blobPKCS7) )
            goto ErrorCreatePKCS7RARequestFromPKCS10;

        assert(pPkcs10Blob->pbData != NULL);
        MyCoTaskMemFree(pPkcs10Blob->pbData);
        *pPkcs10Blob = blobPKCS7;
        memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));

    }
 
    ssFlags = CERT_CREATE_SELFSIGN_NO_SIGN;
    if(m_wszPVKFileName[0] != 0)
        ssFlags |= CERT_CREATE_SELFSIGN_NO_KEY_INFO;

    // Get the cert extensions we wish to add to the certificate. 
    // Search for the extensions we need.  
    {
        PCERT_EXTENSION pCertExtCertTypeName = NULL; 
        while(NULL != (pCertExtCertTypeName =  EnumStackExtension(pCertExtCertTypeName, m_fNewRequestMethod)) ) {
            for (DWORD dTmp = 0; dTmp < sizeof(rgszExtensionOIDs) / sizeof(LPSTR); dTmp++) { 
                if (0 == strcmp(rgszExtensionOIDs[dTmp], pCertExtCertTypeName->pszObjId))
                    rgNeededExtensions[(ceExtensions.cExtension)++] = *pCertExtCertTypeName; 
            }
        }

        // Even if we didn't find all of the extensions we wanted, continue ... 
    }

    assert(pCertContext == NULL);
    pCertContext = MyCertCreateSelfSignCertificate(
        hProv,
        &reqInfo.Subject,
        ssFlags,
        &m_keyProvInfo,
        NULL,
        NULL,
        NULL,
        (ceExtensions.cExtension > 0) ? &ceExtensions : NULL
        );
    if (NULL == pCertContext)
        goto ErrorCertCreateSelfSignCertificate;

    // now put the pass thru data on the cert
    requestFlags.fWriteToCSP    =   (m_fWriteCertToCSP != 0);
    requestFlags.fWriteToDS     =   (m_fWriteCertToUserDS != 0);
    requestFlags.openFlags      =   m_RequestStore.dwFlags;

#if DBG
    assert(NULL == requestInfoBlob.pbData);
#endif
    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                XENROLL_REQUEST_INFO,
                &requestFlags,
                requestInfoBlob.pbData,
                &requestInfoBlob.cbData))
        {
            goto ErrorEncodeRequestInfoBlob;
        }
        if (NULL != requestInfoBlob.pbData)
        {
            //done
            break;
        }
        requestInfoBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, requestInfoBlob.cbData);
        if (NULL == requestInfoBlob.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    // set the property on the dummy request cert.
    if( !CertSetCertificateContextProperty(
            pCertContext,
            XENROLL_PASS_THRU_PROP_ID,
            0,
            &requestInfoBlob) )
        goto ErrorCertSetCertificateContextProperty;

    if(m_pCertContextRenewal != NULL && m_fHonorRenew) {

        blobRenewalCert.pbData = m_pCertContextRenewal->pbCertEncoded;
        blobRenewalCert.cbData = m_pCertContextRenewal->cbCertEncoded;

        // set the renewal property if any
        if( !CertSetCertificateContextProperty(
                pCertContext,
                XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
                0,
                &blobRenewalCert) )
            goto ErrorCertSetCertificateContextProperty;
    }

    // save the private key away if needed
    if(m_wszPVKFileName[0] != 0) {

        // open the PVK File
        if( (hFile = CreateOpenFileSafely2(m_wszPVKFileName, IDS_PVK_C, IDS_PVK_O)) == NULL )
            goto ErrorCreatePVKFile;

        assert(m_keyProvInfo.dwKeySpec == AT_SIGNATURE || m_keyProvInfo.dwKeySpec == AT_KEYEXCHANGE);

        // write out the private key
        if( !PrivateKeySave(
            hProv,
            hFile,
            m_keyProvInfo.dwKeySpec,
            NULL,
            m_wszPVKFileName,
            0
            ) )  {
            goto ErrorPrivateKeySave;
        }

        // put a different kind of propery in the store that just points to the pvk file
        keyProvInfoT = m_keyProvInfo;
        keyProvInfoT.pwszContainerName = m_wszPVKFileName;
        if( !CreatePvkProperty(&keyProvInfoT, &blobData) )
            goto ErrorCreatePvkProperty;

        // This is really not needed, it is only nice for other tools
        // like makecert or signcode to be able to look at a cert without
        // specifying a .PVK file if the cert points to the .pvk file.
        // So we don't care if this actually fail, which it will on Auth2 and
        // SP3 Crypt32.dll since Phil was so kind as to not allow any unknown property
        // to be set on the cert --- BAD PHIL!
        CertSetCertificateContextProperty(
                pCertContext,
                CERT_PVK_FILE_PROP_ID,
                0,
                &blobData);

        // only delete the keyset if the key was not pre-existing
        // this is if we write it out to a PVK file only
        // This is safe for scripting since we just generated this and we are putting it to
        // a pvk file. We really aren't deleting the key.
        if (!m_fNewRequestMethod)
        {
            //keep old behavior for createPKCS10 call
            if(!m_fUseExistingKey)
                GetProv(CRYPT_DELETEKEYSET);
        }
    }

    //set all properties from the caller
    pProp = EnumStackProperty(NULL);
    while (NULL != pProp)
    {
        //goto request cert
        if (!CertSetCertificateContextProperty(
                        pCertContext,
                        pProp->lPropId,
                        0,
                        &pProp->prop))
        {
            goto ErrorCertSetCertificateContextProperty;
        }
        pProp = EnumStackProperty(pProp);
    }

    // open the request cert store
    if( (hStore = GetStore(StoreREQUEST)) == NULL)
        goto ErrorCertOpenRequestStore;

    //if old pending request exists, free it first
    fRet = CertFreeCertificateContext(m_pCertContextPendingRequest);
#if DBG
    assert(fRet);
#endif //DBG
    m_pCertContextPendingRequest = NULL;

    // save the temp cert
    if( !MySafeCertAddCertificateContextToStore(
            hStore,
            pCertContext,
            CERT_STORE_ADD_NEW,
            &m_pCertContextPendingRequest, 
	    m_dwEnabledSafteyOptions) ) {
        goto ErrorCertAddToRequestStore;
    }

    // Remove the cached HASH. 
    if (m_hashBlobPendingRequest.pbData != NULL)
    {
        LocalFree(m_hashBlobPendingRequest.pbData);
        m_hashBlobPendingRequest.pbData = NULL;
    }

CommonReturn:

    if(pCertContext != NULL)
        CertFreeCertificateContext(pCertContext);
    if(hFile != NULL)
        CloseHandle(hFile);
    if(blobData.pbData != NULL)
        MyCoTaskMemFree(blobData.pbData);
    if(blobPKCS7.pbData != NULL)
        MyCoTaskMemFree(blobPKCS7.pbData);
    if(CSPProvider.Signature.pbData)
        LocalFree(CSPProvider.Signature.pbData);
    if (NULL != pbSMime)
    {
        LocalFree(pbSMime);
    }
    if (NULL != reqInfo.Subject.pbData)
    {
        LocalFree(reqInfo.Subject.pbData);
    }
    if (NULL != rgExtension)
    {
        LocalFree(rgExtension);
    }
    if (NULL != pbKU)
    {
        LocalFree(pbKU);
    }
    if (NULL != pbEKU)
    {
        LocalFree(pbEKU);
    }
    if (NULL != pszPurpose)
    {
        LocalFree(pszPurpose);
    }
    if (NULL != enhKeyUsage.rgpszUsageIdentifier)
    {
        LocalFree(enhKeyUsage.rgpszUsageIdentifier);
    }
    if (NULL != blobExt.pbData)
    {
        LocalFree(blobExt.pbData);
    }
    if (NULL != blobCSPAttr.pbData)
    {
        LocalFree(blobCSPAttr.pbData);
    }
    if (NULL != blobOSVAttr.pbData)
    {
        LocalFree(blobOSVAttr.pbData);
    }
    if (NULL != SignatureInfo.ToBeSigned.pbData)
    {
        LocalFree(SignatureInfo.ToBeSigned.pbData);
    }
    if (NULL != SignatureInfo.Signature.pbData)
    {
        LocalFree(SignatureInfo.Signature.pbData);
    }
    if (NULL != requestInfoBlob.pbData)
    {
        LocalFree(requestInfoBlob.pbData);
    }
    if (NULL != blobClientId.pbData)
    {
        LocalFree(blobClientId.pbData);
    }
    if (NULL != pbSubjectKeyHashExtension)
    {
        LocalFree(pbSubjectKeyHashExtension);
    }
    if (NULL != pwszNotSafeRequesting)
    {
	LocalFree(pwszNotSafeRequesting);
    }
    if (NULL != pwszTitle)
    {
	LocalFree(pwszTitle);
    }

    // don't know if we have an error or not
    // but I do know the errBefore is set properly
    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    // on error return a NULL
    if(pPkcs10Blob->pbData != NULL)
        MyCoTaskMemFree(pPkcs10Blob->pbData);
    memset(pPkcs10Blob, 0, sizeof(CRYPT_DATA_BLOB));


    goto CommonReturn;

TRACE_ERROR(ErrorGetSignatureFromHPROV);
TRACE_ERROR(ErrorEncodeCSPAttr);
TRACE_ERROR(ErrorCertSetCertificateContextProperty);
TRACE_ERROR(ErrorCryptAcquireContext);
TRACE_ERROR(ErrorCryptGenKey);
TRACE_ERROR(ErrorCryptExportPublicKeyInfo);
TRACE_ERROR(ErrorCertStrToNameW);
TRACE_ERROR(ErrorEncodeKeyUsage);
TRACE_ERROR(ErrorEncodeEnhKeyUsage);
TRACE_ERROR(ErrorEncodeExtensions);
TRACE_ERROR(ErrorEncodePKCS10ToBeSigned);
TRACE_ERROR(ErrorCryptSignCertificatePKCS10);
TRACE_ERROR(ErrorEncodePKCS10Request);
TRACE_ERROR(ErrorCantConvertPurpose);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorCertAddToRequestStore);
TRACE_ERROR(ErrorCreatePVKFile);
TRACE_ERROR(ErrorPrivateKeySave);
TRACE_ERROR(ErrorCreatePvkProperty);
TRACE_ERROR(ErrorCertCreateSelfSignCertificate);
TRACE_ERROR(ErrorEncodeRequestInfoBlob);
TRACE_ERROR(ErrorCreatePKCS7RARequestFromPKCS10);
TRACE_ERROR(ErrorGetCapiHashAndSigAlgId);
TRACE_ERROR(ErrorCryptFindOIDInfo);
TRACE_ERROR(ErrorEncodeOSVAttr);
TRACE_ERROR(ErrorGetVersionEx);
TRACE_ERROR(CancelledError);
TRACE_ERROR(CreateSMimeExtensionError);
TRACE_ERROR(OutOfMemoryError);
TRACE_ERROR(putClientIdError);
TRACE_ERROR(myCreateSubjectKeyIdentifierExtensionError)
TRACE_ERROR(xeLoadRCStringError);
TRACE_ERROR(CryptGetUserKeyCancelError)
#if DBG
TRACE_ERROR(SetKeyContainerSecurityError)
#endif //DBG
}

HRESULT STDMETHODCALLTYPE CCEnroll::acceptPKCS7Blob(
    /* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7) {

    HRESULT     hr;
    HRESULT     hr2 = S_OK;
    LONG                       dwKeySpec               = 0;
    PCCERT_CONTEXT              pCertContextMy          = NULL;
    PCCERT_CONTEXT              pCertContextRequest     = NULL;
    PCCERT_CONTEXT              pCertContextEnd         = NULL;
    HANDLE                      hFile                   = NULL;
    DWORD                       cb                      = 0;
    HCRYPTPROV                  hProv                   = NULL;
    HCRYPTKEY                   hKey                    = NULL;
    HCERTSTORE                  hStoreDS                = NULL;
    HCERTSTORE                  hStoreRequest           = NULL;
    HCERTSTORE                  hStoreMy                = NULL;
    LPWSTR                      pwszTitle               = NULL; 
    LPWSTR                      pwszNotSafeAccepting    = NULL; 

    EnterCriticalSection(&m_csXEnroll);
    
    // Accepting a request is not safe for scripting:  pop up a warning dialog if called from script
    if (0 != m_dwEnabledSafteyOptions) { 
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
	if (S_OK != hr)
	    goto xeLoadRCStringError;
	
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_ACCEPTING_CERT, &pwszNotSafeAccepting);
	if (S_OK != hr)
	    goto xeLoadRCStringError;
	
	if (IDYES != MessageBoxU(NULL, pwszNotSafeAccepting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) { 
	    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
	    goto CancelledError; 
	}
    }

    // get the end entity cert
    hr2 = GetEndEntityCert(pBlobPKCS7, TRUE, &pCertContextEnd);
    if (S_OK != hr2 && XENROLL_E_CANNOT_ADD_ROOT_CERT != hr2)
    {
        hr = hr2;
        goto ErrorGetEndEntityCert;
    }

    if(m_fDeleteRequestCert)
    {
        if ((hStoreRequest = GetStore(StoreREQUEST)) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenRequestStore;
        }

        // check to see if this is in the request store
        if ((pCertContextRequest = CertFindCertificateInStore(
                hStoreRequest,
                CRYPT_ASN_ENCODING,
                0,
                CERT_FIND_PUBLIC_KEY,
                (void *) &pCertContextEnd->pCertInfo->SubjectPublicKeyInfo,
                NULL)) != NULL)
        {
            CertDeleteCertificateFromStore(pCertContextRequest);
            pCertContextRequest = NULL;
        }
    }

    cb = 0;
    // if the cert is to be written to the CSP,
    // put it there but only if we have keys
    if (m_fWriteCertToCSP  &&
        CertGetCertificateContextProperty(
            pCertContextEnd,
            CERT_KEY_PROV_INFO_PROP_ID, NULL, &cb))
    {
        if ((hProv = GetProv(0)) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCryptAcquireContext;
        }

        // This can't fail
        get_KeySpec(&dwKeySpec);

        if (!CryptGetUserKey(
                hProv,
                dwKeySpec,
                &hKey))
        {
            hKey = NULL;
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCryptGetUserKey;
        }

        // always attempt to write the cert to the csp
        if (!CryptSetKeyParam(
                hKey,
                KP_CERTIFICATE,
                pCertContextEnd->pbCertEncoded,
                0))
        {
            // only return an error if it is a smart card error
            // otherwise ignore the error
            if (SCODE_FACILITY(GetLastError()) == FACILITY_SCARD)
            {
                //return error code from writing cert to csp
                //important to save the error code before following clean up
                hr = MY_HRESULT_FROM_WIN32(GetLastError());

                //if can't write cert back to smartcard, remove the cert from my store
                if ((hStoreMy = GetStore(StoreMY)) == NULL)
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto ErrorCertOpenMyStore;
                }

                // check to see if this is in the MY store
                if ((pCertContextMy = CertFindCertificateInStore(
                        hStoreMy,
                        CRYPT_ASN_ENCODING,
                        0,
                        CERT_FIND_PUBLIC_KEY,
                        (void *) &pCertContextEnd->pCertInfo->SubjectPublicKeyInfo,
                        NULL)) != NULL)
                {
                    //try to remove it
                    CertDeleteCertificateFromStore(pCertContextMy);
                    pCertContextMy = NULL;
                }
                if (!m_fUseExistingKey)
                {
                        GetProv(CRYPT_DELETEKEYSET);
                }
                //error any way
                goto ErrorWriteToCSP;
            }
        }
    }

    if(m_fWriteCertToUserDS)
    {
        // otherwise attempt to open the store
        if ((hStoreDS = CertOpenStore(
                CERT_STORE_PROV_SYSTEM,
                X509_ASN_ENCODING,
                NULL,
                CERT_SYSTEM_STORE_CURRENT_USER,
                L"UserDS")) == NULL)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCertOpenDSStore;
        }

        if (!CertAddCertificateContextToStore(
                    hStoreDS,
                    pCertContextEnd,
                    CERT_STORE_ADD_NEW,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorAddCertificateContextToDSStore;
        }

        CertCloseStore(hStoreDS, 0);
        hStoreDS = NULL;

    }

    // determine if he wants to save the spc file
    if (m_wszSPCFileName[0] != 0)
    {
        // open the spc file
        hFile = CreateOpenFileSafely2(m_wszSPCFileName, IDS_SPC_C, IDS_SPC_O);
        if (NULL == hFile)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorCreateSPCFile;
        }

        // write the spc
        assert(pBlobPKCS7->pbData != NULL);
        cb = 0;
        if (!WriteFile(
            hFile,
            pBlobPKCS7->pbData,
            pBlobPKCS7->cbData,
            &cb,
            NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto ErrorWriteSPCFile;
        }
    }

    if (S_OK != hr2)
    {
        //return hr2 error
        hr = hr2;
    }
    else
    {
        hr = S_OK;
    }
ErrorReturn:

    if(hKey != NULL)
        CryptDestroyKey(hKey);
    if(hFile != NULL)
        CloseHandle(hFile);

    if(pCertContextEnd != NULL)
        CertFreeCertificateContext(pCertContextEnd);
    if(hStoreDS != NULL)
        CertCloseStore(hStoreDS, 0);
    if (NULL != pwszNotSafeAccepting)
	LocalFree(pwszNotSafeAccepting);
    if (NULL != pwszTitle)
	LocalFree(pwszTitle);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(ErrorWriteToCSP);
TRACE_ERROR(ErrorCreateSPCFile);
TRACE_ERROR(ErrorWriteSPCFile);
TRACE_ERROR(ErrorGetEndEntityCert);
TRACE_ERROR(ErrorCryptAcquireContext);
TRACE_ERROR(ErrorCryptGetUserKey);
TRACE_ERROR(ErrorCertOpenDSStore);
TRACE_ERROR(ErrorAddCertificateContextToDSStore);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorCertOpenMyStore);
TRACE_ERROR(CancelledError);
TRACE_ERROR(xeLoadRCStringError);
}

PCCERT_CONTEXT STDMETHODCALLTYPE CCEnroll::getCertContextFromPKCS7(
    /* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7) {
    HRESULT hr;
    PCCERT_CONTEXT pCert;

    // get the end entity cert
    hr = GetEndEntityCert(pBlobPKCS7, FALSE, &pCert);
#if DBG
    if (S_OK != hr)
    {
        assert(NULL == pCert);
    }
#endif //DBG
    return pCert;
}

HRESULT STDMETHODCALLTYPE CCEnroll::enumProvidersWStr(
    /* [in] */ LONG dwIndex,
    /* [in] */ LONG dwFlags,
    /* [out] */ LPWSTR __RPC_FAR *ppwsz) {

    DWORD   iLast = 0;
    LONG    i;
    DWORD   dwProvType = 0;
    DWORD   cb = 0;
    HRESULT hr = S_OK;
    DWORD errBefore = GetLastError();

    assert(ppwsz != NULL);
    *ppwsz = NULL;
    SetLastError(ERROR_SUCCESS);

    EnterCriticalSection(&m_csXEnroll);

    for(i=0; i<=dwIndex; i++) {

        do {

            cb = 0;
            if( !CryptEnumProvidersU(
                           iLast,
                           0,
                           0,
                           &dwProvType,
                           NULL,
                           &cb
                           ) ) {

                // only skip if entry is bad
                if( GetLastError() != NTE_PROV_TYPE_ENTRY_BAD)
                    goto ErrorCryptEnumProvidersU;
            }
            iLast++;
        } while((CRYPT_ENUM_ALL_PROVIDERS & dwFlags) != CRYPT_ENUM_ALL_PROVIDERS  &&
                    dwProvType != m_keyProvInfo.dwProvType);
    }

    iLast--;
    if( (*ppwsz = (LPWSTR) MyCoTaskMemAlloc(cb)) == NULL  ||
        !CryptEnumProvidersU(
                       iLast,
                       0,
                       0,
                       &dwProvType,
                       *ppwsz,
                       &cb
                       )                        ) {
        goto ErrorCryptEnumProvidersU;
    }

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    if(*ppwsz != NULL)
    {
        MyCoTaskMemFree(*ppwsz);
	*ppwsz = NULL;
    }

    goto CommonReturn;

TRACE_ERROR(ErrorCryptEnumProvidersU);
}


//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::enumContainersWStr(
    /* [in] */ LONG               dwIndex,
    /* [out] */ LPWSTR __RPC_FAR *ppwsz) {

    DWORD       errBefore   = GetLastError();
    DWORD       cb          = 0;
    LONG        i           = 0;
    char *      psz         = NULL;
    HRESULT     hr          = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    SetLastError(ERROR_SUCCESS);

    assert(ppwsz != NULL);
    *ppwsz = NULL;

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	goto AccessDeniedError;

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    while (TRUE)
    {
        if(!CryptGetProvParam(
                m_hVerifyProv,
                PP_ENUMCONTAINERS,
                (BYTE*)psz,
                &cb,
                CRYPT_FIRST))
        {
            goto ErrorCryptGetProvParam;
        }
        if (NULL != psz)
        {
            //done
            break;
        }
        psz = (char*)LocalAlloc(LMEM_FIXED, cb);
        if (NULL == psz)
        {
            goto ErrorOutOfMem;
        }
    }

    for(i=1; i<=dwIndex; i++) {
        //assume 1st enum buffer size is big enough for all?
        if( !CryptGetProvParam(
            m_hVerifyProv,
            PP_ENUMCONTAINERS,
            (BYTE *) psz,
            &cb,
            0) )
            goto ErrorCryptGetProvParam;
    }

    if( (*ppwsz = WideFromMB(psz)) == NULL )
        goto ErrorOutOfMem;


CommonReturn:
    if (NULL != psz)
    {
        LocalFree(psz);
    }

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    if(*ppwsz != NULL)
        MyCoTaskMemFree(*ppwsz);
    *ppwsz = NULL;

    goto CommonReturn;


SET_ERROR(AccessDeniedError, E_ACCESSDENIED);
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(ErrorOutOfMem);
}


HRESULT CCEnroll::PKCS10ToCert(IN   HCERTSTORE        hCertStore,
                               IN   CRYPT_DATA_BLOB   pkcs10Blob, 
                               OUT  PCCERT_CONTEXT   *ppCertContext)
{
    HRESULT            hr       = E_FAIL; 
    PCERT_REQUEST_INFO pReqInfo = NULL; 

    // Input validation: 
    if (NULL == hCertStore || NULL == pkcs10Blob.pbData || NULL == ppCertContext)
        return E_INVALIDARG;

    if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                            &pkcs10Blob, 
                            CERT_QUERY_CONTENT_FLAG_PKCS10,
                            CERT_QUERY_FORMAT_FLAG_ALL,
                            CRYPT_DECODE_ALLOC_FLAG,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            (const void **) &pReqInfo) )
        goto MyCryptQueryObjectError;

    if ( NULL == (*ppCertContext = CertFindCertificateInStore
                  (hCertStore,
                   CRYPT_ASN_ENCODING, 
                   0, 
                   CERT_FIND_PUBLIC_KEY, 
                   (void *) &pReqInfo->SubjectPublicKeyInfo, 
                   NULL)) )
        goto CertFindCertificateInStoreError;

    hr = S_OK; 

CommonReturn:
    if (NULL != pReqInfo) { LocalFree(pReqInfo); } // Allocated in CryptQueryObject(). 
    return hr; 

 ErrorReturn:
    goto CommonReturn; 

SET_HRESULT(CertFindCertificateInStoreError,  MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MyCryptQueryObjectError,          MY_HRESULT_FROM_WIN32(GetLastError())); 
}

HRESULT CCEnroll::PKCS7ToCert(IN  HCERTSTORE       hCertStore,
                              IN  CRYPT_DATA_BLOB  pkcs7Blob, 
                              OUT PCCERT_CONTEXT  *ppCertContext)
{
    CRYPT_DATA_BLOB            pkcs10Blob;
    CRYPT_VERIFY_MESSAGE_PARA  VerifyPara; 
    HRESULT                    hr           = E_FAIL; 

    // Init locals:
    ZeroMemory(&pkcs10Blob, sizeof(pkcs10Blob)); 
    ZeroMemory(&VerifyPara, sizeof(VerifyPara)); 

    VerifyPara.cbSize                   = sizeof(VerifyPara); 
    VerifyPara.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; 

    if (!MyCryptVerifyMessageSignature
        (&VerifyPara,
         0,                  // dwSignerIndex
         pkcs7Blob.pbData,
         pkcs7Blob.cbData,
         pkcs10Blob.pbData,
         &(pkcs10Blob.cbData),
         NULL                // ppSignerCert
         ) || 0 == pkcs10Blob.cbData)
        goto MyCryptVerifyMessageSignatureError;

    if (NULL == (pkcs10Blob.pbData = (PBYTE)LocalAlloc(LPTR, pkcs10Blob.cbData)))
        goto MemoryError; 

    if (!MyCryptVerifyMessageSignature
        (&VerifyPara,
         0,                  // dwSignerIndex
         pkcs7Blob.pbData,
         pkcs7Blob.cbData,
         pkcs10Blob.pbData,
         &pkcs10Blob.cbData,
         NULL                // ppSignerCert
         ))
        goto MyCryptVerifyMessageSignatureError;

    hr = this->PKCS10ToCert(hCertStore, pkcs10Blob, ppCertContext); 

CommonReturn:
    if (NULL != pkcs10Blob.pbData) { LocalFree(pkcs10Blob.pbData); } 
    return hr; 

ErrorReturn:
    goto CommonReturn;

SET_HRESULT(MemoryError,                        E_OUTOFMEMORY); 
SET_HRESULT(MyCryptVerifyMessageSignatureError, MY_HRESULT_FROM_WIN32(GetLastError()));
}

HRESULT STDMETHODCALLTYPE CCEnroll::freeRequestInfoBlob(
    /* [in] */ CRYPT_DATA_BLOB pkcs7OrPkcs10) {

    DWORD            dwContentType   = NULL;
    HCERTSTORE       hStoreRequest   = NULL;
    HRESULT          hr              = E_FAIL;
    PCCERT_CONTEXT   pCertContext    = NULL; 

    // We're not supposed to delete the cert anyway, so we're done!
    if (!m_fDeleteRequestCert)
        return S_OK; 

    if (NULL == pkcs7OrPkcs10.pbData)
        return E_INVALIDARG;

    EnterCriticalSection(&m_csXEnroll);

    // Step 1)  Determine if we have a PKCS7 or a PKCS10:
    //
    if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                            &pkcs7OrPkcs10,
                            (CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | CERT_QUERY_CONTENT_FLAG_PKCS10),
                            CERT_QUERY_FORMAT_FLAG_ALL,
                            0,
                            NULL,
                            &dwContentType,  // OUT:  PKCS10 or PKCS7
                            NULL,
                            NULL, 
                            NULL,
                            NULL) )
        goto MyCryptQueryObjectError;
    
    // Step 2)  Find a cert context with a matching public key in the request store: 
    // 

    if (NULL == (hStoreRequest = GetStore(StoreREQUEST)))
        goto UnexpectedError; 

    switch (dwContentType) 
    {
    case CERT_QUERY_CONTENT_PKCS7_SIGNED:
        hr = this->PKCS7ToCert(hStoreRequest, pkcs7OrPkcs10, &pCertContext);
        if (S_OK != hr)
        {
            if (CRYPT_E_NOT_FOUND == hr)
            {
                //freeRequestInfo could be called when cert is not issued
                //PKCS7 could be CMC which is signed by request key and
                //cert is not in local store yet. We try cached cert
                if (NULL != m_pCertContextPendingRequest)
                {
                    //looks we still have cached request cert handle
                    pCertContext = CertDuplicateCertificateContext(
                                        m_pCertContextPendingRequest);
                    if (NULL == pCertContext)
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto CertDuplicateCertificateContextError;
                    }
                }
                else if (NULL != m_hashBlobPendingRequest.pbData &&
                         0 < m_hashBlobPendingRequest.cbData)
                {
                    //don't have cached request handle but thumbprint exists
                    //retrieve the request cert handle from store
                    pCertContext = CertFindCertificateInStore(
                                hStoreRequest,  //request store
                                X509_ASN_ENCODING,
                                0,
                                CERT_FIND_HASH,
                                &m_hashBlobPendingRequest,
                                NULL);
                    if (NULL == pCertContext)
                    {
                        hr = MY_HRESULT_FROM_WIN32(GetLastError());
                        goto CertFindCertificateInStoreError;
                    }
                }
                else
                {
                    //sorry, don't know which cert to be free
                    //however, can try to find public key from PKCS7
                    goto PKCS7ToCertError; 
                }
            }
            else
            {
                //other errors
                goto PKCS7ToCertError; 
            }
        }
        break;
    case CERT_QUERY_CONTENT_PKCS10:
        if (S_OK != (hr = this->PKCS10ToCert(hStoreRequest, pkcs7OrPkcs10, &pCertContext)))
            goto PKCS10ToCertError; 
        break;
    default:
        goto InvalidContentTypeError; 
    }

    if (!CertDeleteCertificateFromStore(pCertContext))
    {
        // pCertContext is freed even when CertDeleteCertificateFromStore() returns an error. 
        pCertContext = NULL; 
        goto CertDeleteCertificateFromStoreError; 
    }

    hr = S_OK; 
CommonReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

ErrorReturn:
    if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); } 
    goto CommonReturn;

SET_HRESULT(CertDeleteCertificateFromStoreError,  MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(PKCS7ToCertError,                     hr);
SET_HRESULT(PKCS10ToCertError,                    hr);
SET_HRESULT(InvalidContentTypeError,              E_INVALIDARG);
SET_HRESULT(MyCryptQueryObjectError,              MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(UnexpectedError,                      E_UNEXPECTED);
TRACE_ERROR(CertDuplicateCertificateContextError)
TRACE_ERROR(CertFindCertificateInStoreError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_SPCFileNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szw) {
    HRESULT hr = S_OK;
    EnterCriticalSection(&m_csXEnroll);
    if( (*szw = CopyWideString(m_wszSPCFileName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_SPCFileNameWStr(
    /* [in] */ LPWSTR pwsz) {

    HRESULT hr = S_OK;
 
    if(pwsz == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
        
    EnterCriticalSection(&m_csXEnroll);
        
    if( m_wszSPCFileName != wszEmpty)
        MyCoTaskMemFree(m_wszSPCFileName);
    if( (m_wszSPCFileName = CopyWideString(pwsz)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_PVKFileNameWStr(
    /* [out] */ LPWSTR __RPC_FAR *szw) {

    HRESULT hr = S_OK;
    
    EnterCriticalSection(&m_csXEnroll);
    
    if( (*szw = CopyWideString(m_wszPVKFileName)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_PVKFileNameWStr(
    /* [in] */ LPWSTR pwsz) {

    HRESULT hr = S_OK;
        
    if(pwsz == NULL)
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
        
    EnterCriticalSection(&m_csXEnroll);
        
    if( m_wszPVKFileName != wszEmpty)
        MyCoTaskMemFree(m_wszPVKFileName);
    if( (m_wszPVKFileName = CopyWideString(pwsz)) == NULL )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    else
        m_dwGenKeyFlags |= CRYPT_EXPORTABLE; //why???

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
  }

HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgorithmWStr(
    /* [out] */ LPWSTR __RPC_FAR *ppwsz) {

    PCCRYPT_OID_INFO            pOidInfo        = NULL;
    ALG_ID                      rgAlg[2];
    HRESULT                     hr              = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    assert(ppwsz != NULL);
    *ppwsz  = NULL;

    if( !GetCapiHashAndSigAlgId(rgAlg) )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // Convert to an oid
    else if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_ALGID_KEY,
        (void *) &rgAlg[0],
        CRYPT_HASH_ALG_OID_GROUP_ID)) ) ) {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

    else if( (*ppwsz = WideFromMB(pOidInfo->pszOID)) == NULL)
            hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgorithmWStr(
    /* [in] */ LPWSTR pwsz) {

    HRESULT             hr          = S_OK;
    char *              szObjId     = NULL;
    PCCRYPT_OID_INFO    pOidInfo    = NULL;

    if(pwsz == NULL) {
        return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
    }

    if(!_wcsicmp(L"SHA1", pwsz))
        szObjId = CopyAsciiString(szOID_OIWSEC_sha1);
    else if(!_wcsicmp(L"MD5", pwsz))
        szObjId = CopyAsciiString(szOID_RSA_MD5RSA);
    else if(!_wcsicmp(L"MD2", pwsz))
        szObjId = CopyAsciiString(szOID_RSA_MD2RSA);
    else
        szObjId = MBFromWide(pwsz);

    // something went wrong
    if(szObjId == NULL)
        return(MY_HRESULT_FROM_WIN32(GetLastError()));

    // find the hashing algid
    if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_OID_KEY,
        szObjId,
        0)) ) )
    {
        //XIAOHS: CryptFindOIDInfo does not set the LastError in this case.
        //AV in xEnroll.  See bug# 189320
        //hr = MY_HRESULT_FROM_WIN32(GetLastError());
        hr=NTE_BAD_ALGID;
    }

    assert(szObjId != NULL);
    MyCoTaskMemFree(szObjId);

    EnterCriticalSection(&m_csXEnroll);

    if(hr == S_OK) {
        if( pOidInfo->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID ||
            pOidInfo->dwGroupId == CRYPT_SIGN_ALG_OID_GROUP_ID )
            m_HashAlgId = pOidInfo->Algid;
        else
            hr = CRYPT_E_NOT_FOUND;
    }

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgID(
    LONG    hashAlgID
    ) {

    EnterCriticalSection(&m_csXEnroll);
    m_HashAlgId = hashAlgID;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgID(
    LONG *   hashAlgID
    ) {
    EnterCriticalSection(&m_csXEnroll);
    *hashAlgID = m_HashAlgId;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_RenewalCertificate(
            /* [out] */ PCCERT_CONTEXT __RPC_FAR *ppCertContext) {

    HRESULT     hr      = S_OK;

    *ppCertContext = NULL;

    if( m_pCertContextRenewal == NULL)
        return(MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND));

    EnterCriticalSection(&m_csXEnroll);
    if( NULL == (*ppCertContext = CertDuplicateCertificateContext(m_pCertContextRenewal)) )
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_RenewalCertificate(
            /* [in] */ PCCERT_CONTEXT pCertContext)
{
    HRESULT  hr;
    PCCERT_CONTEXT  pGoodCertContext= NULL;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetGoodCertContext(pCertContext, &pGoodCertContext);
    if (S_OK != hr)
    {
        goto GetGoodCertContextError;
    }

    if(m_pCertContextRenewal != NULL)
    {
        CertFreeCertificateContext(m_pCertContextRenewal);
    }
    m_pCertContextRenewal = pGoodCertContext;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(GetGoodCertContextError);
}

BOOL
CCEnroll::CopyAndPushStackExtension(
    PCERT_EXTENSION pExt,
    BOOL            fCMC)
{

    DWORD       cb              = 0;
    DWORD       cbOid           = 0;
    PEXT_STACK  pExtStackEle    = NULL;
    PBYTE       pb              = NULL;
    PEXT_STACK  *ppExtStack = NULL;
    DWORD       *pcExtStack = NULL;

    assert(pExt != NULL);

    // allocate the space
    cbOid = POINTERROUND((DWORD)strlen(pExt->pszObjId) + 1); //ia64 align
    cb = sizeof(EXT_STACK) + cbOid + pExt->Value.cbData;
    if(NULL == (pb = (PBYTE) malloc(cb))) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(FALSE);
    }

    // set my pointers
    pExtStackEle = (PEXT_STACK) pb;
    pb += sizeof(EXT_STACK);
    pExtStackEle->ext.pszObjId = (LPSTR) pb;
    pb += cbOid;
    pExtStackEle->ext.Value.pbData = pb;

    // set the values
    strcpy(pExtStackEle->ext.pszObjId, pExt->pszObjId);
    pExtStackEle->ext.fCritical     = pExt->fCritical;
    pExtStackEle->ext.Value.cbData  = pExt->Value.cbData;
    memcpy(pExtStackEle->ext.Value.pbData, pExt->Value.pbData, pExt->Value.cbData);

    // insert on the list
    EnterCriticalSection(&m_csXEnroll);
    
    ppExtStack = fCMC ? &m_pExtStackNew : &m_pExtStack;
    pcExtStack = fCMC ? &m_cExtStackNew : &m_cExtStack;
    pExtStackEle->pNext = *ppExtStack;
    *ppExtStack = pExtStackEle;
    (*pcExtStack)++;

    LeaveCriticalSection(&m_csXEnroll);

    return(TRUE);
}

PCERT_EXTENSION
CCEnroll::PopStackExtension(
    BOOL fCMC)
{

    PEXT_STACK  pExtStackEle = NULL;
    PEXT_STACK *ppExtStack = NULL;
    DWORD      *pcExtStack = NULL;

    EnterCriticalSection(&m_csXEnroll);

    ppExtStack = fCMC ? &m_pExtStackNew : &m_pExtStack;
    if(NULL != *ppExtStack)
    {
        pExtStackEle = *ppExtStack;
        *ppExtStack = (*ppExtStack)->pNext;
        pcExtStack = fCMC ? &m_cExtStackNew : &m_cExtStack;
        (*pcExtStack)--;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCERT_EXTENSION) pExtStackEle);
}

DWORD
CCEnroll::CountStackExtension(BOOL fCMC)
{
    DWORD   cExt = 0;

    EnterCriticalSection(&m_csXEnroll);
    cExt = fCMC ? m_cExtStackNew : m_cExtStack;
    LeaveCriticalSection(&m_csXEnroll);

    return(cExt);
}

PCERT_EXTENSION
CCEnroll::EnumStackExtension(
    PCERT_EXTENSION pExtLast,
    BOOL            fCMC)
{
    PEXT_STACK pExtStackEle    = (PEXT_STACK)pExtLast;

    EnterCriticalSection(&m_csXEnroll);

    if(NULL == pExtStackEle)
    {
        pExtStackEle = fCMC ? m_pExtStackNew : m_pExtStack;
    }
    else
    {
        pExtStackEle = pExtStackEle->pNext;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCERT_EXTENSION) pExtStackEle);
}

void
CCEnroll::FreeAllStackExtension(void)
{
    EnterCriticalSection(&m_csXEnroll);

    //free cmc extensions
    while(0 != m_cExtStackNew)
    {
        FreeStackExtension(PopStackExtension(TRUE));
    }

    //free old client extensions
    while(0 != m_cExtStack)
    {
        FreeStackExtension(PopStackExtension(FALSE));
    }

    LeaveCriticalSection(&m_csXEnroll);
}

void CCEnroll::FreeStackExtension(PCERT_EXTENSION pExt) {
    if(pExt != NULL)
        free(pExt);
}

//obselete call for new client
HRESULT STDMETHODCALLTYPE
CCEnroll::AddExtensionsToRequest(
    /* [in] */ PCERT_EXTENSIONS pCertExtensions)
{

    HRESULT hr  = S_OK;
    DWORD   i   = 0;

    assert(pCertExtensions != NULL);

    for(i = 0; i < pCertExtensions->cExtension; i++)
    {
        //push into old extension stack
        if(!CopyAndPushStackExtension(&pCertExtensions->rgExtension[i], FALSE))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            break;
        }
    }

    return(hr);
}

BOOL
CCEnroll::CopyAndPushStackAttribute(
    PCRYPT_ATTRIBUTE pAttr,
    BOOL             fCMC)
{
    DWORD       i               = 0;
    DWORD       cb              = 0;
    DWORD       cbOid           = 0;
    PATTR_STACK pAttrStackEle   = NULL;
    PBYTE       pb              = NULL;
    PATTR_STACK *ppAttrStack = NULL;
    DWORD       *pcAttrStack = NULL;

    assert(pAttr != NULL);

     // allocate the space
    cb = sizeof(ATTR_STACK);
    //make sure aligned for ia64
    cbOid = POINTERROUND((DWORD)strlen(pAttr->pszObjId) + 1);
    cb += cbOid;
    cb += sizeof(CRYPT_ATTR_BLOB) * pAttr->cValue;
    for(i=0; i<pAttr->cValue; i++)
        cb += POINTERROUND(pAttr->rgValue[i].cbData); //pointer align

    if(NULL == (pb = (PBYTE) malloc(cb))) {
        SetLastError(ERROR_OUTOFMEMORY);
        return(FALSE);
    }

    // set my pointers
    pAttrStackEle = (PATTR_STACK) pb;
    pb += sizeof(ATTR_STACK);
    pAttrStackEle->attr.pszObjId = (LPSTR) pb;
    pb += cbOid;
    strcpy(pAttrStackEle->attr.pszObjId, pAttr->pszObjId);

    pAttrStackEle->attr.cValue = pAttr->cValue;
    pAttrStackEle->attr.rgValue = (PCRYPT_ATTR_BLOB) pb;
    pb += sizeof(CRYPT_ATTR_BLOB) * pAttr->cValue;
    for(i=0; i<pAttr->cValue; i++) {
        pAttrStackEle->attr.rgValue[i].pbData = pb;
        pAttrStackEle->attr.rgValue[i].cbData = pAttr->rgValue[i].cbData;
        memcpy(pAttrStackEle->attr.rgValue[i].pbData, pAttr->rgValue[i].pbData, pAttr->rgValue[i].cbData);
        pb += POINTERROUND(pAttr->rgValue[i].cbData);
    }
    assert( pb == ((BYTE *) pAttrStackEle) + cb );

    // insert on the list
    EnterCriticalSection(&m_csXEnroll);

    ppAttrStack = fCMC ? &m_pAttrStackNew : &m_pAttrStack;
    pcAttrStack = fCMC ? &m_cAttrStackNew : &m_cAttrStack;
    pAttrStackEle->pNext = *ppAttrStack;
    *ppAttrStack = pAttrStackEle;
    (*pcAttrStack)++;

    LeaveCriticalSection(&m_csXEnroll);

    return(TRUE);
}

PCRYPT_ATTRIBUTE
CCEnroll::PopStackAttribute(BOOL fCMC)
{
    PATTR_STACK pAttrStackEle = NULL;
    PATTR_STACK *ppAttrStack = NULL;
    DWORD       *pcAttrStack = NULL;

    EnterCriticalSection(&m_csXEnroll);

    ppAttrStack = fCMC ? &m_pAttrStackNew : &m_pAttrStack;

    if(NULL != *ppAttrStack)
    {
        pAttrStackEle = *ppAttrStack;
        *ppAttrStack = (*ppAttrStack)->pNext;
        pcAttrStack = fCMC ? &m_cAttrStackNew : &m_cAttrStack;
        (*pcAttrStack)--;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCRYPT_ATTRIBUTE) pAttrStackEle);
}

DWORD
CCEnroll::CountStackAttribute(BOOL fCMC)
{
    DWORD   cAttr = 0;

    EnterCriticalSection(&m_csXEnroll);
    cAttr = fCMC ? m_cAttrStackNew : m_cAttrStack;
    LeaveCriticalSection(&m_csXEnroll);

    return(cAttr);
}

PCRYPT_ATTRIBUTE
CCEnroll::EnumStackAttribute(
    PCRYPT_ATTRIBUTE pAttrLast,
    BOOL             fCMC)
{
    PATTR_STACK pAttrStackEle    = (PATTR_STACK) pAttrLast;

    EnterCriticalSection(&m_csXEnroll);

    if(NULL == pAttrLast)
    {
        pAttrStackEle = fCMC ? m_pAttrStackNew : m_pAttrStack;
    }
    else
    {
        pAttrStackEle = pAttrStackEle->pNext;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return((PCRYPT_ATTRIBUTE) pAttrStackEle);
}

void CCEnroll::FreeAllStackAttribute(void)
{
    EnterCriticalSection(&m_csXEnroll);

    while(0 != m_cAttrStackNew)
    {
        FreeStackAttribute(PopStackAttribute(TRUE));
    }

    while(0 != m_cAttrStack)
    {
        FreeStackAttribute(PopStackAttribute(FALSE));
    }

    LeaveCriticalSection(&m_csXEnroll);
}

void CCEnroll::FreeStackAttribute(PCRYPT_ATTRIBUTE pAttr) {
    if(pAttr != NULL)
        free(pAttr);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::AddAuthenticatedAttributesToPKCS7Request(
    /* [in] */ PCRYPT_ATTRIBUTES pAttributes)
{
    HRESULT hr = S_OK;
    DWORD i;

    for(i = 0; i < pAttributes->cAttr; i++)
    {
        if(!CopyAndPushStackAttribute(&pAttributes->rgAttr[i], FALSE))
        {
            hr = (MY_HRESULT_FROM_WIN32(GetLastError()));
            break;
        }
        //put into cmc stack too
        if(!CopyAndPushStackAttribute(&pAttributes->rgAttr[i], TRUE))
        {
            hr = (MY_HRESULT_FROM_WIN32(GetLastError()));
            break;
        }
    }

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::CreatePKCS7RequestFromRequest(
    /* [in] */  PCRYPT_DATA_BLOB pRequest,
    /* [in] */  PCCERT_CONTEXT pSigningRACertContext,
    /* [out] */ PCRYPT_DATA_BLOB pPkcs7Blob) {

    HRESULT                     hr              = S_OK;
    DWORD                       errBefore       = GetLastError();
    CRYPT_SIGN_MESSAGE_PARA     signMsgPara;
    PCCRYPT_OID_INFO            pOidInfo        = NULL;
    PCRYPT_ATTRIBUTE            pAttrCur        = NULL;
    DWORD                       i;
    ALG_ID                      rgAlg[2];
    CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
    DWORD                cb = 0;

    assert(pSigningRACertContext != NULL);
    assert(pRequest != NULL);
    assert(pPkcs7Blob != NULL);

    memset(&signMsgPara, 0, sizeof(CRYPT_SIGN_MESSAGE_PARA));
    memset(pPkcs7Blob, 0, sizeof(CRYPT_DATA_BLOB));

    if( !GetCapiHashAndSigAlgId(rgAlg) )
        goto ErrorGetCapiHashAndSigAlgId;

    // find out what the oid is
    if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
        CRYPT_OID_INFO_ALGID_KEY,
        (void *) &rgAlg[0],
        CRYPT_HASH_ALG_OID_GROUP_ID)) ) )
    {
        SetLastError((DWORD)NTE_BAD_ALGID);
        goto ErrorCryptFindOIDInfo;
    }

    // now add all of the user defined extensions
    EnterCriticalSection(&m_csXEnroll);
    signMsgPara.cAuthAttr = CountStackAttribute(m_fNewRequestMethod);

    signMsgPara.rgAuthAttr = (PCRYPT_ATTRIBUTE)LocalAlloc(LMEM_FIXED,
                    signMsgPara.cAuthAttr * sizeof(CRYPT_ATTRIBUTE));
    if( NULL == signMsgPara.rgAuthAttr)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        LeaveCriticalSection(&m_csXEnroll);
        goto ErrorOutOfMemory;
    }

    i = 0;
    pAttrCur = NULL;
    while(NULL != (pAttrCur = EnumStackAttribute(pAttrCur, m_fNewRequestMethod)) ) {
        signMsgPara.rgAuthAttr[i] = *pAttrCur;
        i++;
    }
    LeaveCriticalSection(&m_csXEnroll);

    signMsgPara.cbSize                  = sizeof(CRYPT_SIGN_MESSAGE_PARA);
    signMsgPara.dwMsgEncodingType       = PKCS_7_ASN_ENCODING;
    signMsgPara.pSigningCert            = pSigningRACertContext;
    signMsgPara.HashAlgorithm.pszObjId  = (char *) pOidInfo->pszOID;
    signMsgPara.cMsgCert                = 1;
    signMsgPara.rgpMsgCert              = &pSigningRACertContext;

    //get key prov info
    while (TRUE)
    {
        if(!CertGetCertificateContextProperty(
                pSigningRACertContext,
                CERT_KEY_PROV_INFO_PROP_ID,
                pKeyProvInfo,
                &cb))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertGetCertificateContextPropertyError;
        }
        if (NULL != pKeyProvInfo)
        {
            //got it, done
            break;
        }
        pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)LocalAlloc(LMEM_FIXED, cb);
        if (NULL == pKeyProvInfo)
        {
            hr = E_OUTOFMEMORY;
            goto ErrorOutOfMemory;
        }
    }
    if (0x0 != (pKeyProvInfo->dwFlags & CRYPT_SILENT))
    {
        //have to set silent through msg param
        signMsgPara.dwFlags |= CRYPT_MESSAGE_SILENT_KEYSET_FLAG;
    }


    if( !CryptSignMessage(
        &signMsgPara,
        FALSE,
        1,
        (const BYTE **) &pRequest->pbData,
        &pRequest->cbData ,
        NULL,
        &pPkcs7Blob->cbData)                          ||
    (pPkcs7Blob->pbData = (BYTE *)
        MyCoTaskMemAlloc(pPkcs7Blob->cbData)) == NULL ||
    !CryptSignMessage(
        &signMsgPara,
        FALSE,
        1,
        (const BYTE **) &pRequest->pbData,
        &pRequest->cbData ,
        pPkcs7Blob->pbData,
        &pPkcs7Blob->cbData) )
        goto ErrorCryptSignMessage;

CommonReturn:
    if (NULL != pKeyProvInfo)
    {
        LocalFree(pKeyProvInfo);
    }
    if (NULL != signMsgPara.rgAuthAttr)
    {
        LocalFree(signMsgPara.rgAuthAttr);
    }

    // don't know if we have an error or not
    // but I do know the errBefore is set properly
    SetLastError(errBefore);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();


    // on error return a NULL
    if(pPkcs7Blob->pbData != NULL)
        MyCoTaskMemFree(pPkcs7Blob->pbData);
    memset(pPkcs7Blob, 0, sizeof(CRYPT_DATA_BLOB));

    goto CommonReturn;

TRACE_ERROR(ErrorGetCapiHashAndSigAlgId);
TRACE_ERROR(ErrorCryptSignMessage);
TRACE_ERROR(ErrorCryptFindOIDInfo);
TRACE_ERROR(ErrorOutOfMemory);
TRACE_ERROR(CertGetCertificateContextPropertyError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::AddNameValuePairToSignatureWStr(
    /* [in] */ LPWSTR pwszName,
    /* [in] */ LPWSTR pwszValue)
{
    HRESULT hr = S_OK;

    assert(pwszName != NULL && pwszValue != NULL);

    CRYPT_ENROLLMENT_NAME_VALUE_PAIR nameValuePair = {pwszName, pwszValue};
    CRYPT_ATTR_BLOB blobAttr;
    CRYPT_ATTRIBUTE attr = {szOID_ENROLLMENT_NAME_VALUE_PAIR, 1, &blobAttr};
    CRYPT_ATTRIBUTES attrs = {1, &attr};

    memset(&blobAttr, 0, sizeof(CRYPT_ATTR_BLOB));

    hr = xeEncodeNameValuePair(
                &nameValuePair,
                &blobAttr.pbData,
                &blobAttr.cbData);
    if (S_OK != hr)
    {
        goto error;
    }

    hr = AddAuthenticatedAttributesToPKCS7Request(&attrs);

error:
    if (NULL != blobAttr.pbData)
    {
        MyCoTaskMemFree(blobAttr.pbData);
    }
    return hr;
}


HRESULT STDMETHODCALLTYPE CCEnroll::AddCertTypeToRequestWStr(
            LPWSTR szw) {

    HRESULT                     hr              = S_OK;
    DWORD                       errBefore       = GetLastError();

    CERT_NAME_VALUE  nameValue;
    CERT_EXTENSION  ext;
    CERT_EXTENSIONS exts = {1, &ext};

    memset(&ext, 0, sizeof(CERT_EXTENSION));

    nameValue.dwValueType = CERT_RDN_BMP_STRING;
    nameValue.Value.cbData = 0;
    nameValue.Value.pbData = (PBYTE) szw;

    ext.pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION;

    if( !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_UNICODE_ANY_STRING,
            &nameValue,
            NULL,
            &ext.Value.cbData
            ) )
        goto ErrorCryptEncodeObject;

    ext.Value.pbData = (PBYTE)LocalAlloc(LMEM_FIXED, ext.Value.cbData);
    if(NULL == ext.Value.pbData)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        goto ErrorOutOfMemory;
    }

    if( !CryptEncodeObject(
            CRYPT_ASN_ENCODING,
            X509_UNICODE_ANY_STRING,
            &nameValue,
            ext.Value.pbData,
            &ext.Value.cbData
            ) )
        goto ErrorCryptEncodeObject;

    if(S_OK != AddExtensionsToRequest(&exts))
        goto ErrorAddExtensionsToRequest;

    //put cert template extension into CMC stack
    if(!CopyAndPushStackExtension(&ext, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CopyAndPushStackExtensionError;
    }

CommonReturn:
    if (NULL != ext.Value.pbData)
    {
        LocalFree(ext.Value.pbData);
    }

    // don't know if we have an error or not
    // but I do know the errBefore is set properly
    SetLastError(errBefore);
    return(hr);

ErrorReturn:
    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorCryptEncodeObject);
TRACE_ERROR(ErrorAddExtensionsToRequest);
TRACE_ERROR(ErrorOutOfMemory);
TRACE_ERROR(CopyAndPushStackExtensionError);
}


HRESULT STDMETHODCALLTYPE CCEnroll::AddCertTypeToRequestWStrEx(
            IN  LONG            lType,
            IN  LPCWSTR         pwszOIDOrName,
            IN  LONG            lMajorVersion,
            IN  BOOL            fMinorVersion,
            IN  LONG            lMinorVersion)
{
    HRESULT hr;
    LPCSTR            lpszStructType;
    CERT_NAME_VALUE   nameValue;
    CERT_TEMPLATE_EXT Template;
    VOID             *pv;
    CERT_EXTENSION    ext; //free pbData
    DWORD             cb = 0;
    CHAR             *pszOID = NULL;

    //init
    ZeroMemory(&ext, sizeof(ext));
    ext.fCritical = FALSE;

    if (NULL == pwszOIDOrName)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    switch (lType)
    {
        case XECT_EXTENSION_V1:
            ext.pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION;
            nameValue.dwValueType = CERT_RDN_BMP_STRING;
            nameValue.Value.cbData = 0;
            nameValue.Value.pbData = (BYTE*)pwszOIDOrName;
            pv = (VOID*)&nameValue;
            lpszStructType = X509_UNICODE_ANY_STRING;
        break;
        case XECT_EXTENSION_V2:
            ext.pszObjId = szOID_CERTIFICATE_TEMPLATE;
            //convert wsz OID to ansi
            while (TRUE)
            {
                cb = WideCharToMultiByte(
                            GetACP(),
                            0,
                            pwszOIDOrName,
                            -1,
                            pszOID,
                            cb,
                            NULL,
                            NULL);
                if (0 == cb)
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto WideCharToMultiByteError;
                }
                if (NULL != pszOID)
                {
                    //done
                    break;
                }
                pszOID = (CHAR*)LocalAlloc(LMEM_FIXED, cb);
                if (NULL == pszOID)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
            }

            ZeroMemory(&Template, sizeof(Template));
            Template.pszObjId = pszOID;
            Template.dwMajorVersion = lMajorVersion;
            Template.fMinorVersion =  fMinorVersion;
            Template.dwMinorVersion = lMinorVersion;
            pv = (VOID*)&Template;
            lpszStructType = X509_CERTIFICATE_TEMPLATE;
        break;
        default:
            hr = E_INVALIDARG;
            goto InvalidArgError;
        break;
    }

    while (TRUE)
    {
        if (!CryptEncodeObject(
                    X509_ASN_ENCODING,
                    lpszStructType,
                    pv,
                    ext.Value.pbData,
                    &ext.Value.cbData))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptEncodeObjectError;
        }
        if (NULL != ext.Value.pbData)
        {
            //done
            break;
        }
        ext.Value.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, ext.Value.cbData);
        if (NULL == ext.Value.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    //put cert template extension into CMC stack
    if(!CopyAndPushStackExtension(&ext, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CopyAndPushStackExtensionError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pszOID)
    {
        LocalFree(pszOID);
    }
    if (NULL != ext.Value.pbData)
    {
        LocalFree(ext.Value.pbData);
    }
    return hr;

TRACE_ERROR(InvalidArgError)
TRACE_ERROR(CopyAndPushStackExtensionError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(WideCharToMultiByteError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::getProviderTypeWStr( 
    IN  LPCWSTR  pwszProvName,
    OUT LONG *   plProvType)
{
    HRESULT  hr;
    DWORD    i = 0;
    DWORD    cb;
    DWORD    dwProvType;
    WCHAR   *pwszEnumProvName = NULL;

    if (NULL == pwszProvName)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    //init
    *plProvType = -1;

    while (TRUE)
    {
        while (TRUE)
        {
            if (!CryptEnumProvidersU(
                    i,
                    NULL,
                    0,
                    &dwProvType,
                    pwszEnumProvName,
                    &cb))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                if (MY_HRESULT_FROM_WIN32(NTE_PROV_TYPE_ENTRY_BAD) == hr)
                {
                    //skip bad one and goto next
                    assert(NULL == pwszEnumProvName);
                    break; //skip this one
                }
                //error
                goto CryptEnumProvidersUError;
            }
            if (NULL != pwszEnumProvName)
            {
                //get the current csp name
                break;
            }
            pwszEnumProvName = (WCHAR*)LocalAlloc(LMEM_FIXED, cb);
            if (NULL == pwszEnumProvName)
            {
                hr = E_OUTOFMEMORY;
                goto OutOfMemoryError;
            }
        }
        if (NULL != pwszEnumProvName)
        {
            if (0 == _wcsicmp(pwszProvName, pwszEnumProvName))
            {
                //found matched name
                *plProvType = (LONG)dwProvType;
                break; //out of outer loop
            }
        }
        //not mached, go to next one
        ++i;
        if (NULL != pwszEnumProvName)
        {
            LocalFree(pwszEnumProvName);
            pwszEnumProvName = NULL;
        }
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszEnumProvName)
    {
        LocalFree(pwszEnumProvName);
    }
    return hr;

TRACE_ERROR(InvalidArgError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptEnumProvidersUError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7Blob( 
    /* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7)
{
    return InstallPKCS7BlobEx(pBlobPKCS7, NULL);
}

HRESULT CCEnroll::InstallPKCS7BlobEx( 
    /* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7,
    /* [out] */ LONG           *plCertInstalled)
{

    HRESULT                     hr                      = S_OK;
    DWORD                       errBefore               = GetLastError();
    HCERTSTORE                  hStoreMsg               = NULL;
    LPWSTR                      pwszTitle               = NULL; 
    LPWSTR                      pwszNotSafeAccepting    = NULL; 
 
    EnterCriticalSection(&m_csXEnroll);

    // Accepting a request is not safe for scripting:  pop up a warning dialog if called from script
    if (0 != m_dwEnabledSafteyOptions) { 
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
	if (S_OK != hr) { 
	    SetLastError(hr); 
	    goto xeLoadRCStringError;
	}
	
	hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_ACCEPTING_CERT, &pwszNotSafeAccepting);
	if (S_OK != hr) { 
	    SetLastError(hr); 
	    goto xeLoadRCStringError;
	}
	
	if (IDYES != MessageBoxU(NULL, pwszNotSafeAccepting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) { 
	    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
	    SetLastError(hr); 
	    goto CancelledError; 
	}
    }

    if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                       pBlobPKCS7,
                       (CERT_QUERY_CONTENT_FLAG_CERT |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
                       CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
                       CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED) ,
                       CERT_QUERY_FORMAT_FLAG_ALL,
                       0,
                       NULL,
                       NULL,
                       NULL,
                       &hStoreMsg,
                       NULL,
                       NULL) )
        goto ErrorCryptQueryObject;

    hr = AddCertsToStores(hStoreMsg, plCertInstalled);
    //don't treat cancel as error but return the err code
    if (S_OK != hr && MY_HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
    {
        goto ErrorAddCertsToStores;
    }

CommonReturn:

    if(hStoreMsg != NULL)
        CertCloseStore(hStoreMsg, 0);
    if (NULL != pwszNotSafeAccepting)
	LocalFree(pwszNotSafeAccepting);
    if (NULL != pwszTitle)
	LocalFree(pwszTitle);

    // don't know if we have an error or not
    // but I do know the errBefore is set properly
    SetLastError(errBefore);
    LeaveCriticalSection(&m_csXEnroll);

    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorCryptQueryObject);
TRACE_ERROR(ErrorAddCertsToStores);
TRACE_ERROR(CancelledError);
TRACE_ERROR(xeLoadRCStringError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7( 
    /* [in] */ BSTR wszPKCS7)
{
    CRYPT_DATA_BLOB             blobPKCS7;

    assert(wszPKCS7 != NULL);

    // just put into a blob
    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    // install the blob
    return(InstallPKCS7Blob(&blobPKCS7));
}

HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7Ex( 
    /* [in] */ BSTR   wszPKCS7,
    /* [out] */ LONG __RPC_FAR *plCertInstalled)
{
    CRYPT_DATA_BLOB             blobPKCS7;

    assert(wszPKCS7 != NULL);

    // just put into a blob
    memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
    blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
    blobPKCS7.pbData = (PBYTE) wszPKCS7;

    // install the blob
    return(InstallPKCS7BlobEx(&blobPKCS7, plCertInstalled));
}


// this is a scary routine. Put in for louis, use at your own risk.
HRESULT STDMETHODCALLTYPE CCEnroll::Reset(void)
{
    HRESULT hr;

    EnterCriticalSection(&m_csXEnroll);
    Destruct();
    hr = Init();
    LeaveCriticalSection(&m_csXEnroll);

    return hr;
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetSupportedKeySpec(
    LONG __RPC_FAR *pdwKeySpec) {

    DWORD               errBefore   = GetLastError();
    DWORD               hr          = S_OK;
    DWORD               cb          = sizeof(DWORD);

    SetLastError(ERROR_SUCCESS);

    assert(pdwKeySpec != NULL);
    *pdwKeySpec = 0;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    if( !CryptGetProvParam(
            m_hVerifyProv,
            PP_KEYSPEC,
            (BYTE *) pdwKeySpec,
            &cb,
            0
            ) ) 
        goto ErrorCryptGetProvParam;

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
 
    // We have an error, make sure we set it.
    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(GetVerifyProvError);
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetKeyLenEx(
    LONG    lSizeSpec,
    LONG    lKeySpec,
    LONG __RPC_FAR *pdwKeySize)
{
    BOOL        fKeyX;
    BOOL        fKeyInc = FALSE;
    DWORD       dwKeySize = 0xFFFFFFFF;
    DWORD       cb;
    HRESULT     hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    switch (lKeySpec)
    {
        case XEKL_KEYSPEC_KEYX:
            fKeyX = TRUE;
            break;
        case XEKL_KEYSPEC_SIG:
            fKeyX = FALSE;
            break;
        default:
            //invalid parameter
            hr = E_INVALIDARG;
            goto InvalidArgError;
    }

    switch (lSizeSpec)
    {
        case XEKL_KEYSIZE_MIN:
        case XEKL_KEYSIZE_MAX:
        case XEKL_KEYSIZE_DEFAULT:
            break;
        case XEKL_KEYSIZE_INC:
            fKeyInc = TRUE;
            break;
        default:
            //invalid parameter
            hr = E_INVALIDARG;
            goto InvalidArgError;
    }

    if (!fKeyInc)
    {
        DWORD dwAlg = (fKeyX ? ALG_CLASS_KEY_EXCHANGE : ALG_CLASS_SIGNATURE);

        *pdwKeySize = GetKeySizeInfo(lSizeSpec, dwAlg);
    
        if(0xFFFFFFFF == *pdwKeySize)
        {
            *pdwKeySize = 0;
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto GetKeySizeInfoError;
        }
    }
    else
    {
        if ((fKeyX && (0 != m_dwXhgKeyLenInc)) ||
            (!fKeyX && (0 != m_dwSigKeyLenInc)))
        {
            //we got the cached inc size
            if (fKeyX)
            {
                *pdwKeySize = m_dwXhgKeyLenInc;
            }
            else
            {
                *pdwKeySize = m_dwSigKeyLenInc;
            }
        }
        else
        {
            hr = GetVerifyProv();
            if (S_OK != hr)
            {
                goto GetVerifyProvError;
            }

            //init
            *pdwKeySize = 0;
            cb = sizeof(dwKeySize);
            if (!CryptGetProvParam(
                    m_hVerifyProv,
                    fKeyX ? PP_KEYX_KEYSIZE_INC : PP_SIG_KEYSIZE_INC,
                    (BYTE*)&dwKeySize,
                    &cb,
                    0))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto CryptGetProvParamError;
            }            
            else
            {
                *pdwKeySize = dwKeySize;
                //cache it
                if (fKeyX)
                {
                    m_dwXhgKeyLenInc = dwKeySize;
                }
                else
                {
                    m_dwSigKeyLenInc = dwKeySize;
                }
            }
        }
    }

ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(CryptGetProvParamError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(GetKeySizeInfoError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetKeyLen(
    BOOL    fMin,
    BOOL    fExchange,
    LONG __RPC_FAR *pdwKeySize) {

    DWORD   hr = S_OK;
    LONG    lKeySizeSpec = (fMin ? XEKL_KEYSIZE_MIN : XEKL_KEYSIZE_MAX);

    if(fExchange)
        *pdwKeySize = GetKeySizeInfo(lKeySizeSpec, ALG_CLASS_KEY_EXCHANGE);
    else
        *pdwKeySize = GetKeySizeInfo(lKeySizeSpec, ALG_CLASS_SIGNATURE);
    
    if(*pdwKeySize == 0xFFFFFFFF) {
        *pdwKeySize = 0;
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

    return(hr);
}

DWORD CCEnroll::GetKeySizeInfo(
    LONG    lKeySizeSpec,
    DWORD   algClass) {

    DWORD               cb = sizeof(PROV_ENUMALGS_EX);
    HRESULT             hr = S_OK;
    DWORD               errBefore   = GetLastError();
    DWORD               dwFlags     = CRYPT_FIRST;
    PROV_ENUMALGS_EX    algInfo;
    DWORD               dwKeySize   = 0xFFFFFFFF;
    DWORD               err         = ERROR_SUCCESS;

#ifdef DBG
    //only accept two flags
    assert(ALG_CLASS_KEY_EXCHANGE == algClass ||
           ALG_CLASS_SIGNATURE == algClass);
#endif //DBG

    SetLastError(ERROR_SUCCESS);

    memset(&algInfo, 0, sizeof(algInfo));

    EnterCriticalSection(&m_csXEnroll);

    if ((ALG_CLASS_KEY_EXCHANGE == algClass && 0 != m_dwXhgKeyLenMax) ||
        (ALG_CLASS_SIGNATURE == algClass && 0 != m_dwSigKeyLenMax))
    {
        //got cached sizes, use only KeyLenMax as check
#if DBG
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            assert(0 != m_dwXhgKeyLenMin);
            assert(0 != m_dwXhgKeyLenDef);
        }
        if (ALG_CLASS_SIGNATURE == algClass)
        {
            assert(0 != m_dwSigKeyLenMin);
            assert(0 != m_dwSigKeyLenDef);
        }
#endif //DBG
        //OK, cached, easy
    }
    else
    {
#if DBG
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            assert(0 == m_dwXhgKeyLenMin);
            assert(0 == m_dwXhgKeyLenDef);
        }
        if (ALG_CLASS_SIGNATURE == algClass)
        {
            assert(0 == m_dwSigKeyLenMin);
            assert(0 == m_dwSigKeyLenDef);
        }
#endif //DBG
        hr = GetVerifyProv();
        if (S_OK != hr)
        {
            goto GetVerifyProvError;
        }

        while (CryptGetProvParam(
                m_hVerifyProv,
                PP_ENUMALGS_EX,
                (BYTE *) &algInfo,
                &cb,
                dwFlags))
        {
            // get rid of CRYPT_FIRST flag
            dwFlags = 0;

            if (ALG_CLASS_KEY_EXCHANGE == GET_ALG_CLASS(algInfo.aiAlgid))
            {
                //cache them
                m_dwXhgKeyLenMax = algInfo.dwMaxLen;
                m_dwXhgKeyLenMin = algInfo.dwMinLen;
                m_dwXhgKeyLenDef = algInfo.dwDefaultLen;
            }
            else if (ALG_CLASS_SIGNATURE == GET_ALG_CLASS(algInfo.aiAlgid))
            {
                m_dwSigKeyLenMax = algInfo.dwMaxLen;
                m_dwSigKeyLenMin = algInfo.dwMinLen;
                m_dwSigKeyLenDef = algInfo.dwDefaultLen;
            }

            //see if we cache all sizes through single enum loop
            if (0 != m_dwXhgKeyLenMax &&
                0 != m_dwXhgKeyLenMin &&
                0 != m_dwXhgKeyLenDef &&
                0 != m_dwSigKeyLenMax &&
                0 != m_dwSigKeyLenMin &&
                0 != m_dwSigKeyLenDef)
            {
                //looks we cached all
                break;
            }
        }
    }

    // if we got here,
    // either PP_ENUMALGS_EX is not supported by CSP , should return error
    // or csp doesn't support specified algorithm, should ERROR_NO_MORE_ITEMS

    err = GetLastError();

    if (err != ERROR_SUCCESS)
    {
        if (err != ERROR_NO_MORE_ITEMS) 
        {
            goto ErrorCryptGetProvParam;
        }
        // should be ERROR_NO_MORE_ITEMS
        if ((ALG_CLASS_KEY_EXCHANGE == algClass && 0 != m_dwXhgKeyLenMax) ||
            (ALG_CLASS_SIGNATURE == algClass && 0 != m_dwSigKeyLenMax))
        {
            //we may get here because the csp is signature or exchange only
            //so we cannot cache both once
            SetLastError(ERROR_SUCCESS);
        }
        else
        {
            SetLastError((DWORD)NTE_BAD_ALGID);
        }
    }
            
    //should have all sizes
    if(XEKL_KEYSIZE_MIN == lKeySizeSpec)
    {
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            dwKeySize = m_dwXhgKeyLenMin;
        }
        else
        {
            dwKeySize = m_dwSigKeyLenMin;
        }
    }
    else if (XEKL_KEYSIZE_MAX == lKeySizeSpec)
    {
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            dwKeySize = m_dwXhgKeyLenMax;
        }
        else
        {
            dwKeySize = m_dwSigKeyLenMax;
        }
    }
    else if (XEKL_KEYSIZE_DEFAULT == lKeySizeSpec)
    {
        if (ALG_CLASS_KEY_EXCHANGE == algClass)
        {
            dwKeySize = m_dwXhgKeyLenDef;
        }
        else
        {
            dwKeySize = m_dwSigKeyLenDef;
        }
    }

CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(dwKeySize);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
 
    // We have an error, make sure we set it.
    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
}


HRESULT STDMETHODCALLTYPE CCEnroll::EnumAlgs(
    /* [in] */ LONG  dwIndex,
    /* [in] */ LONG  algMask,
    /* [out] */ LONG  __RPC_FAR *pdwAlgID) {

    DWORD           errBefore   = GetLastError();
    PROV_ENUMALGS       enumAlgs;
    DWORD           cb          = sizeof(enumAlgs);
    LONG            i           = 0;
    HRESULT         hr          = S_OK;
    DWORD           dwFlags;
    BOOL            f1st = TRUE;

    SetLastError(ERROR_SUCCESS);

    memset(&enumAlgs, 0, sizeof(enumAlgs));
    assert(pdwAlgID != NULL);
    *pdwAlgID = 0;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    if (MAXDWORD != m_dwLastAlgIndex &&
        ((DWORD)dwIndex) == m_dwLastAlgIndex + 1)
    {
        //continue enum
        dwFlags = 0;
        while (f1st || (DWORD)algMask != GET_ALG_CLASS(enumAlgs.aiAlgid))
        {
            if(!CryptGetProvParam(
                        m_hVerifyProv,
                        PP_ENUMALGS,
                        (BYTE*)&enumAlgs,
                        &cb,
                        dwFlags))
            {
                goto ErrorCryptGetProvParam;
            }
            f1st = FALSE;
        }
    }
    else
    {
        dwFlags = CRYPT_FIRST;
        for (i = 0; i <= dwIndex; i++)
        {
            if(!CryptGetProvParam(
                   m_hVerifyProv,
                   PP_ENUMALGS,
                   (BYTE*)&enumAlgs,
                   &cb,
                   dwFlags))
            {
                    goto ErrorCryptGetProvParam;
            }
            dwFlags = 0; 

            // if we have not hit something we are counting, do it again
            if ((DWORD)algMask != GET_ALG_CLASS(enumAlgs.aiAlgid)) 
            {
                i--;
            }
        }
    }
    //update cached index
    m_dwLastAlgIndex = dwIndex;

    *pdwAlgID = enumAlgs.aiAlgid;
    
CommonReturn:
    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    //error, reset index
    m_dwLastAlgIndex = MAXDWORD;

    goto CommonReturn;

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
}


HRESULT STDMETHODCALLTYPE CCEnroll::GetAlgNameWStr(
    /* [in] */ LONG               algID,
    /* [out] */ LPWSTR __RPC_FAR *ppwsz) {

    DWORD           errBefore   = GetLastError();
    PROV_ENUMALGS       enumAlgs;
    DWORD           cb          = sizeof(enumAlgs);
    HRESULT         hr          = S_OK;
    DWORD           dwFlags     = CRYPT_FIRST;

    SetLastError(ERROR_SUCCESS);

    memset(&enumAlgs, 0, sizeof(enumAlgs));
    
    EnterCriticalSection(&m_csXEnroll);

    hr = GetVerifyProv();
    if (S_OK != hr)
    {
        goto GetVerifyProvError;
    }

    do {
   
        if( !CryptGetProvParam(
            m_hVerifyProv,
            PP_ENUMALGS,
            (BYTE *) &enumAlgs,
            &cb,
            dwFlags) )
            goto ErrorCryptGetProvParam;

        dwFlags = 0; 
        
   } while((DWORD)algID != enumAlgs.aiAlgid);

   if( (*ppwsz = WideFromMB(enumAlgs.szName)) == NULL )
        goto ErrorOutOfMem;
    
CommonReturn:

    SetLastError(errBefore);

    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(ErrorOutOfMem);
}

HRESULT STDMETHODCALLTYPE CCEnroll::GetAlgName(
            /* [in] */ LONG                     algID,
            /* [out][retval] */ BSTR __RPC_FAR *pbstr) {

    DWORD       errBefore   = GetLastError();
    LPWSTR      pwsz        = NULL;
    HRESULT     hr          = S_OK;

    SetLastError(ERROR_SUCCESS);

    assert(pbstr != NULL);

    if((hr = GetAlgNameWStr(algID, &pwsz)) != S_OK)
        goto ErrorgetAlgNameWStr;

    if( (*pbstr = SysAllocString(pwsz)) == NULL )
        goto ErrorSysAllocString;

CommonReturn:

    if(pwsz != NULL)
        MyCoTaskMemFree(pwsz);

    SetLastError(errBefore);
    return(hr);

ErrorReturn:

    if(GetLastError() == ERROR_SUCCESS)
        SetLastError((DWORD)E_UNEXPECTED);
    hr = MY_HRESULT_FROM_WIN32(GetLastError());

    // We have an error, make sure we set it.
    errBefore = GetLastError();

    goto CommonReturn;

TRACE_ERROR(ErrorgetAlgNameWStr);
TRACE_ERROR(ErrorSysAllocString);
}

HRESULT STDMETHODCALLTYPE CCEnroll::get_ReuseHardwareKeyIfUnableToGenNew(
    /* [retval][out] */ BOOL __RPC_FAR *fBool) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fReuseHardwareKeyIfUnableToGenNew;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::put_ReuseHardwareKeyIfUnableToGenNew(
    /* [in] */ BOOL fBool) {
    EnterCriticalSection(&m_csXEnroll);
    m_fReuseHardwareKeyIfUnableToGenNew = fBool;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreMy(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csXEnroll);

    if(m_MyStore.hStore != NULL)
        hr = E_ACCESSDENIED;
        
    else {
        if(m_MyStore.wszName != wszMY)
            MyCoTaskMemFree(m_MyStore.wszName);
            
        m_MyStore.wszName = NULL;
        m_MyStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);
    
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreCA(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;
    
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_CAStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_CAStore.wszName != wszCA)
            MyCoTaskMemFree(m_CAStore.wszName);
            
        m_CAStore.wszName = NULL;
        m_CAStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);

    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreROOT(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;
    
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_RootStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
            MyCoTaskMemFree(m_RootStore.wszName);
            
        m_RootStore.wszName = NULL;
        m_RootStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);
    
    return(hr);
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreRequest(
    HCERTSTORE   hStore
    ) {
    HRESULT hr = S_OK;
    
    EnterCriticalSection(&m_csXEnroll);
    
    if(m_RequestStore.hStore != NULL)
        hr = E_ACCESSDENIED;
    else {
        if(m_RequestStore.wszName != wszREQUEST)
            MyCoTaskMemFree(m_RequestStore.wszName);
            
        m_RequestStore.wszName = NULL;
        m_RequestStore.hStore = CertDuplicateStore(hStore);
    }

    LeaveCriticalSection(&m_csXEnroll);
    
    return(hr);
}

HRESULT STDMETHODCALLTYPE  CCEnroll::put_LimitExchangeKeyToEncipherment(
    BOOL    fBool
    ) {
    
    EnterCriticalSection(&m_csXEnroll);
    m_fLimitExchangeKeyToEncipherment = fBool;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
    }

HRESULT STDMETHODCALLTYPE  CCEnroll::get_LimitExchangeKeyToEncipherment(
    BOOL * fBool
    ) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fLimitExchangeKeyToEncipherment;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
    }

HRESULT STDMETHODCALLTYPE  CCEnroll::put_EnableSMIMECapabilities(
    BOOL fSMIME
    )
{
    HRESULT hr;
    
    EnterCriticalSection(&m_csXEnroll);

    if (m_fKeySpecSetByClient)
    {
        //SMIME is set by the client
        if (AT_SIGNATURE == m_keyProvInfo.dwKeySpec && fSMIME)
        {
            //try to set signature key spec also SMIME
            hr = XENROLL_E_KEYSPEC_SMIME_MISMATCH;
            goto MismatchError;
        }
    }
    else
    {
        //user didn't set key spec
        //determine the spec accordingly
        m_keyProvInfo.dwKeySpec = fSMIME ? AT_KEYEXCHANGE : AT_SIGNATURE;
    }
    m_fEnableSMIMECapabilities = fSMIME;
    m_fSMIMESetByClient = TRUE;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return(hr);

TRACE_ERROR(MismatchError)
}

HRESULT STDMETHODCALLTYPE  CCEnroll::get_EnableSMIMECapabilities(
    BOOL * fBool
    ) {

    EnterCriticalSection(&m_csXEnroll);
    *fBool = m_fEnableSMIMECapabilities;
    LeaveCriticalSection(&m_csXEnroll);
    return(S_OK);
    }

//ICEnroll4

HRESULT
GetCertificateContextFromBStr(
    IN  BSTR  bstrCert,
    OUT PCCERT_CONTEXT *ppCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pCert = NULL;
    BYTE    *pbCert = NULL;
    DWORD    cbCert = 0;

    // could be any form, binary or base64
    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                        (WCHAR*)bstrCert,
                        SysStringLen(bstrCert),
                        CRYPT_STRING_ANY,
                        pbCert,
                        &cbCert,
                        NULL,
                        NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbCert)
        {
            break; //done
        }
        pbCert = (BYTE*)LocalAlloc(LMEM_FIXED, cbCert);
        if (NULL == pbCert)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    pCert = CertCreateCertificateContext(
                                X509_ASN_ENCODING,
                                pbCert,
                                cbCert);
    if (NULL == pCert)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertCreateCertificateContextError;
    }
    *ppCert = pCert;
    pCert = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pbCert)
    {
        LocalFree(pbCert);
    }
    if (NULL != pCert)
    {
        CertFreeCertificateContext(pCert);
    }
    return (hr);

TRACE_ERROR(CertCreateCertificateContextError)
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(OutOfMemoryError)
}


HRESULT STDMETHODCALLTYPE
CCEnroll::put_PrivateKeyArchiveCertificate(
    IN  BSTR  bstrCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pPrivateKeyArchiveCert = NULL;

    if (NULL != bstrCert)
    {
        hr = GetCertificateContextFromBStr(bstrCert, &pPrivateKeyArchiveCert);
        if (S_OK != hr)
        {
            goto GetCertificateContextFromBStrError;
        }
    }

    // set key archive certificate
    hr = SetPrivateKeyArchiveCertificate(pPrivateKeyArchiveCert);
    if (S_OK != hr)
    {
        goto SetPrivateKeyArchiveCertificateError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pPrivateKeyArchiveCert)
    {
        CertFreeCertificateContext(pPrivateKeyArchiveCert);
    }
    return hr;

TRACE_ERROR(GetCertificateContextFromBStrError)
TRACE_ERROR(SetPrivateKeyArchiveCertificateError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::get_PrivateKeyArchiveCertificate(
    OUT BSTR __RPC_FAR *pbstrCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pPrivateKeyArchiveCert = NULL;
    CRYPT_DATA_BLOB blobCert;

    //init
    *pbstrCert = NULL;

    pPrivateKeyArchiveCert = GetPrivateKeyArchiveCertificate();

    if (NULL != pPrivateKeyArchiveCert)
    {
        blobCert.pbData = pPrivateKeyArchiveCert->pbCertEncoded;
        blobCert.cbData = pPrivateKeyArchiveCert->cbCertEncoded;
        hr = BlobToBstring(&blobCert, CRYPT_STRING_BASE64HEADER, pbstrCert);
        if (S_OK != hr)
        {
            goto BlobToBstringError;
        }
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pPrivateKeyArchiveCert)
    {
        CertFreeCertificateContext(pPrivateKeyArchiveCert);
    }
    return hr;

TRACE_ERROR(BlobToBstringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::put_ThumbPrint(IN BSTR bstrThumbPrint) 
{ 
    CRYPT_DATA_BLOB hashBlob; 
    HRESULT         hr; 

    if (bstrThumbPrint == NULL)
        return E_INVALIDARG; 

    hashBlob.cbData = 0; 
    hashBlob.pbData = NULL;

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_BASE64,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData); 
    if (NULL == hashBlob.pbData)
      goto MemoryErr; 

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_BASE64,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hr = this->put_ThumbPrintWStr(hashBlob); 

 ErrorReturn: 
    if (NULL != hashBlob.pbData) { LocalFree(hashBlob.pbData); } 
    return hr; 

SET_HRESULT(MyCryptToBinaryErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(MemoryErr,          E_OUTOFMEMORY); 
} 

HRESULT STDMETHODCALLTYPE
CCEnroll::put_ThumbPrintWStr(IN CRYPT_DATA_BLOB  hashBlob)
{
    if (hashBlob.pbData == NULL)
        return E_INVALIDARG; 
    
    if (m_hashBlobPendingRequest.pbData != NULL)
    {
        LocalFree(m_hashBlobPendingRequest.pbData); 
        m_hashBlobPendingRequest.pbData = NULL; 
    }

    m_hashBlobPendingRequest.cbData = hashBlob.cbData; 
    m_hashBlobPendingRequest.pbData = (LPBYTE)LocalAlloc(LPTR, m_hashBlobPendingRequest.cbData);
    
    if (m_hashBlobPendingRequest.pbData == NULL)
        return E_OUTOFMEMORY; 

    CopyMemory(m_hashBlobPendingRequest.pbData, hashBlob.pbData, hashBlob.cbData); 
    return S_OK; 
}
     
HRESULT STDMETHODCALLTYPE 
CCEnroll::get_ThumbPrint(OUT BSTR __RPC_FAR *pbstrThumbPrint) 
{ 
    CRYPT_DATA_BLOB hashBlob; 
    DWORD           cchThumbPrintStr; 
    HRESULT         hr; 
    WCHAR          *pwszThumbPrint = NULL; 
    int             i, n; 

   // Input validation: 
    if (pbstrThumbPrint == NULL)
        return E_INVALIDARG; 

    // Initialize locals: 
    ZeroMemory(&hashBlob, sizeof(hashBlob));
    *pbstrThumbPrint  = NULL; 
     
    if (S_OK != (hr = this->get_ThumbPrintWStr(&hashBlob)))
        goto ErrorReturn; 
    
    hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData); 
    if (NULL == hashBlob.pbData)
        goto MemoryErr; 
        
    if (S_OK != (hr = this->get_ThumbPrintWStr(&hashBlob)))
        goto ErrorReturn; 

    // Now we have a binary thumbprint.  Convert this to base64:
    while (TRUE)
    {
        if (!MyCryptBinaryToStringW(
                        hashBlob.pbData, 
                        hashBlob.cbData, 
                        CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR,
                        pwszThumbPrint,
                        &cchThumbPrintStr))
        {
            goto MyCryptToStringErr; 
        }
        if (NULL != pwszThumbPrint)
        {
            //done
            break;
        }
        pwszThumbPrint = (WCHAR*)LocalAlloc(LMEM_FIXED,
                                          cchThumbPrintStr * sizeof(WCHAR)); 
        if (NULL == pwszThumbPrint)
        {
            goto MemoryErr; 
        }
    }

    //make sure no new line and CR
    n = (int)wcslen(pwszThumbPrint);
    for (i = n - 1; i > -1; --i)
    {
        if (L'\r' != pwszThumbPrint[i] &&
            L'\n' != pwszThumbPrint[i])
        {
            break; //done
        }
        pwszThumbPrint[i] = L'\0'; //null it
    }

    // Ok, we've acquired the HASH.  Now copy it to the out parameter: 
    *pbstrThumbPrint = SysAllocString(pwszThumbPrint); 
    if (NULL == *pbstrThumbPrint)
    {
        goto MemoryErr; 
    }

    hr = S_OK; 
ErrorReturn:
    if (NULL != hashBlob.pbData)
    {
        LocalFree(hashBlob.pbData);
    }
    if (NULL != pwszThumbPrint)
    {
        LocalFree(pwszThumbPrint);
    } 
    return hr; 

SET_HRESULT(MyCryptToStringErr, MY_HRESULT_FROM_WIN32(GetLastError())) 
SET_HRESULT(MemoryErr, E_OUTOFMEMORY) 
} 

HRESULT STDMETHODCALLTYPE
CCEnroll::get_ThumbPrintWStr(IN OUT PCRYPT_DATA_BLOB pHashBlob) { 
    HRESULT hr = S_OK; 

    // Input validation: 
    if (NULL == pHashBlob)
        return E_INVALIDARG; 

    // TWO CASES: 
    // 
    // 1)  the thumbprint has been explicitly set by an external caller. 
    // 2)  the thumbprint _wasn't_ explicitly set.  In this case, use the thumbprint
    //     of the request generated by the last call to createPKCS10().
    //
    // CASE 1: 
    //
    if (NULL != m_hashBlobPendingRequest.pbData)
    {
        if (NULL != pHashBlob->pbData)
        {
            if (pHashBlob->cbData < m_hashBlobPendingRequest.cbData) { 
                hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
            }
            else { 
                CopyMemory(pHashBlob->pbData, m_hashBlobPendingRequest.pbData, m_hashBlobPendingRequest.cbData);
                hr = S_OK;
            }
        }

        pHashBlob->cbData = m_hashBlobPendingRequest.cbData; 
        return hr; 
    }
    // CASE 2: 
    // 
    else
    {
        if (NULL == m_pCertContextPendingRequest)
            return E_POINTER; 
     
        // Executes at most twice.  
        if (!CertGetCertificateContextProperty
            (m_pCertContextPendingRequest, 
             CERT_HASH_PROP_ID, 
             (LPVOID)(pHashBlob->pbData), 
             &(pHashBlob->cbData)))
        {
            return MY_HRESULT_FROM_WIN32(GetLastError()); 
        }

        return S_OK; 
    }
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::binaryToString(
    IN  LONG  Flags,
    IN  BSTR  strBinary,
    OUT BSTR *pstrEncoded)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobBinary;
    WCHAR            *pwszEncoded = NULL;

    blobBinary.pbData = (BYTE*)strBinary;
    blobBinary.cbData = SysStringByteLen(strBinary);

    hr = binaryBlobToString(Flags, &blobBinary, &pwszEncoded);
    if (S_OK != hr)
    {
        goto binaryBlobToStringError;
    }

    *pstrEncoded = SysAllocString(pwszEncoded);
    if (NULL == pstrEncoded)
    {
        hr = E_OUTOFMEMORY;
        goto SysAllocStringLenError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszEncoded)
    {
        MyCoTaskMemFree(pwszEncoded);
    }
    return hr;

TRACE_ERROR(binaryBlobToStringError);
TRACE_ERROR(SysAllocStringLenError);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::stringToBinary(
    IN  LONG  Flags,
    IN  BSTR  strEncoded,
    OUT BSTR *pstrBinary)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobBinary;

    ZeroMemory(&blobBinary, sizeof(blobBinary));

    hr = stringToBinaryBlob(Flags, (LPCWSTR)strEncoded, &blobBinary, NULL, NULL);
    if (S_OK != hr)
    {
        goto stringToBinaryBlobError;
    }
    *pstrBinary = SysAllocStringLen(
            (OLECHAR*)blobBinary.pbData, blobBinary.cbData);
    if (NULL == *pstrBinary)
    {
        hr = E_OUTOFMEMORY;
        goto SysAllocStringLenError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobBinary.pbData)
    {
        MyCoTaskMemFree(blobBinary.pbData);
    }
    return hr;

TRACE_ERROR(stringToBinaryBlobError);
TRACE_ERROR(SysAllocStringLenError);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addExtensionToRequest(
    IN  LONG  Flags,
    IN  BSTR  strName,
    IN  BSTR  strValue)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobValue;
    DWORD  cchStrValue = SysStringLen(strValue);
    BYTE   *pbExtVal = NULL;
    DWORD  cbExtVal = 0;

    //convert to binary in case base64 etc.
    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (WCHAR*)strValue,
                    cchStrValue,
                    CRYPT_STRING_ANY,
                    pbExtVal,
                    &cbExtVal,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbExtVal)
        {
            //done
            break;
        }
        pbExtVal = (BYTE*)LocalAlloc(LMEM_FIXED, cbExtVal);
        if (NULL == pbExtVal)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    blobValue.pbData = pbExtVal;
    blobValue.cbData = cbExtVal;

    hr = addExtensionToRequestWStr(Flags, strName, &blobValue);
    if (S_OK != hr)
    {
        goto addExtensionToRequestWStrError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pbExtVal)
    {
        LocalFree(pbExtVal);
    }
    return hr;

TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(addExtensionToRequestWStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addAttributeToRequest(
    IN  LONG  Flags,
    IN  BSTR  strName,
    IN  BSTR  strValue)
{
    HRESULT hr;
    CRYPT_DATA_BLOB   blobValue;
    DWORD  cchStrValue = SysStringLen(strValue);
    BYTE   *pbAttVal = NULL;
    DWORD  cbAttVal = 0;

    //convert to binary in case base64 etc.
    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (WCHAR*)strValue,
                    cchStrValue,
                    CRYPT_STRING_ANY,
                    pbAttVal,
                    &cbAttVal,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pbAttVal)
        {
            //done
            break;
        }
        pbAttVal = (BYTE*)LocalAlloc(LMEM_FIXED, cbAttVal);
        if (NULL == pbAttVal)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    blobValue.pbData = pbAttVal;
    blobValue.cbData = cbAttVal;

    hr = addAttributeToRequestWStr(Flags, strName, &blobValue);
    if (S_OK != hr)
    {
        goto addAttributeToRequestWStrError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pbAttVal)
    {
        LocalFree(pbAttVal);
    }
    return hr;

TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(addAttributeToRequestWStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addNameValuePairToRequest(
    IN  LONG  Flags, //not used
    IN  BSTR  strName,
    IN  BSTR  strValue)
{
    return addNameValuePairToRequestWStr(Flags, strName, strValue);
}

HRESULT STDMETHODCALLTYPE CCEnroll::addBlobPropertyToCertificate(
    IN  LONG   lPropertyId,
    IN  LONG   lFlags,
    IN  BSTR   strProperty)
{
    CRYPT_DATA_BLOB  blob;

    blob.pbData = (BYTE*)strProperty;
    blob.cbData = SysStringByteLen(strProperty);
    if (0x0 != (XECP_STRING_PROPERTY & lFlags))
    {
        //this is a string property, including null
        blob.cbData += sizeof(WCHAR);
    }

    return addBlobPropertyToCertificateWStr(lPropertyId, lFlags, &blob);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::put_SignerCertificate(
    IN  BSTR  bstrCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pSignerCert = NULL;

    if (NULL != bstrCert)
    {
        hr = GetCertificateContextFromBStr(bstrCert, &pSignerCert);
        if (S_OK != hr)
        {
            goto GetCertificateContextFromBStrError;
        }
    }

    // set key archive certificate
    hr = SetSignerCertificate(pSignerCert);
    if (S_OK != hr)
    {
        goto SetSignerCertificateError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pSignerCert)
    {
        CertFreeCertificateContext(pSignerCert);
    }
    return hr;

TRACE_ERROR(GetCertificateContextFromBStrError)
TRACE_ERROR(SetSignerCertificateError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::resetExtensions()
{
    HRESULT hr = S_OK;

    FreeAllStackExtension();

    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::resetAttributes()
{
    HRESULT hr = S_OK;

    FreeAllStackAttribute();

    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createRequest(
    IN  LONG  Flags,
    IN  BSTR  strDNName,
    IN  BSTR  strUsage,
    OUT BSTR *pstrRequest)
{
    return createRequestWStrBStr(
                Flags,
                (LPCWSTR)strDNName,
                (LPCWSTR)strUsage,
                CRYPT_STRING_BASE64REQUESTHEADER,
                pstrRequest);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFileRequest(
    IN  LONG  Flags,
    IN  BSTR  strDNName,
    IN  BSTR  strUsage,
    IN  BSTR  strRequestFileName)
{
    return createFileRequestWStr(Flags, (LPCWSTR)strDNName, (LPCWSTR)strUsage, (LPCWSTR)strRequestFileName);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptResponse(
    IN  BSTR  bstrResponse)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;
    DWORD  cchStrResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    if (NULL == bstrResponse)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    //assume a string
    cchStrResponse = SysStringLen(bstrResponse);

    //convert to binary in case base64 etc.
    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (WCHAR*)bstrResponse,
                    cchStrResponse,
                    CRYPT_STRING_ANY,
                    blobResponse.pbData,
                    &blobResponse.cbData,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != blobResponse.pbData)
        {
            //done
            break;
        }
        blobResponse.pbData = (BYTE*)LocalAlloc(
                                        LMEM_FIXED, blobResponse.cbData);
        if (NULL == blobResponse.pbData)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }

    // accept the blob
    hr = acceptResponseBlob(&blobResponse);
    if (S_OK != hr)
    {
        goto acceptResponseBlobError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobResponse.pbData)
    {
        LocalFree(blobResponse.pbData);
    }
    return (hr);

TRACE_ERROR(acceptResponseBlobError)
TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptFileResponse(
    IN  BSTR  bstrResponseFileName)
{
    return acceptFileResponseWStr((LPCWSTR)bstrResponseFileName);
}

HRESULT
CCEnroll::GetCertFromResponseBlobToBStr(
    IN  CRYPT_DATA_BLOB  *pBlobResponse,
    OUT BSTR *pstrCert)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobCert;
    PCCERT_CONTEXT pCert = NULL;

    hr = getCertContextFromResponseBlob(
                pBlobResponse,
                &pCert);
    if (S_OK != hr)
    {
        goto getCertContextFromResponseBlobError;
    }

    assert(NULL != pCert);

    blobCert.pbData = pCert->pbCertEncoded;
    blobCert.cbData = pCert->cbCertEncoded;
    hr = BlobToBstring(&blobCert, CRYPT_STRING_BASE64HEADER, pstrCert);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pCert)
    {
        CertFreeCertificateContext(pCert);
    }
    return hr;

TRACE_ERROR(getCertContextFromResponseBlobError)
TRACE_ERROR(BlobToBstringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertFromResponse(
    IN  BSTR  strResponse,
    OUT BSTR *pstrCert)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    if (NULL == strResponse)
    {
        hr = E_POINTER;
        goto NullPointerError;
    }

    hr = BstringToBlob(strResponse, &blobResponse);
    if (S_OK != hr)
    {
        goto BstringToBlobError;
    }

    hr = GetCertFromResponseBlobToBStr(
                &blobResponse,
                pstrCert);
    if (S_OK != hr)
    {
        goto GetCertFromResponseBlobToBStrError;
    }

    hr = S_OK;
ErrorReturn:
    return hr;

TRACE_ERROR(NullPointerError)
TRACE_ERROR(BstringToBlobError)
TRACE_ERROR(GetCertFromResponseBlobToBStrError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertFromFileResponse(
    IN  BSTR  strResponseFileName,
    OUT BSTR *pstrCert)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    hr = xeStringToBinaryFromFile(
                (LPCWSTR)strResponseFileName,
                &blobResponse.pbData,
                &blobResponse.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    hr = GetCertFromResponseBlobToBStr(
                &blobResponse,
                pstrCert);
    if (S_OK != hr)
    {
        goto GetCertFromResponseBlobToBStrError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobResponse.pbData)
    {
        LocalFree(blobResponse.pbData);
    }
    return hr;

TRACE_ERROR(xeStringToBinaryFromFileError)
TRACE_ERROR(GetCertFromResponseBlobToBStrError)
}

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE 
CCEnroll::createPFX(
    IN  BSTR  strPassword,
    OUT BSTR *pstrPFX)
{
    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	return E_ACCESSDENIED; 
    return createPFXWStrBStr((LPCWSTR)strPassword, pstrPFX);
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFilePFX(
    IN  BSTR  strPassword,
    IN  BSTR  strPFXFileName)
{
    return createFilePFXWStr((LPCWSTR)strPassword, (LPCWSTR)strPFXFileName);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::setPendingRequestInfo(
    IN  LONG  lRequestID,
    IN  BSTR  strCADNS,
    IN  BSTR  strCAName,
    IN  BSTR  strFriendlyName
    )
{
    return setPendingRequestInfoWStr(
                lRequestID,
                (LPCWSTR)strCADNS,
                (LPCWSTR)strCAName,
                (LPCWSTR)strFriendlyName);
}

HRESULT STDMETHODCALLTYPE
CCEnroll::enumPendingRequest(
    IN  LONG     lIndex,
    IN  LONG     lDesiredProperty,
    OUT VARIANT *pvarProperty
    )
{
    CRYPT_DATA_BLOB  dataBlobProperty; 
    HRESULT          hr; 
    LONG             lProperty; 
    VARIANT          varProperty; 

    // See if we're initializing an enumeration.  If so, just dispatch to
    // enumPendingRequestWStr: 
    if (XEPR_ENUM_FIRST == lIndex) { 
        return enumPendingRequestWStr(XEPR_ENUM_FIRST, 0, NULL); 
    }

    // Input validation: 
    if (lIndex < 0 || NULL == pvarProperty)
        return E_INVALIDARG; 

    // Initialize locals: 
    memset(&varProperty,      0, sizeof(VARIANT));
    memset(&dataBlobProperty, 0, sizeof(CRYPT_DATA_BLOB)); 

    switch (lDesiredProperty) 
        { 
        case XEPR_REQUESTID: 
        case XEPR_VERSION:
            if (S_OK != (hr = enumPendingRequestWStr(lIndex, lDesiredProperty, &lProperty)))
                goto ErrorReturn;

            varProperty.vt   = VT_I4; 
            varProperty.lVal = lProperty; 
            *pvarProperty    = varProperty; 
            goto CommonReturn; 

        case XEPR_CANAME:       
        case XEPR_CAFRIENDLYNAME: 
        case XEPR_CADNS:          
        case XEPR_HASH:            
        case XEPR_V1TEMPLATENAME:  
        case XEPR_V2TEMPLATEOID:   
            dataBlobProperty.cbData = 0; 
            dataBlobProperty.pbData = NULL;

            // Determine the size of the property we desire. 
            hr = enumPendingRequestWStr(lIndex, lDesiredProperty, (LPVOID)&dataBlobProperty);
            if (S_OK != hr || 0 == dataBlobProperty.cbData)
                goto ErrorReturn; 

            dataBlobProperty.pbData = (LPBYTE)LocalAlloc(LPTR, dataBlobProperty.cbData); 
            if (NULL == dataBlobProperty.pbData)
                goto MemoryErr; 

            // Request the property, using our newly allocated buffer. 
            hr = enumPendingRequestWStr(lIndex, lDesiredProperty, (LPVOID)&dataBlobProperty);
            if (hr != S_OK)
                goto ErrorReturn; 

            varProperty.vt      = VT_BSTR; 
            varProperty.bstrVal = SysAllocStringByteLen((LPCSTR)dataBlobProperty.pbData, dataBlobProperty.cbData); 
            if (NULL == varProperty.bstrVal)
                goto MemoryErr; 

            *pvarProperty = varProperty; 
            goto CommonReturn; 

        case XEPR_DATE:            
            goto NotImplErr; 

        default: 
            goto InvalidArgErr; 
    }

 CommonReturn: 
    if (NULL != dataBlobProperty.pbData) { LocalFree(dataBlobProperty.pbData); } 
    return hr; 

 ErrorReturn:
    if (NULL != varProperty.bstrVal) { SysFreeString(varProperty.bstrVal); } 
    goto CommonReturn; 

SET_HRESULT(InvalidArgErr, E_INVALIDARG); 
SET_HRESULT(MemoryErr,     E_OUTOFMEMORY); 
SET_HRESULT(NotImplErr,    E_NOTIMPL); 
}

HRESULT STDMETHODCALLTYPE
CCEnroll::removePendingRequest(
    IN  BSTR bstrThumbPrint
    )
{
    CRYPT_DATA_BLOB hashBlob; 
    HRESULT         hr; 

    if (bstrThumbPrint == NULL)
        return E_INVALIDARG; 

    hashBlob.cbData = 0; 
    hashBlob.pbData = NULL;

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_ANY,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData); 
    if (NULL == hashBlob.pbData)
      goto MemoryErr; 

    if (!MyCryptStringToBinaryW
        ((WCHAR*)bstrThumbPrint,
         SysStringLen(bstrThumbPrint),
         CRYPT_STRING_ANY,
         hashBlob.pbData, 
         &hashBlob.cbData, 
         NULL,
         NULL))
      goto MyCryptToBinaryErr; 

    hr = this->removePendingRequestWStr(hashBlob); 

 CommonReturn: 
    if (NULL != hashBlob.pbData) { LocalFree(hashBlob.pbData); } 
    return hr; 

 ErrorReturn: 
    goto CommonReturn; 

SET_HRESULT(MyCryptToBinaryErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(MemoryErr,          E_OUTOFMEMORY); 
}

//IEnroll4

HRESULT
myCertGetNameString(
    IN  PCCERT_CONTEXT pCert,
    IN  BOOL           fIssuer,
    OUT WCHAR        **ppwszName)
{
    HRESULT  hr;
    DWORD    dwFlags = fIssuer ? CERT_NAME_ISSUER_FLAG : 0;
    DWORD    dwTypePara;
    WCHAR   *pwszName = NULL;
    DWORD    cch = 0;

    while (TRUE)
    {
        cch = CertGetNameStringW(
                pCert,
                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                dwFlags,
                (void*)&dwTypePara,
                pwszName,
                cch);
        if (0 == cch)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertGetNameStringError;
        }
        if (NULL != pwszName)
        {
            //done
            break;
        }
        pwszName = (WCHAR*)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
        if (NULL == pwszName)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }
    *ppwszName = pwszName;
    pwszName = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszName)
    {
        LocalFree(pwszName);
    }
    return hr;

TRACE_ERROR(CertGetNameStringError)
TRACE_ERROR(OutOfMemoryError)
}

HRESULT CCEnroll::GetGoodCertContext(
    IN PCCERT_CONTEXT pCertContext,
    OUT PCCERT_CONTEXT *ppGoodCertContext)
{
    HRESULT hr;
    PCCERT_CONTEXT  pGoodCertContext = NULL;
    DWORD           cb;

    //init
    *ppGoodCertContext = NULL;

    if(pCertContext == NULL)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    //see if the passed cert has kpi
    if(CertGetCertificateContextProperty(
                pCertContext,
                CERT_KEY_PROV_INFO_PROP_ID,
                NULL,
                &cb))
    {
        //this means kpi exists, passed cert is good
        pGoodCertContext = CertDuplicateCertificateContext(pCertContext);
        if (NULL == pGoodCertContext)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertDuplicateCertificateContextError;
        }
    }

    *ppGoodCertContext = pGoodCertContext;
    pGoodCertContext = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pGoodCertContext)
    {
        CertFreeCertificateContext(pGoodCertContext);
    }
    return hr;

TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(CertDuplicateCertificateContextError)
}

HRESULT STDMETHODCALLTYPE CCEnroll::SetSignerCertificate(
    IN PCCERT_CONTEXT pCertContext)
{
    HRESULT hr;
    PCCERT_CONTEXT  pCertGoodContext = NULL;

    EnterCriticalSection(&m_csXEnroll);

    hr = GetGoodCertContext(pCertContext, &pCertGoodContext);
    if (S_OK != hr)
    {
        goto GetGoodCertContextError;
    }
    if(NULL != m_pCertContextSigner)
    {
        CertFreeCertificateContext(m_pCertContextSigner);
    }
    m_pCertContextSigner = pCertGoodContext;

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(GetGoodCertContextError)
}

HRESULT
VerifyPrivateKeyArchiveCertificate(
    IN PCCERT_CONTEXT pCert)
{
    HRESULT  hr;
    CERT_CHAIN_PARA ChainParams;
    CERT_CHAIN_POLICY_PARA ChainPolicy;
    CERT_CHAIN_POLICY_STATUS PolicyStatus;
    CERT_CHAIN_CONTEXT const *pCertChain = NULL;
    char *apszCAXchgOids[] = {szOID_KP_CA_EXCHANGE};
    WCHAR               *pwszSubject = NULL;
    WCHAR               *pwszIssuer = NULL;
    WCHAR               *pwszDesignedSubject = NULL;

    //easy check to make sure ca exchange cert issuer and subject
    //names are in convention
    hr = myCertGetNameString(
                pCert,
                FALSE,
                &pwszSubject);
    if (S_OK != hr)
    {
        goto myCertGetNameStringError;
    }

    hr = myCertGetNameString(
                pCert,
                TRUE,
                &pwszIssuer);
    if (S_OK != hr)
    {
        goto myCertGetNameStringError;
    }

    hr = myAddNameSuffix(
                pwszIssuer,
                wszCNXCHGSUFFIX,
                cchCOMMONNAMEMAX_XELIB,
                &pwszDesignedSubject);
    if (S_OK != hr)
    {
        goto myAddNameSuffixError;
    }

    if (0 != wcscmp(pwszSubject, pwszDesignedSubject))
    {
        //unexpected, they should match
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    ZeroMemory(&ChainParams, sizeof(ChainParams));
    ChainParams.cbSize = sizeof(ChainParams);
    ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
    ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = apszCAXchgOids;
    ChainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(apszCAXchgOids);

    //get cert chain 1st
    if (!MyCertGetCertificateChain(
                NULL,   //HHCE_CURRENT_USER
                pCert,   //ca exchange cert
                NULL,   //use current system time
                NULL,   //no additional stores
                &ChainParams,   //chain params
                CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,  // Make sure that none of the certs in the chain were revoked
                NULL,   //reserved
                &pCertChain))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertGetCertificateChainError;
    }

    ZeroMemory(&ChainPolicy, sizeof(ChainPolicy));
    ChainPolicy.cbSize = sizeof(ChainPolicy);
    ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;

    ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
    PolicyStatus.cbSize = sizeof(PolicyStatus);
    PolicyStatus.lChainIndex = -1;
    PolicyStatus.lElementIndex = -1;

    //verify the chain
    if (!MyCertVerifyCertificateChainPolicy(
                CERT_CHAIN_POLICY_BASE,
                pCertChain,
                &ChainPolicy,
                &PolicyStatus))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertVerifyCertificateChainPolicyError;
    }

    if (S_OK != PolicyStatus.dwError)
    {
        //chain back to root fails
        hr = PolicyStatus.dwError;
        goto CertVerifyCertificateChainPolicyError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pCertChain)
    {
        MyCertFreeCertificateChain(pCertChain);
    }
    if (NULL != pwszSubject)
    {
        LocalFree(pwszSubject);
    }
    if (NULL != pwszDesignedSubject)
    {
        LocalFree(pwszDesignedSubject);
    }
    if (NULL != pwszIssuer)
    {
        LocalFree(pwszIssuer);
    }
    return hr;

TRACE_ERROR(CertGetCertificateChainError)
TRACE_ERROR(CertVerifyCertificateChainPolicyError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(myCertGetNameStringError)
TRACE_ERROR(myAddNameSuffixError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::SetPrivateKeyArchiveCertificate(
    IN PCCERT_CONTEXT  pPrivateKeyArchiveCert)
{
    HRESULT hr;
    PCCERT_CONTEXT pCert = NULL;

    if (NULL != pPrivateKeyArchiveCert)
    {
        //duplicate the cert
        pCert = CertDuplicateCertificateContext(pPrivateKeyArchiveCert);
        if (NULL == pCert)
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertDuplicateCertificateContextError;
        }

        //verify ca exchange cert
        hr = VerifyPrivateKeyArchiveCertificate(pCert);
        if (S_OK != hr)
        {
            goto VerifyPrivateKeyArchiveCertificateError;
        }
    }

    EnterCriticalSection(&m_csXEnroll);

    if (NULL != m_PrivateKeyArchiveCertificate)
    {
        CertFreeCertificateContext(m_PrivateKeyArchiveCertificate);
    }
    m_PrivateKeyArchiveCertificate = pCert;
    pCert = NULL; 

    LeaveCriticalSection(&m_csXEnroll);

    hr = S_OK;
ErrorReturn:
    if (NULL != pCert)
    {
        CertFreeCertificateContext(pCert);
    }
    return (hr);

TRACE_ERROR(CertDuplicateCertificateContextError)
TRACE_ERROR(VerifyPrivateKeyArchiveCertificateError)
}
                
PCCERT_CONTEXT STDMETHODCALLTYPE
CCEnroll::GetPrivateKeyArchiveCertificate(void)
{
    PCCERT_CONTEXT pCert = NULL;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL != m_PrivateKeyArchiveCertificate)
    {
        pCert = CertDuplicateCertificateContext(m_PrivateKeyArchiveCertificate);
    }
    LeaveCriticalSection(&m_csXEnroll);

    return pCert;
}
    
HRESULT STDMETHODCALLTYPE
CCEnroll::binaryBlobToString(
    IN   LONG               Flags,
    IN   PCRYPT_DATA_BLOB   pblobBinary,
    OUT  LPWSTR            *ppwszString)
{
    HRESULT hr;
    WCHAR  *pwszEncoded = NULL;
    DWORD   dwEncoded = 0;

    while (TRUE)
    {
        if (!MyCryptBinaryToStringW(
                    pblobBinary->pbData,
                    pblobBinary->cbData,
                    Flags,
                    pwszEncoded,
                    &dwEncoded))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptBinaryToStringError;
        }
        if (NULL != pwszEncoded)
        {
            //done
            break;
        }
        //dwEncoded includes null terminator
        pwszEncoded = (WCHAR*)MyCoTaskMemAlloc(dwEncoded * sizeof(WCHAR));
        if (NULL == pwszEncoded)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    *ppwszString = pwszEncoded;
    pwszEncoded = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszEncoded)
    {
        MyCoTaskMemFree(pwszEncoded);
    }
    return hr;

TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(MyCryptBinaryToStringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::stringToBinaryBlob(
    IN   LONG               Flags,
    IN   LPCWSTR            pwszString,
    OUT  PCRYPT_DATA_BLOB   pblobBinary,
    OUT  LONG              *pdwSkip,
    OUT  LONG              *pdwFlags)
{
    HRESULT  hr;
    size_t   nLength = wcslen(pwszString); 

    if (nLength*sizeof(WCHAR) > (DWORD)-1)
	goto InvalidArgError; 

    //init
    pblobBinary->pbData = NULL;
    pblobBinary->cbData = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    pwszString,
                    (DWORD)nLength, 
                    Flags,
                    pblobBinary->pbData,
                    &pblobBinary->cbData,
                    (DWORD*)pdwSkip,
                    (DWORD*)pdwFlags))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pblobBinary->pbData)
        {
            //done
            break;
        }
        pblobBinary->pbData = (BYTE*)MyCoTaskMemAlloc(pblobBinary->cbData);
        if (NULL == pblobBinary->pbData)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    hr = S_OK;
ErrorReturn:
    return hr;

SET_HRESULT(InvalidArgError, E_INVALIDARG);
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(MyCoTaskMemAllocError)
}


HRESULT STDMETHODCALLTYPE 
CCEnroll::addExtensionToRequestWStr(
    IN   LONG               Flags,
    IN   LPCWSTR            pwszName,
    IN   PCRYPT_DATA_BLOB   pblobValue)
{
    HRESULT hr = S_OK;
    CERT_EXTENSION ext;
    CERT_EXTENSION *pExt = NULL; //enum 1st
    CHAR   *pszName = NULL;

    //convert wsz oid to sz oid
    hr = xeWSZToSZ(pwszName, &pszName);
    if (S_OK != hr)
    {
        goto error;
    }

    while (NULL != (pExt = EnumStackExtension(pExt, TRUE)))
    {
	if (0 == strcmp(pszName, pExt->pszObjId))
	{
	    //already had the extension, can't have more than 1
	    hr = MY_HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
	    goto error;
	}
    }

    //check to see if it is key usage extension
    if (0 == strcmp(pszName, szOID_KEY_USAGE))
    {
	EnterCriticalSection(&m_csXEnroll);
	m_fUseClientKeyUsage = TRUE;
	LeaveCriticalSection(&m_csXEnroll);
    }
    
    ZeroMemory(&ext, sizeof(ext));
    ext.fCritical = Flags;
    ext.pszObjId = pszName;
    ext.Value = *pblobValue;
    
    if(!CopyAndPushStackExtension(&ext, TRUE))
    {
	hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

error:
    if (NULL != pszName)
    {
        MyCoTaskMemFree(pszName);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addAttributeToRequestWStr(
    IN   LONG               /*Flags*/, 
    IN   LPCWSTR            pwszName,
    IN   PCRYPT_DATA_BLOB   pblobValue)
{
    HRESULT hr = S_OK;
    CRYPT_ATTR_BLOB attrBlob;
    CRYPT_ATTRIBUTE attr;
    CHAR   *pszName = NULL;

    //convert wsz oid to sz oid
    hr = xeWSZToSZ(pwszName, &pszName);
    if (S_OK != hr)
    {
        goto error;
    }

    ZeroMemory(&attr, sizeof(attr));
    attrBlob = *pblobValue;
    attr.pszObjId = pszName;
    attr.cValue = 1;
    attr.rgValue = &attrBlob;

    if(!CopyAndPushStackAttribute(&attr, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

error:
    if (NULL != pszName)
    {
        MyCoTaskMemFree(pszName);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::addNameValuePairToRequestWStr(
    IN   LONG         /*Flags*/, 
    IN   LPCWSTR      pwszName,
    IN   LPCWSTR      pwszValue)
{
    HRESULT hr = S_OK;

    assert(pwszName != NULL && pwszValue != NULL);

    CRYPT_ENROLLMENT_NAME_VALUE_PAIR nameValuePair =
            {const_cast<LPWSTR>(pwszName), const_cast<LPWSTR>(pwszValue)};
    CRYPT_ATTR_BLOB blobAttr;
    CRYPT_ATTRIBUTE attr = {szOID_ENROLLMENT_NAME_VALUE_PAIR, 1, &blobAttr};

    memset(&blobAttr, 0, sizeof(CRYPT_ATTR_BLOB));

    hr = xeEncodeNameValuePair(
                &nameValuePair,
                &blobAttr.pbData,
                &blobAttr.cbData);
    if (S_OK != hr)
    {
        goto error;
    }

    if(!CopyAndPushStackAttribute(&attr, TRUE))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
    }

error:
    if (NULL != blobAttr.pbData)
    {
        MyCoTaskMemFree(blobAttr.pbData);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE CCEnroll::addBlobPropertyToCertificateWStr(
    IN  LONG               lPropertyId,
    IN  LONG               lFlags,
    IN  PCRYPT_DATA_BLOB   pBlobProp)
{
    HRESULT      hr;
    PPROP_STACK  pProp;
    PPROP_STACK  pPropEle = NULL;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == pBlobProp ||
        NULL == pBlobProp->pbData ||
        0 == pBlobProp->cbData)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    // Don't allow arbitrary properties to be set on the request from a script
    // (could be a security risk, we should only allow a small set)
    if (0 != m_dwEnabledSafteyOptions && !IsDesiredProperty(lPropertyId))
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        goto InvalidParameterError;
    }

    //check if the same property exists
    pProp = EnumStackProperty(NULL);
    while (NULL != pProp)
    {
        if (pProp->lPropId == lPropertyId)
        {
            //exists already
            hr = MY_HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
            goto PropertyExistError;
        }
        pProp = EnumStackProperty(pProp);
    }

    pPropEle = (PPROP_STACK)LocalAlloc(LMEM_ZEROINIT, sizeof(PROP_STACK));
    if (NULL == pPropEle)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    
    pPropEle->lPropId = lPropertyId;
    pPropEle->lFlags = lFlags;
    pPropEle->prop.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, pBlobProp->cbData);
    if (NULL == pPropEle->prop.pbData)
    {
        hr = E_OUTOFMEMORY;
        goto OutOfMemoryError;
    }
    CopyMemory(pPropEle->prop.pbData, pBlobProp->pbData, pBlobProp->cbData);
    pPropEle->prop.cbData = pBlobProp->cbData;

    //put into stack
    pPropEle->pNext = m_pPropStack;
    m_pPropStack = pPropEle; //assign m_pPropStack
    m_cPropStack++; //increment of m_cPropStack
    pPropEle = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pPropEle)
    {
        if (NULL != pPropEle->prop.pbData)
        {
            LocalFree(pPropEle->prop.pbData);
        }
        LocalFree(pPropEle);
    }

    LeaveCriticalSection(&m_csXEnroll);
    return hr;

TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(PropertyExistError)
TRACE_ERROR(OutOfMemoryError)
}

PPROP_STACK
CCEnroll::EnumStackProperty(PPROP_STACK pProp)
{
    EnterCriticalSection(&m_csXEnroll);

    if(NULL == pProp)
    {
        //1st one
        pProp = m_pPropStack;
    }
    else
    {
        pProp = pProp->pNext;
    }

    LeaveCriticalSection(&m_csXEnroll);

    return pProp;
}

HRESULT STDMETHODCALLTYPE CCEnroll::resetBlobProperties()
{
    PPROP_STACK  pPropEle;
    PPROP_STACK  pPropNext;

    EnterCriticalSection(&m_csXEnroll);

    pPropEle = m_pPropStack;
    while (NULL != pPropEle)
    {
        //save it to temp
        pPropNext = EnumStackProperty(pPropEle);
        //free the current ele
        if (NULL != pPropEle->prop.pbData)
        {
            LocalFree(pPropEle->prop.pbData);
        }
        LocalFree(pPropEle);
        pPropEle = pPropNext;
    }
    m_pPropStack = NULL;
    m_cPropStack = 0;
    
    LeaveCriticalSection(&m_csXEnroll);
    return S_OK;
}

HRESULT
CCEnroll::GetKeyArchivePKCS7(
    OUT CRYPT_ATTR_BLOB *pBlobKeyArchivePKCS7)
{
    HRESULT    hr;
    HCRYPTPROV hProv;
    HCRYPTKEY  hKey = NULL;
    BYTE      *pBlobPrivateKey = NULL;
    DWORD      cBlobPrivateKey = 0;
    CRYPT_ENCRYPT_MESSAGE_PARA cemp;
    ALG_ID  algId[] = {CALG_3DES, CALG_RC4, CALG_RC2, ALG_TYPE_ANY};
    CRYPT_OID_INFO const *pOidInfo = NULL;
    DWORD i = 0;

    //init
    pBlobKeyArchivePKCS7->pbData = NULL;
    pBlobKeyArchivePKCS7->cbData = 0;

    EnterCriticalSection(&m_csXEnroll);

    //make sure key archival cert is set
    assert(NULL != m_PrivateKeyArchiveCertificate);

    PCCERT_CONTEXT apCert[] = {m_PrivateKeyArchiveCertificate};

    //get user private key
    hProv = GetProv(0); //existing key container handle
    if (NULL == hProv)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CryptAcquireContextError;
    }

    if (NULL == m_hCachedKey)
    {
        //likely used existing key
        if(!CryptGetUserKey(
                    hProv,
                    m_keyProvInfo.dwKeySpec,
                    &hKey))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptGetUserKeyError;
        }
    }

    //export private key
    while (TRUE)
    {
        if (!CryptExportKey(
                NULL != hKey ? hKey : m_hCachedKey,
                NULL, //don't encrypt
                PRIVATEKEYBLOB,
                0,
                pBlobPrivateKey,
                &cBlobPrivateKey))
        {
            //map to xenroll error
            hr = XENROLL_E_KEY_NOT_EXPORTABLE;
            goto CryptExportKeyError;
        }
        if (NULL != pBlobPrivateKey)
        {
            //done
            break;
        }
        pBlobPrivateKey = (BYTE*)MyCoTaskMemAlloc(cBlobPrivateKey);
        if (NULL == pBlobPrivateKey)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if (NULL == m_hCachedKey)
    {
        //it could be csp not supporting CRYPT_ARCHIVABLE
        //got private key, now let's take care of key permission
        if (0x0 == (m_dwGenKeyFlags & CRYPT_EXPORTABLE))
        {
            // user didn't ask exportable, turn it off
            DWORD dwFlags = 0;
            DWORD dwSize = sizeof(dwFlags);
            if (!CryptGetKeyParam(
                    hKey,
                    KP_PERMISSIONS,
                    (BYTE*)&dwFlags,
                    &dwSize,
                    0))
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto CryptGetKeyParamError;
            }
#if DBG
            assert(dwSize = sizeof(dwFlags));
            // make sure was on
            assert(0x0 != (dwFlags & CRYPT_EXPORT));
#endif
            //turn off exportable
            dwFlags = dwFlags & (~CRYPT_EXPORT);
            if (!CryptSetKeyParam(
                    hKey,
                    KP_PERMISSIONS,
                    (BYTE*)&dwFlags,
                    0))
            {
                //hr = MY_HRESULT_FROM_WIN32(GetLastError());
                //goto CryptSetKeyParamError;
                hr = S_OK; //UNDONE, even ms csps have problem with this
            }
        }
    }

    //prepare for encryption
    ZeroMemory(&cemp, sizeof(cemp)); //avoid 0 assignment
    cemp.cbSize = sizeof(cemp);
    cemp.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
    hr = S_OK; //critical init for double while loop

    while (ALG_TYPE_ANY != algId[i])
    {
        pOidInfo = xeCryptFindOIDInfo(
                        CRYPT_OID_INFO_ALGID_KEY,
                        &algId[i],
                        CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
        if (NULL != pOidInfo)
        {
            cemp.ContentEncryptionAlgorithm.pszObjId = 
                                const_cast<char *>(pOidInfo->pszOID);
            //encryt into pkcs7
            while (TRUE)
            {
                if (!CryptEncryptMessage(
                        &cemp,
                        sizeof(apCert)/sizeof(apCert[0]),
                        apCert,
                        pBlobPrivateKey,
                        cBlobPrivateKey,
                        pBlobKeyArchivePKCS7->pbData,
                        &pBlobKeyArchivePKCS7->cbData))
                {
                    //save the 1st error code
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
#ifdef DBG
                    assert(NULL == pBlobKeyArchivePKCS7->pbData);
#endif
                    break; //break inner while loop
                }
                if (NULL != pBlobKeyArchivePKCS7->pbData)
                {
                    //done, got encrypted blob
                    //ignore error from previous alg tries
                    hr = S_OK;
                    break;
                }
                pBlobKeyArchivePKCS7->pbData = (BYTE*)MyCoTaskMemAlloc(
                                        pBlobKeyArchivePKCS7->cbData);
                if (NULL == pBlobKeyArchivePKCS7->pbData)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
            }
            if (S_OK == hr)
            {
                //done, out of outer while loop
                break;
            }
        }
        ++i;
    }
    if (NULL == pOidInfo)
    {
        hr = CRYPT_E_NOT_FOUND;
        goto CryptElemNotFoundError;
    }

    if (S_OK != hr)
    {
        goto CryptEncryptMessageError;
    }

    hr = S_OK;
ErrorReturn:
    //now let's destroy cached key handle
    if (NULL != m_hCachedKey)
    {
        CryptDestroyKey(m_hCachedKey);
        m_hCachedKey = NULL; //critical to reset
    }
    //note, do above before leaving critical section
    LeaveCriticalSection(&m_csXEnroll);
    if (NULL != pBlobPrivateKey)
    {
	SecureZeroMemory(pBlobPrivateKey, cBlobPrivateKey);
        MyCoTaskMemFree(pBlobPrivateKey);
    }
    if (NULL != hKey)
    {
        CryptDestroyKey(hKey);
    }
    return hr;

TRACE_ERROR(CryptEncryptMessageError)
TRACE_ERROR(CryptAcquireContextError)
TRACE_ERROR(CryptGetUserKeyError)
TRACE_ERROR(CryptExportKeyError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptElemNotFoundError)
//TRACE_ERROR(CryptSetKeyParamError)
TRACE_ERROR(CryptGetKeyParamError)
}

HRESULT
GetKeyProvInfoFromCert(
    IN  PCCERT_CONTEXT    pCert,
    OUT DWORD            *pdwKeySpec,
    OUT HCRYPTPROV       *phProv)
{
    HRESULT hr;
    CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
    DWORD                cb = 0;
    HCRYPTPROV           hProv = NULL;

    if (NULL == pCert || NULL == phProv || NULL == pdwKeySpec)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    while (TRUE)
    {
        if(!CertGetCertificateContextProperty(
                pCert,
                CERT_KEY_PROV_INFO_PROP_ID,
                pKeyProvInfo,
                &cb))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertGetCertificateContextPropertyError;
        }
        if (NULL != pKeyProvInfo)
        {
            //got it, done
            break;
        }
        pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)LocalAlloc(LMEM_FIXED, cb);
        if (NULL == pKeyProvInfo)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    if (!CryptAcquireContextU(
            &hProv,
            pKeyProvInfo->pwszContainerName,
            pKeyProvInfo->pwszProvName,
            pKeyProvInfo->dwProvType,
            pKeyProvInfo->dwFlags))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CryptAcquireContextUError;
    }
    *phProv = hProv;
    hProv = NULL;
    *pdwKeySpec = pKeyProvInfo->dwKeySpec;

    hr = S_OK;
ErrorReturn:
    if (NULL != pKeyProvInfo)
    {
        LocalFree(pKeyProvInfo);
    }
    if (NULL != hProv)
    {
        CryptReleaseContext(hProv, 0);
    }
    return hr;

TRACE_ERROR(CryptAcquireContextUError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CertGetCertificateContextPropertyError)
TRACE_ERROR(InvalidArgError)
}

HRESULT
xeCreateKeyArchivalHashAttribute(
    IN  CRYPT_HASH_BLOB     *pBlobKAHash,
    OUT CRYPT_ATTR_BLOB     *pBlobKAAttr)
{
    HRESULT hr;
    BYTE   *pbData = NULL;
    DWORD   cbData = 0;

    while (TRUE)
    {
        if(!CryptEncodeObject(
                CRYPT_ASN_ENCODING,
                X509_OCTET_STRING,
                (void*)pBlobKAHash,
                pbData,
                &cbData))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CryptEncodeObjectError;
        }

        if (NULL != pbData)
        {
            //done
            break;
        }

        pbData = (BYTE*)LocalAlloc(LMEM_FIXED, cbData);
        if (NULL == pbData)
        {
            hr = E_OUTOFMEMORY;
            goto LocalAllocError;
        }
    }
    pBlobKAAttr->pbData = pbData;
    pBlobKAAttr->cbData = cbData;
    pbData = NULL;

    hr = S_OK;
ErrorReturn:
    if (NULL != pbData)
    {
        LocalFree(pbData);
    }
    return hr;

TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(LocalAllocError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createRequestWStr(
    IN   LONG              Flags,
    IN   LPCWSTR           pwszDNName,
    IN   LPCWSTR           pwszUsage,
    OUT  PCRYPT_DATA_BLOB  pblobRequest)
{
    HRESULT hr;
    CRYPT_DATA_BLOB  blobPKCS10;
    CRYPT_ATTR_BLOB  blobKeyArchivePKCS7;
    ALG_ID           rgAlg[2];
    PCCRYPT_OID_INFO pOidInfo;
    CERT_EXTENSION  *rgExt = NULL;
    DWORD            cExt = 0;
    CERT_EXTENSION  *pExt = NULL; //for enum 1st
    CRYPT_ATTRIBUTE  *rgAttr = NULL;
    DWORD            cAttr = 0;
    CRYPT_ATTRIBUTE  *pAttr = NULL; //for enum 1st
    CRYPT_ATTRIBUTES rgAttributes;
    CRYPT_ATTRIBUTE  *rgUnauthAttr = NULL; //init
    DWORD            cUnauthAttr = 0; //init
    DWORD            cb;
    HCRYPTPROV       hProvSigner = NULL;
    DWORD            dwKeySpecSigner = 0;
    PCCERT_CONTEXT   pCertSigner = NULL; //just init, no free
    HCRYPTPROV       hRequestProv = NULL;
    BYTE            *pbSubjectKeyHash = NULL;
    DWORD            cbSubjectKeyHash = 0;
    CRYPT_HASH_BLOB  blobKAHash;
    CRYPT_ATTR_BLOB  blobKAHashAttr;
    CRYPT_ATTRIBUTE  attrKAHash =
        {szOID_ENCRYPTED_KEY_HASH, 1, &blobKAHashAttr};

    ZeroMemory(&blobPKCS10, sizeof(blobPKCS10));
    ZeroMemory(&blobKeyArchivePKCS7, sizeof(blobKeyArchivePKCS7));
    ZeroMemory(&blobKAHash, sizeof(blobKAHash));
    ZeroMemory(&blobKAHashAttr, sizeof(blobKAHashAttr));

    EnterCriticalSection(&m_csXEnroll);
    
    // BUG 533202: should block key archival when re-using key (from script)
    if (0 != m_dwEnabledSafteyOptions && NULL != m_PrivateKeyArchiveCertificate && m_fUseExistingKey) { 
	hr = E_ACCESSDENIED; 
	goto AccessDeniedError; 
    }

    m_fNewRequestMethod = TRUE;  //critical
    m_fOID_V2 = TRUE;
    m_fCMCFormat = FALSE;
    m_fHonorIncludeSubjectKeyID = FALSE;

    switch (Flags)
    {
        case XECR_CMC:
        {
            if (NULL != m_pCertContextRenewal &&
                NULL != m_pCertContextSigner)
            {
                //don't support both on yet
                hr = MY_HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                goto NotSupportedError;
            }

            m_fCMCFormat = TRUE;
            m_fHonorIncludeSubjectKeyID = TRUE;
            // create pkcs 10 first
            hr = createPKCS10WStr(
                        pwszDNName,
                        pwszUsage,     //wszPurpose,
                        &blobPKCS10);
            if(S_OK != hr)
            {
                goto createPKCS10WStrError;
            }
            //set it back
            m_fCMCFormat = FALSE;

            //get all extensions
            cb = CountStackExtension(TRUE) * sizeof(CERT_EXTENSION);
            if (0 < cb)
            {
                rgExt = (CERT_EXTENSION*)LocalAlloc(LMEM_FIXED, cb);
                if (NULL == rgExt)
                {    
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                ZeroMemory(rgExt, cb);
                while(NULL != (pExt = EnumStackExtension(pExt, TRUE)))
                {
                    rgExt[cExt] = *pExt;
                    cExt++;
                }
            }

            //get all attributes including namevalue pair
            cb = CountStackAttribute(TRUE) * sizeof(CRYPT_ATTRIBUTE);
            if (NULL != m_PrivateKeyArchiveCertificate)
            {
                //add one more attribute to hold encrypted key hash
                cb += sizeof(CRYPT_ATTRIBUTE);
            }
            if (0 < cb)
            {
                rgAttr = (CRYPT_ATTRIBUTE*)LocalAlloc(LMEM_FIXED, cb);
                if (NULL == rgAttr)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                ZeroMemory(rgAttr, cb);
                while(NULL != (pAttr = EnumStackAttribute(pAttr, TRUE)))
                {
                    rgAttr[cAttr] = *pAttr;
                    cAttr++;
                }
                rgAttributes.rgAttr = rgAttr;
                rgAttributes.cAttr = cAttr;
            }

            if (NULL != m_PrivateKeyArchiveCertificate)
            {
                hr = GetKeyArchivePKCS7(&blobKeyArchivePKCS7);
                if (S_OK != hr)
                {
                    goto GetKeyArchivePKCS7Error;
                }
                rgUnauthAttr = (CRYPT_ATTRIBUTE*)LocalAlloc(LMEM_FIXED,
                                        sizeof(CRYPT_ATTRIBUTE));
                if (NULL == rgUnauthAttr)
                {
                    hr = E_OUTOFMEMORY;
                    goto OutOfMemoryError;
                }
                rgUnauthAttr->pszObjId = szOID_ARCHIVED_KEY_ATTR;
                rgUnauthAttr->cValue = 1;
                rgUnauthAttr->rgValue = &blobKeyArchivePKCS7;
                ++cUnauthAttr;

                //if key archival cert is set, should save the hash
                //of the encrypted private key

                hr = myCalculateKeyArchivalHash(
                            blobKeyArchivePKCS7.pbData,
                            blobKeyArchivePKCS7.cbData,
                            &blobKAHash.pbData,
                            &blobKAHash.cbData);
                if (S_OK != hr)
                {
                    goto myCalculateKeyArchivalHashError;
                }

                if (!CertSetCertificateContextProperty(
                        m_pCertContextPendingRequest, //use pending cert
                        CERT_ARCHIVED_KEY_HASH_PROP_ID,
                        0,
                        &blobKAHash))
                {
                    hr = MY_HRESULT_FROM_WIN32(GetLastError());
                    goto CertSetCertificateContextPropertyError;
                }

                hr = xeCreateKeyArchivalHashAttribute(
                            &blobKAHash,
                            &blobKAHashAttr);
                if (S_OK != hr)
                {
                    goto xeCreateKeyArchivalHashAttributeError;
                }

                //add this attribute into the array
                rgAttr[cAttr] = attrKAHash;
                cAttr++;
                rgAttributes.rgAttr = rgAttr;
                rgAttributes.cAttr = cAttr;
            }

            //client may set m_HashAlgId but it is not guaranteed
            //GetCapiHashAndSigAlgId will determine which one
            //is actually used
            if (!GetCapiHashAndSigAlgId(rgAlg))
            {
                hr = NTE_BAD_ALGID;
                goto GetCapiHashAndSigAlgIdError;
            }
            pOidInfo = xeCryptFindOIDInfo(
                            CRYPT_OID_INFO_ALGID_KEY,
                            (void*)rgAlg, //point to rgAlg[0]
                            CRYPT_HASH_ALG_OID_GROUP_ID);
            if (NULL == pOidInfo)
            {
                goto xeCryptFindOIDInfoError;
            }

            if (NULL != m_pCertContextRenewal)
            {
                pCertSigner = m_pCertContextRenewal;
            }
            if (NULL != m_pCertContextSigner)
            {
                pCertSigner = m_pCertContextSigner;
            }
            if (NULL != pCertSigner)
            {
                //get signer key prov info
                hr = GetKeyProvInfoFromCert(
                                pCertSigner,
                                &dwKeySpecSigner,
                                &hProvSigner);
                if (S_OK != hr)
                {
                    goto GetKeyProvInfoFromCertError;
                }
            }

            //this is CMC, honor anyway
            if (m_fIncludeSubjectKeyID)
            {
                hr = myGetPublicKeyHash(
                            NULL,
                            m_pPublicKeyInfo,
                            &pbSubjectKeyHash,
                            &cbSubjectKeyHash);
                if (S_OK != hr)
                {
                    goto myGetPublicKeyHashError;
                }
            }
            hRequestProv = GetProv(0);
            if (NULL == hRequestProv)
            {
                hr = MY_HRESULT_FROM_WIN32(GetLastError());
                goto GetProvError;
            }

            //ok, now call cmc create
            hr = BuildCMCRequest(
                        m_lClientId,
                        FALSE,       //fNestedCMCRequest
                        blobPKCS10.pbData,
                        blobPKCS10.cbData,
                        rgExt,
                        cExt,
                        (0 != cAttr) ? &rgAttributes : NULL,
                        (0 != cAttr) ? 1 : 0,
                        rgUnauthAttr,
                        cUnauthAttr,
                        pbSubjectKeyHash,
                        cbSubjectKeyHash,
                        hRequestProv,
                        m_keyProvInfo.dwKeySpec,
                        pOidInfo->pszOID,
                        pCertSigner,
                        hProvSigner,
                        dwKeySpecSigner,
                        NULL, //pOidInfo->pszOID, //this seems to me not necessary because we passed the cert context
                        &pblobRequest->pbData,
                        &pblobRequest->cbData);
            if (S_OK != hr)
            {
                goto BuildCMCRequestError;
            }
        }
        break;

        case XECR_PKCS7:
            if ((NULL == m_pCertContextRenewal &&
                 NULL == m_pCertContextSigner) ||
                NULL != m_PrivateKeyArchiveCertificate)
            {
                //renew cert is not set, can't make it pkcs7
                //pkcs7 can't support key archival
                hr = E_INVALIDARG;
                goto InvalidArgError;
            }
            // old method will return pkcs7
            hr = createPKCS10WStr(
                        pwszDNName,
                        pwszUsage,     //wszPurpose,
                        pblobRequest);
            if(S_OK != hr)
            {
                goto createPKCS10WStrError;
            }
        break;

        case XECR_PKCS10_V1_5:
            m_fOID_V2 = FALSE;
            //fall through
        case XECR_PKCS10_V2_0:

            if (NULL != m_PrivateKeyArchiveCertificate)
            {
                //pkcs10 can't support key archival
                hr = E_INVALIDARG;
                goto InvalidArgError;
            }
            m_fHonorRenew = FALSE; //avoid return pkcs7
            //for new PKCS10 we allow include subject key id extension
            m_fHonorIncludeSubjectKeyID = TRUE;
            // call old method
            hr = createPKCS10WStr(
                        pwszDNName,
                        pwszUsage,     //wszPurpose,
                        pblobRequest);
            if(S_OK != hr)
            {
                goto createPKCS10WStrError;
            }
        break;

        default:
            hr = E_INVALIDARG;
            goto InvalidArgError;
        break;
    }

    //in all cases, we called createPKCS10WStr
    if(m_wszPVKFileName[0] != 0 && !m_fUseExistingKey)
    {
        //we hold on this until possible cmc is created
        GetProv(CRYPT_DELETEKEYSET);
    }

    hr = S_OK;
ErrorReturn:
    m_fNewRequestMethod = FALSE; //critical
    m_fOID_V2 = FALSE; //critical for backward compatiability
    m_fHonorRenew = TRUE; //critical
    m_fHonorIncludeSubjectKeyID = TRUE; //critical for backward compt.
    LeaveCriticalSection(&m_csXEnroll);

    if (NULL != rgExt)
    {
        LocalFree(rgExt);
    }
    if (NULL != rgAttr)
    {
        LocalFree(rgAttr);
    }
    if (NULL != rgUnauthAttr)
    {
        LocalFree(rgUnauthAttr);
    }
    if (NULL != blobKeyArchivePKCS7.pbData)
    {
        MyCoTaskMemFree(blobKeyArchivePKCS7.pbData);
    }
    if (NULL != blobPKCS10.pbData)
    {
        MyCoTaskMemFree(blobPKCS10.pbData);
    }
    if (NULL != hProvSigner)
    {
        CryptReleaseContext(hProvSigner, 0);
    }
    if (NULL != pbSubjectKeyHash)
    {
        LocalFree(pbSubjectKeyHash);
    }
    if (NULL != blobKAHash.pbData)
    {
        LocalFree(blobKAHash.pbData);
    }
    if (NULL != blobKAHashAttr.pbData)
    {
        LocalFree(blobKAHashAttr.pbData);
    }
    return hr;

TRACE_ERROR(AccessDeniedError);
TRACE_ERROR(createPKCS10WStrError)
TRACE_ERROR(BuildCMCRequestError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(GetCapiHashAndSigAlgIdError)
TRACE_ERROR(GetKeyArchivePKCS7Error)
TRACE_ERROR(xeCryptFindOIDInfoError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(GetKeyProvInfoFromCertError)
TRACE_ERROR(NotSupportedError)
TRACE_ERROR(GetProvError)
TRACE_ERROR(myGetPublicKeyHashError)
TRACE_ERROR(CertSetCertificateContextPropertyError)
TRACE_ERROR(myCalculateKeyArchivalHashError)
TRACE_ERROR(xeCreateKeyArchivalHashAttributeError)
}

HRESULT
CCEnroll::BlobToBstring(
    IN   CRYPT_DATA_BLOB   *pBlob,
    IN   DWORD              dwFlag,
    OUT  BSTR              *pBString)
{
    HRESULT           hr;
    WCHAR            *pwszB64;
    DWORD             cch;

    //init
    *pBString = NULL;

    // BASE64 encode blob
    pwszB64 = NULL;
    cch = 0;
    while (TRUE)
    {
        if (!MyCryptBinaryToStringW(
                pBlob->pbData,
                pBlob->cbData,
                dwFlag,
                pwszB64,
                &cch))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptBinaryToStringWError;
        }
        if (NULL != pwszB64)
        {
            //got it, done
            break;
        }
        pwszB64 = (WCHAR *)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
        if (NULL == pwszB64)
        {
            hr = E_OUTOFMEMORY;
            goto OutOfMemoryError;
        }
    }

    // SysAllocStringLen
    *pBString = SysAllocStringLen(pwszB64, cch);
    if(NULL == *pBString)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        hr = E_OUTOFMEMORY;
        goto SysAllocStringLenError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != pwszB64)
    {
        LocalFree(pwszB64);
    }
    return(hr);

TRACE_ERROR(MyCryptBinaryToStringWError)
TRACE_ERROR(SysAllocStringLenError)
TRACE_ERROR(OutOfMemoryError)
}

HRESULT
CCEnroll::BstringToBlob(
    IN  BSTR               bString,
    OUT CRYPT_DATA_BLOB   *pBlob)
{
    HRESULT  hr;

    assert(NULL != pBlob);

    //init
    pBlob->pbData = NULL;
    pBlob->cbData = 0;

    while (TRUE)
    {
        if (!MyCryptStringToBinaryW(
                    (LPCWSTR)bString,
                    SysStringLen(bString),
                    CRYPT_STRING_ANY,
                    pBlob->pbData,
                    &pBlob->cbData,
                    NULL,
                    NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto MyCryptStringToBinaryWError;
        }
        if (NULL != pBlob->pbData)
        {
            break; //done
        }
        pBlob->pbData = (BYTE*)MyCoTaskMemAlloc(pBlob->cbData);
        if (NULL == pBlob->pbData)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    hr = S_OK;
ErrorReturn:
    return(hr);

TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(MyCryptStringToBinaryWError)
}

HRESULT
CCEnroll::createRequestWStrBStr(
    IN   LONG              Flags,
    IN   LPCWSTR           pwszDNName,
    IN   LPCWSTR           pwszUsage,
    IN   DWORD             dwFlag,
    OUT  BSTR __RPC_FAR   *pbstrRequest)
{
    HRESULT           hr;
    CRYPT_DATA_BLOB   blobRequest;

    memset(&blobRequest, 0, sizeof(blobRequest));

    hr = createRequestWStr(Flags, pwszDNName, pwszUsage, &blobRequest);
    if (S_OK != hr)
    {
        goto createRequestWStrError;
    }

    // convert to bstr
    hr = BlobToBstring(&blobRequest, dwFlag, pbstrRequest);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != blobRequest.pbData)
    {
        MyCoTaskMemFree(blobRequest.pbData);
    }
    return(hr);

TRACE_ERROR(createRequestWStrError)
TRACE_ERROR(BlobToBstringError)
}

HRESULT
CCEnroll::BStringToFile(
    IN BSTR         bString,
    IN LPCWSTR      pwszFileName)
{
    HRESULT hr;
    HANDLE  hFile = NULL;
    DWORD   cb = 0;
    LPSTR   sz = NULL;
    size_t  nLength; 

    sz = MBFromWide(bString);
    if(NULL == sz)
    {
        hr = E_OUTOFMEMORY;
        goto MBFromWideError;
    }
    
    nLength = strlen(sz); 
    if (nLength > (DWORD)-1)
	goto InvalidArgError; 

    
    // open the file
    hFile = CreateFileSafely(pwszFileName);
    if (NULL == hFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CreateFileFileSafelyError;
    }

    // write the pkcs10
    if(!WriteFile(
        hFile,
        sz,
	(DWORD)nLength, 
        &cb,
        NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto WriteFileError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != hFile)
    {
        CloseHandle(hFile);
    }
    if(NULL != sz)
    {
        MyCoTaskMemFree(sz);
    }
    return(hr);

TRACE_ERROR(CreateFileFileSafelyError)
SET_HRESULT(InvalidArgError, E_INVALIDARG);
TRACE_ERROR(MBFromWideError)
TRACE_ERROR(WriteFileError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFileRequestWStr(
    IN   LONG        Flags,
    IN   LPCWSTR     pwszDNName,
    IN   LPCWSTR     pwszUsage,
    IN   LPCWSTR     pwszRequestFileName)
{
    HRESULT hr;
    BSTR    bstrRequest = NULL;

    // get the Request
    hr = createRequestWStrBStr(
                Flags,
                pwszDNName,
                pwszUsage,
                CRYPT_STRING_BASE64REQUESTHEADER,
                &bstrRequest);
    if(S_OK != hr)
    {
        goto createRequestWStrBStrError;
    }

    // save it to file
    hr = BStringToFile(bstrRequest, pwszRequestFileName);
    if (S_OK != hr)
    {
        goto BStringToFileError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != bstrRequest)
    {
        SysFreeString(bstrRequest);
    }
    return(hr);

TRACE_ERROR(createRequestWStrBStrError)
TRACE_ERROR(BStringToFileError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptResponseBlob(
    IN   PCRYPT_DATA_BLOB   pblobResponse)
{
    HRESULT hr_old = S_OK;
    HRESULT hr;
    XCMCRESPONSE *prgResponse = NULL;
    DWORD         cResponse = 0;

    EnterCriticalSection(&m_csXEnroll);

    //check in parameter
    if (NULL == pblobResponse)
    {
        hr = E_POINTER;
        goto NullPointerError;
    }
    if (NULL == pblobResponse->pbData ||
        0 == pblobResponse->cbData)
    {
        hr = E_INVALIDARG;
        goto InvalidArgError;
    }

    //make sure init archived key hash
    ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));

    hr_old = ParseCMCResponse(
                pblobResponse->pbData,
                pblobResponse->cbData,
                NULL,
                &prgResponse,
                &cResponse);
    //note, if for any reasons above failed, try pkcs7
    if (S_OK == hr_old)
    {
        if (1 < cResponse)
        {
            //not supported yet
            hr = MY_HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            goto NotSupportedError;
        }
#if DBG
        //make sure not zero, should be 1
        assert(1 == cResponse);
#endif //DBG

        //check response status
        if (CMC_STATUS_SUCCESS != prgResponse->StatusInfo.dwStatus)
        {
            hr = prgResponse->StatusInfo.dwStatus; //take status error
            goto CMCResponseStatusError;
        }

        //some code here to get encrypted archived key hash from the response
        //and make m_blobResponseKAHash point to the hash data
        if (NULL != prgResponse->pbEncryptedKeyHash)
        {
            m_blobResponseKAHash.pbData = prgResponse->pbEncryptedKeyHash;
            m_blobResponseKAHash.cbData = prgResponse->cbEncryptedKeyHash;
        }
    }

    //note, hr_old may not be S_OK, accept the response as pkcs7
    hr = acceptPKCS7Blob(pblobResponse);
    if (S_OK != hr)
    {
        if (S_OK != hr_old)
        {
            //return old error instead of new one
            hr = hr_old;
        }
        goto acceptPKCS7BlobError;
    }

    hr = S_OK;
ErrorReturn:
    //reset hash to zero
    ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));
    LeaveCriticalSection(&m_csXEnroll);
    if (NULL != prgResponse)
    {
        FreeCMCResponse(prgResponse, cResponse);
    }
    return hr;

TRACE_ERROR(acceptPKCS7BlobError)
TRACE_ERROR(CMCResponseStatusError)
TRACE_ERROR(NotSupportedError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(NullPointerError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::acceptFileResponseWStr(
    IN   LPCWSTR     pwszResponseFileName)
{
    HRESULT     hr;
    CRYPT_DATA_BLOB  blob;

    ZeroMemory(&blob, sizeof(blob));

    hr = xeStringToBinaryFromFile(
                pwszResponseFileName,
                &blob.pbData,
                &blob.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    // accept the blob
    hr = acceptResponseBlob(&blob);

ErrorReturn:
    if (NULL != blob.pbData)
    {
        LocalFree(blob.pbData);
    }
    return(hr);

TRACE_ERROR(xeStringToBinaryFromFileError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertContextFromResponseBlob(
    IN   PCRYPT_DATA_BLOB   pblobResponse,
    OUT  PCCERT_CONTEXT    *ppCertContext)
{
    HRESULT hr;

    if (NULL == ppCertContext)
    {
        hr = E_POINTER;
        goto NullPointerError;
    }

    //???should check response status?

    //response is already in pkcs7
    hr = GetEndEntityCert(pblobResponse, FALSE, ppCertContext);
    if (S_OK != hr)
    {
        goto GetEndEntityCertError;
    }

    hr = S_OK;
ErrorReturn:
    return hr;

TRACE_ERROR(NullPointerError)
TRACE_ERROR(GetEndEntityCertError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::getCertContextFromFileResponseWStr(
    IN   LPCWSTR          pwszResponseFileName,
    OUT  PCCERT_CONTEXT  *ppCertContext)
{
    HRESULT hr;
    CRYPT_DATA_BLOB blobResponse;

    ZeroMemory(&blobResponse, sizeof(blobResponse));

    // could be any form, binary or base64
    hr = xeStringToBinaryFromFile(
                pwszResponseFileName,
                &blobResponse.pbData,
                &blobResponse.cbData,
                CRYPT_STRING_ANY);
    if (S_OK != hr)
    {
        goto xeStringToBinaryFromFileError;
    }

    hr = getCertContextFromResponseBlob(
                &blobResponse,
                ppCertContext);
    if (S_OK != hr)
    {
        goto getCertContextFromResponseBlobError;
    }

    hr = S_OK;
ErrorReturn:
    if (NULL != blobResponse.pbData)
    {
        LocalFree(blobResponse.pbData);
    }
    return hr;

TRACE_ERROR(xeStringToBinaryFromFileError)
TRACE_ERROR(getCertContextFromResponseBlobError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createPFXWStr(
    IN   LPCWSTR           pwszPassword,
    OUT  PCRYPT_DATA_BLOB  pblobPFX)
{
    HRESULT hr;
    HCERTSTORE hMemStore = NULL;
    DWORD i;
    CERT_CHAIN_CONTEXT const *pCertChainContext = NULL;
    CERT_CHAIN_PARA CertChainPara;
    CERT_SIMPLE_CHAIN *pSimpleChain;

    EnterCriticalSection(&m_csXEnroll);

    if (NULL == pblobPFX)
    {
        goto EPointerError;
    }

    if (NULL == m_pCertContextStatic)
    {
        hr = E_UNEXPECTED;
        goto UnexpectedError;
    }

    // create a memory store for cert and chain
    hMemStore = CertOpenStore(
                    CERT_STORE_PROV_MEMORY,
                    X509_ASN_ENCODING,
                    NULL,
                    0,
                    NULL);
    if (NULL == hMemStore)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CertOpenStoreError;
    }

    ZeroMemory(&CertChainPara, sizeof(CertChainPara));
    CertChainPara.cbSize = sizeof(CertChainPara);

    // try to build cert and chain
    if (!MyCertGetCertificateChain(
                HCCE_CURRENT_USER,
                m_pCertContextStatic,
                NULL,
                NULL,
                &CertChainPara,
                0,
                NULL,
                &pCertChainContext))
    {
        //use 1st hr error
        hr = MY_HRESULT_FROM_WIN32(GetLastError());

        //try local machine
        if (!MyCertGetCertificateChain(
                    HCCE_LOCAL_MACHINE,
                    m_pCertContextStatic,
                    NULL,
                    NULL,
                    &CertChainPara,
                    0,
                    NULL,
                    &pCertChainContext))
        {
            //still use 1st hr
            goto MyCertGetCertificateChainError;
        }
    }

    // make sure there is at least 1 simple chain
    if (0 == pCertChainContext->cChain)
    {
        hr = MY_HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
        goto NoCertificateChainError;
    }

    //add chain to memory store
    pSimpleChain = pCertChainContext->rgpChain[0];
    for (i = 0; i < pSimpleChain->cElement; i++)
    {
        if (!CertAddCertificateContextToStore(
                hMemStore,
                pSimpleChain->rgpElement[i]->pCertContext,
                CERT_STORE_ADD_REPLACE_EXISTING,
                NULL))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto CertAddCertificateContextToStoreError;
        }
    }

    pblobPFX->pbData = NULL;
    while (TRUE)
    {
        if (!PFXExportCertStore(
                hMemStore,
                pblobPFX,
                pwszPassword,
                EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))
        {
            hr = MY_HRESULT_FROM_WIN32(GetLastError());
            goto PFXExportCertStoreError;
        }
        if (NULL != pblobPFX->pbData)
        {
            //got it, done
            break;
        }
        pblobPFX->pbData = (BYTE*)MyCoTaskMemAlloc(pblobPFX->cbData);
        if (NULL == pblobPFX->pbData)
        {
            hr = E_OUTOFMEMORY;
            goto MyCoTaskMemAllocError;
        }
    }

    hr = S_OK;
ErrorReturn:
    LeaveCriticalSection(&m_csXEnroll);
    if (pCertChainContext != NULL)
    {
        MyCertFreeCertificateChain(pCertChainContext);
    }
    if (NULL != hMemStore)
    {
        CertCloseStore(hMemStore, CERT_CLOSE_STORE_CHECK_FLAG);
    }
    return hr;


TRACE_ERROR(UnexpectedError)
TRACE_ERROR(CertOpenStoreError)
TRACE_ERROR(PFXExportCertStoreError)
TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(CertAddCertificateContextToStoreError)
TRACE_ERROR(NoCertificateChainError)
TRACE_ERROR(MyCertGetCertificateChainError)
SET_HRESULT(EPointerError, E_POINTER)
}

HRESULT
CCEnroll::createPFXWStrBStr( 
    IN  LPCWSTR         pwszPassword,
    OUT BSTR __RPC_FAR *pbstrPFX)
{
    HRESULT           hr;
    CRYPT_DATA_BLOB   blobPFX;

    memset(&blobPFX, 0, sizeof(CRYPT_DATA_BLOB));

    hr = createPFXWStr(pwszPassword, &blobPFX);
    if (S_OK != hr)
    {
        goto createPFXWStrError;
    }

    // convert pfx to bstr
    hr = BlobToBstring(&blobPFX, CRYPT_STRING_BASE64, pbstrPFX);
    if (S_OK != hr)
    {
        goto BlobToBstringError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != blobPFX.pbData)
    {
        MyCoTaskMemFree(blobPFX.pbData);
    }
    return(hr);

TRACE_ERROR(createPFXWStrError)
TRACE_ERROR(BlobToBstringError)
}

HRESULT STDMETHODCALLTYPE 
CCEnroll::createFilePFXWStr(
    IN   LPCWSTR     pwszPassword,
    IN   LPCWSTR     pwszPFXFileName)
{
    HRESULT hr;
    HANDLE  hFile = NULL;
    DWORD   cb = 0;
    CRYPT_DATA_BLOB   blobPFX;

    memset(&blobPFX, 0, sizeof(CRYPT_DATA_BLOB));

    hr = createPFXWStr(pwszPassword, &blobPFX);
    if (S_OK != hr)
    {
        goto createPFXWStrError;
    }

    // open the file
    hFile = CreateFileSafely(pwszPFXFileName);
    if (NULL == hFile)
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto CreateFileFileSafelyError;
    }

    // write the pkcs10
    if(!WriteFile(
        hFile,
        blobPFX.pbData,
        blobPFX.cbData,
        &cb,
        NULL))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError());
        goto WriteFileError;
    }

    hr = S_OK;
ErrorReturn:
    if(NULL != hFile)
    {
        CloseHandle(hFile);
    }
    if(NULL != blobPFX.pbData)
    {
        MyCoTaskMemFree(blobPFX.pbData);
    }
    return(hr);

TRACE_ERROR(createPFXWStrError)
TRACE_ERROR(CreateFileFileSafelyError)
TRACE_ERROR(WriteFileError)
}

HRESULT STDMETHODCALLTYPE
CCEnroll::setPendingRequestInfoWStr(
    IN   LONG     lRequestID,
    IN   LPCWSTR  pwszCADNS,
    IN   LPCWSTR  pwszCAName,
    IN   LPCWSTR  pwszFriendlyName
    )
{

    //------------------------------------------------------------
    //
    // Define locally-scoped helper functions: 
    //
    //------------------------------------------------------------

    CEnrollLocalScope(SetPendingRequestInfoHelper): 
        // Finds the appropriate cert context to set pending info on using the following algorithm:
        //   1) If a hash value HAS NOT been specified, use the cached cert request.  
        //   2) If a hash value HAS been specified, search the request store for a cert with an equivalent
        //      hash value and return it.  If no such cert can be found, return an error code. 
        HRESULT GetPendingRequestCertContext(IN  HCERTSTORE       hStoreRequest,
                                             IN  CRYPT_DATA_BLOB  hashBlob, 
                                             IN  PCCERT_CONTEXT   pCertContextCachedPendingRequest,
                                             OUT PCCERT_CONTEXT  *pCertContextPendingRequest)
        {
            EquivalentHashCertContextFilter filter(hashBlob); 
            
            if (hashBlob.pbData == NULL)
            {
                // We haven't specified a particular context, use the one we've cached. 
                *pCertContextPendingRequest = CertDuplicateCertificateContext(pCertContextCachedPendingRequest); 
                return S_OK; 
            }
            else
            {
                // Returns the first certificate in the request store with a hash matching 
                // pHashBlob.  
                return FilteredCertEnumCertificatesInStore
                    (hStoreRequest, NULL, &filter, pCertContextPendingRequest); 
            }
        }


        DWORD   GetPendingInfoBlobSize(IN  LONG              lRequestID,
                                       IN  LPCWSTR           pwszCADNS,
                                       IN  LPCWSTR           pwszCAName, 
                                       IN  LPCWSTR           pwszFriendlyName)
        {
            assert(pwszCADNS != NULL && pwszCAName != NULL && pwszFriendlyName != NULL); 
            
            return  (DWORD)(sizeof(lRequestID) +                            // Request ID
                     sizeof(DWORD) +                                 // wcslen(pwszCADNS)
                     sizeof(WCHAR) * (wcslen(pwszCADNS) + 1) +       // pwszCADNS
                     sizeof(DWORD) +                                 // wcslen(pwszCAName)
                     sizeof(WCHAR) * (wcslen(pwszCAName) + 1) +      // pwszCAName
                     sizeof(DWORD) +                                 // wcslen(pwszFriendlyName)
                     sizeof(WCHAR) * (wcslen(pwszFriendlyName) + 1)  // pwszFriendlyName
                     ); 
        }


        // Combines the supplied pending request information into a CRYPT_DATA_BLOB 
        // See wincrypt.h for the format.  
        void MakePendingInfoBlob(IN  LONG              lRequestID,
                                 IN  LPCWSTR           pwszCADNS,
                                 IN  LPCWSTR           pwszCAName, 
                                 IN  LPCWSTR           pwszFriendlyName, 
                                 OUT CRYPT_DATA_BLOB   pendingInfoBlob)
        {
            LPBYTE  pbBlob; 

            // None of the inputs should be NULL. 
            assert(pwszCADNS != NULL && pwszCAName != NULL && pwszFriendlyName != NULL); 

            // Declare an array of the strings we wish to write to the pending info blob
            struct StringsToWrite { 
                DWORD    cc; 
                LPCWSTR  pwsz;
            } rgStrings[] = { 
                { (DWORD)wcslen(pwszCADNS)        + 1, pwszCADNS         }, 
                { (DWORD)wcslen(pwszCAName)       + 1, pwszCAName        }, 
                { (DWORD)wcslen(pwszFriendlyName) + 1, pwszFriendlyName  }
            }; 

            // Write the request ID to the blob
            pbBlob = pendingInfoBlob.pbData; 
            memcpy(pbBlob, &lRequestID, sizeof(lRequestID)); 
            pbBlob += sizeof(lRequestID); 

            // Write all strings to the blob
            for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(rgStrings); dwIndex++) 
            { 
                memcpy(pbBlob, &rgStrings[dwIndex].cc, sizeof(rgStrings[dwIndex].cc)); 
                pbBlob += sizeof(rgStrings[dwIndex].cc);
                memcpy(pbBlob, rgStrings[dwIndex].pwsz, rgStrings[dwIndex].cc * sizeof(WCHAR)); 
                pbBlob += rgStrings[dwIndex].cc * sizeof(WCHAR); 
            }

            assert(pbBlob == (pendingInfoBlob.pbData + pendingInfoBlob.cbData)); 
        }
    CEnrollEndLocalScope; 

    //------------------------------------------------------------ 
    //
    // Begin procedure body.
    //
    //------------------------------------------------------------

    CRYPT_DATA_BLOB  pendingInfoBlob;
    HCERTSTORE       hStoreRequest; 
    HRESULT          hr                         = S_OK; 
    PCCERT_CONTEXT   pCertContextPendingRequest = NULL; 

    ZeroMemory(&pendingInfoBlob, sizeof(pendingInfoBlob)); 

    EnterCriticalSection(&m_csXEnroll); 

    // Input validation: 
    if (lRequestID < 0 || pwszCADNS == NULL || pwszCAName == NULL)
        goto InvalidArgErr; 

    // NULL is a valid value for pwszFriendlyName.  If friendly name is NULL, replace with the empty string: 
    if (pwszFriendlyName == NULL) { pwszFriendlyName = L""; }

    if (NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
        goto GetStoreErr; 

    // Use our locally-scoped helper function to acquire the appropriate certificate context. 
    if (S_OK != (hr = local.GetPendingRequestCertContext
                 (hStoreRequest,
                  m_hashBlobPendingRequest, 
                  m_pCertContextPendingRequest,
                  &pCertContextPendingRequest)))
        goto GetPendingRequestCertContextErr; 

    // Allocate memory for our pending info blob: 
    pendingInfoBlob.cbData  = local.GetPendingInfoBlobSize
        (lRequestID,
         pwszCADNS,
         pwszCAName,
         pwszFriendlyName); 
    pendingInfoBlob.pbData  = (LPBYTE)LocalAlloc(LPTR, pendingInfoBlob.cbData); 
    if (NULL == pendingInfoBlob.pbData)
        goto MemoryErr; 

    // Combine our arguments into a "pending info" blob.
    local.MakePendingInfoBlob
        (lRequestID, 
         pwszCADNS, 
         pwszCAName,
         pwszFriendlyName,
         pendingInfoBlob);
                  
    // Use our pending info blob to assign the certificate context property. 
    if (!CertSetCertificateContextProperty
        (pCertContextPendingRequest, 
         CERT_ENROLLMENT_PROP_ID, 
         0,
         &pendingInfoBlob))
    {
        // Failed to set the context property. 
        goto CertSetCertificateContextPropertyErr; 
    }

    // We've completed successfully. 
    hr = S_OK;   

 CommonReturn: 
    if (NULL != pendingInfoBlob.pbData)      { LocalFree(pendingInfoBlob.pbData); }
    if (NULL != pCertContextPendingRequest)  { CertFreeCertificateContext(pCertContextPendingRequest); } 

    LeaveCriticalSection(&m_csXEnroll); 
    return hr; 

 ErrorReturn:
    goto CommonReturn; 

SET_HRESULT(CertSetCertificateContextPropertyErr,  MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(GetPendingRequestCertContextErr,       hr); 
SET_HRESULT(GetStoreErr,                           MY_HRESULT_FROM_WIN32(GetLastError())); 
SET_HRESULT(InvalidArgErr,                         E_INVALIDARG); 
SET_HRESULT(MemoryErr,                             E_OUTOFMEMORY); 
} 

//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCEnroll::enumPendingRequestWStr(
    IN  LONG   lIndex,
    IN  LONG   lDesiredProperty,
    OUT LPVOID ppProperty
    )
{

    //------------------------------------------------------------
    //
    // Define locally scoped helper functions.
    //
    //------------------------------------------------------------

    CEnrollLocalScope(EnumPendingRequestHelper): 
        CRYPT_DATA_BLOB dataBlob;
    
        HRESULT GetContextPropertySimple(PCCERT_CONTEXT pCertContext, DWORD dwPropID)
        {
            BOOL fDone = FALSE;
            dataBlob.pbData = NULL; 
            dataBlob.cbData = 0x150; 

            do { 
                if (dataBlob.pbData != NULL) { LocalFree(dataBlob.pbData); } 

                dataBlob.pbData = (LPBYTE)LocalAlloc(LPTR, dataBlob.cbData);
                if (dataBlob.pbData == NULL) { return E_OUTOFMEMORY; } 

                if (!CertGetCertificateContextProperty
                    (pCertContext, 
                     dwPropID, 
                     (LPVOID)dataBlob.pbData, 
                     &(dataBlob.cbData)))
                {
                    if (GetLastError() != ERROR_MORE_DATA)
                        return MY_HRESULT_FROM_WIN32(GetLastError());
                }
                else 
                {
                    fDone = TRUE;
                }
            } while (!fDone); 
                
            return S_OK;
        }

        // Extracts the next packed string from our pending info blob. 
        // If pbString is non-NULL, it must be large enough to hold the entire string.  
        LPBYTE GetNextString(IN LPBYTE pbBlob, OUT DWORD *pcbSize, OUT LPBYTE pbString) { 
            DWORD dwSize; 

            memcpy(&dwSize, pbBlob, sizeof(DWORD)); 
            dwSize *= sizeof(WCHAR); // Convert to count in bytes. 
            if (NULL != pcbSize) { 
                *pcbSize = dwSize; 
            }
            pbBlob += sizeof(DWORD); 

            if (NULL != pbString) { 
                memcpy(pbString, pbBlob, dwSize); 
            }
            pbBlob += dwSize;

            return pbBlob; 
        }

    
        HRESULT getRequestID(PCCERT_CONTEXT pCertContext, long *pplProperty) {
            HRESULT hr; 
            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
                *pplProperty = *((long *)dataBlob.pbData); 
            return hr;
        }
        
        HRESULT getCAName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            DWORD    dwSize; 
            HRESULT  hr; 
            LPBYTE   pb;
            
            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
            { 
                pb =  dataBlob.pbData + sizeof(DWORD);  // pb points to DNS Name blob
                pb =  GetNextString(pb, NULL, NULL);    // pb points to CA Name blob
                GetNextString(pb, &dwSize, NULL);       // dwSize = size in chars of CA Name
                
                // If pbData is NULL, we're just doing a size check. 
                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dwSize)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        GetNextString(pb, NULL, pDataBlobProperty->pbData); 
                    }   
                }

                pDataBlobProperty->cbData = dwSize; 
            }
            
            return hr; 
        }
        
        HRESULT getCADNSName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            DWORD    dwSize; 
            HRESULT  hr; 
            LPBYTE   pb;
            
            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
            { 
                pb = dataBlob.pbData + sizeof(DWORD); // pb points to DNS Name blob
                GetNextString(pb, &dwSize, NULL);     // dwSize = size in chars of CA Name
                
                // If pbData is NULL, we're just doing a size check. 
                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dwSize)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        GetNextString(pb, NULL, pDataBlobProperty->pbData); 
                    }   
                }

                pDataBlobProperty->cbData = dwSize; 
            }
            
            return hr; 
        }
        
        HRESULT getCAFriendlyName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            DWORD    dwSize; 
            HRESULT  hr; 
            LPBYTE   pb;
            
            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID))) 
            { 
                // Set pb to point to the start of the CA name blob
                pb =  dataBlob.pbData + sizeof(DWORD);        // pb points to DNS Name blob
                pb =  GetNextString(pb, NULL, NULL);          // pb points to CA Name blob
                pb =  GetNextString(pb, NULL, NULL);          // pb points to Friendly Name blob

                // dwSize <-- size in chars of CA Name
                GetNextString(pb, &dwSize, NULL);
                
                // If pbData is NULL, we're just doing a size check. 
                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dwSize)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        GetNextString(pb, NULL, pDataBlobProperty->pbData); 
                    }   
                }

                pDataBlobProperty->cbData = dwSize; 
            }

            return hr; 
        }

        HRESULT getHash(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) { 
            HRESULT hr;

            if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_HASH_PROP_ID)))
            {
                // If pbData is NULL, we're just doing a size check. 
                if (pDataBlobProperty->pbData != NULL)
                {
                    if (pDataBlobProperty->cbData < dataBlob.cbData)
                    {
                        hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA); 
                    }
                    else
                    {
                        memcpy(pDataBlobProperty->pbData, dataBlob.pbData, dataBlob.cbData); 
                    }
                }
                
                pDataBlobProperty->cbData = dataBlob.cbData; 
            }

            return hr; 
        }
        
        HRESULT getDate(PCCERT_CONTEXT pCertContext, PFILETIME pftProperty) { 
            *pftProperty = pCertContext->pCertInfo->NotAfter; 
            return S_OK; 
        } 

        HRESULT getTemplateName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty)   { 
            CERT_NAME_VALUE   *pCertTemplateNameValue = NULL;
            DWORD             cbCertTemplateNameValue; 
            DWORD             cbRequired = 0; 
            HRESULT           hr                      = S_OK; 
            PCERT_EXTENSION   pCertTemplateExtension  = NULL; 

            if (NULL == (pCertTemplateExtension = CertFindExtension
                         (szOID_ENROLL_CERTTYPE_EXTENSION,
                          pCertContext->pCertInfo->cExtension,
                          pCertContext->pCertInfo->rgExtension)))
                return E_INVALIDARG; 

            if (!CryptDecodeObject
                (pCertContext->dwCertEncodingType,
                 X509_UNICODE_ANY_STRING,
                 pCertTemplateExtension->Value.pbData,
                 pCertTemplateExtension->Value.cbData,
                 0,
                 NULL, 
                 &cbCertTemplateNameValue) || (cbCertTemplateNameValue == 0))
                goto CryptDecodeObjectErr; 
                
            pCertTemplateNameValue = (CERT_NAME_VALUE *)LocalAlloc(LPTR, cbCertTemplateNameValue); 
            if (NULL == pCertTemplateNameValue)
                goto MemoryErr; 

            if (!CryptDecodeObject
                (pCertContext->dwCertEncodingType,
                 X509_UNICODE_ANY_STRING,
                 pCertTemplateExtension->Value.pbData,
                 pCertTemplateExtension->Value.cbData,
                 0,
                 (void *)(pCertTemplateNameValue), 
                 &cbCertTemplateNameValue))
                goto CryptDecodeObjectErr; 

            cbRequired = sizeof(WCHAR) * (DWORD)(wcslen((LPWSTR)(pCertTemplateNameValue->Value.pbData)) + 1);
            if (NULL != pDataBlobProperty->pbData)
            {
                // Make sure we've allocated a large enough buffer: 
                if (pDataBlobProperty->cbData < cbRequired) { goto MoreDataErr; } 

                // Write the template name to the OUT param: 
                wcscpy((LPWSTR)pDataBlobProperty->pbData, (LPWSTR)(pCertTemplateNameValue->Value.pbData)); 
            }
                    
            hr = S_OK;
        CommonReturn: 
            // Assign the size of the template name to the cb of the OUT param.  
            // This should be done for all code paths.
            pDataBlobProperty->cbData = cbRequired; 
            
            // Free resources: 
            if (NULL != pCertTemplateNameValue) { LocalFree(pCertTemplateNameValue); }
            return hr; 

        ErrorReturn: 
            goto CommonReturn; 

        SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
        SET_HRESULT(MoreDataErr,   MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA)); 
        SET_HRESULT(MemoryErr,     E_OUTOFMEMORY); 
        }

	HRESULT getTemplateOID(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty)    { 
            CERT_TEMPLATE_EXT  *pCertTemplateExt    = NULL; 
	    DWORD               cbCertTemplateExt   = 0; 
            DWORD               cbRequired = 0; 
	    HRESULT             hr;
            LPWSTR              pwszOID             = NULL; 
	    PCERT_EXTENSION     pCertExtension      = NULL; 
            
 	    if (NULL == (pCertExtension = CertFindExtension
			 (szOID_CERTIFICATE_TEMPLATE, 
			  pCertContext->pCertInfo->cExtension,
			  pCertContext->pCertInfo->rgExtension)))
		return E_INVALIDARG; 

            if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 NULL, 
		 &cbCertTemplateExt) || (cbCertTemplateExt == 0))
                goto CryptDecodeObjectErr;
            
	    pCertTemplateExt = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbCertTemplateExt); 
	    if (NULL == pCertTemplateExt)
		goto MemoryErr; 

	    if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 (void *)(pCertTemplateExt), 
		 &cbCertTemplateExt))
		goto CryptDecodeObjectErr;

            cbRequired = sizeof(WCHAR) * (DWORD)(strlen(pCertTemplateExt->pszObjId) + 1); 

	    // See if we're just doing a size check: 
	    if (NULL != pDataBlobProperty->pbData) 
            {
                // Make sure we've allocated a large enough buffer: 
                if (pDataBlobProperty->cbData < cbRequired) { goto MoreDataErr; }
                
                // Convert the OID to a LPWSTR:
                pwszOID = WideFromMB(pCertTemplateExt->pszObjId); 
                if (NULL == pwszOID) 
                    goto WideFromMBErr; 

                // Write the template OID to the OUT param: 
                wcscpy((LPWSTR)pDataBlobProperty->pbData, pwszOID); 
            }

            hr = S_OK; 
        CommonReturn:
            // Assign the size of the OID to the cb of the OUT param.  
            // This should be done for all code paths.
            pDataBlobProperty->cbData = cbRequired; 

            // Free resources: 
            if (NULL != pCertTemplateExt) { LocalFree(pCertTemplateExt); } 
            if (NULL != pwszOID)          { MyCoTaskMemFree(pwszOID); }

            return hr; 
        ErrorReturn:
            goto CommonReturn;

	SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
        SET_HRESULT(MemoryErr,            E_OUTOFMEMORY); 
        SET_HRESULT(MoreDataErr,          MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA)); 
        SET_HRESULT(WideFromMBErr,        MY_HRESULT_FROM_WIN32(GetLastError())); 
        } 
        
        HRESULT getVersion(PCCERT_CONTEXT pCertContext, long *plVersion) {
            CERT_TEMPLATE_EXT  *pCertTemplateExt    = NULL; 
	    DWORD               cbCertTemplateExt   = 0; 
	    HRESULT             hr;
	    PCERT_EXTENSION     pCertExtension      = NULL; 
            
 	    if (NULL == (pCertExtension = CertFindExtension
			 (szOID_CERTIFICATE_TEMPLATE, 
			  pCertContext->pCertInfo->cExtension,
			  pCertContext->pCertInfo->rgExtension)))
		return E_INVALIDARG; 

            if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 NULL, 
		 &cbCertTemplateExt) || (cbCertTemplateExt == 0))
		goto CryptDecodeObjectErr;
            
	    pCertTemplateExt = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbCertTemplateExt); 
	    if (NULL == pCertTemplateExt)
		goto MemoryErr; 

	    if (FALSE == CryptDecodeObject
		(pCertContext->dwCertEncodingType,
		 X509_CERTIFICATE_TEMPLATE,
		 pCertExtension->Value.pbData,
		 pCertExtension->Value.cbData,
		 0,
		 (void *)(pCertTemplateExt), 
		 &cbCertTemplateExt))
		goto CryptDecodeObjectErr;

            *plVersion = (long)pCertTemplateExt->dwMajorVersion; 
            hr = S_OK; 
        CommonReturn:
            // Free resources: 
            if (NULL != pCertTemplateExt) { LocalFree(pCertTemplateExt); } 

            return hr; 
        ErrorReturn:
            goto CommonReturn;

	SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError())); 
        SET_HRESULT(MemoryErr,            E_OUTOFMEMORY); 
        }

        void InitLocalScope() { 
            dataBlob.cbData = 0;
            dataBlob.pbData = NULL;
        }

        void FreeLocalScope() { if (dataBlob.pbData != NULL) { LocalFree(dataBlob.pbData); } }

    CEnrollEndLocalScope;
        
    //------------------------------------------------------------
    //
    // Begin procedure body.
    //
    //------------------------------------------------------------

    // FIXME: index is 0 based, correct?
    // m_dwLastPendingRequestIndex = 0; 
    // m_pCertContextLastEnumerated

    HCERTSTORE                hStoreRequest     = NULL;
    HRESULT                   hr                = S_OK; 
    PCCERT_CONTEXT            pCertContext      = NULL;

    // Input validiation:
    if (lIndex != XEPR_ENUM_FIRST && (lIndex < 0 || (ppProperty == NULL)))
        return E_INVALIDARG; 

    if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
	return E_ACCESSDENIED; 

    // Init: 
    local.InitLocalScope(); 

    EnterCriticalSection(&m_csXEnroll);
    
    if ( NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
        goto ErrorCertOpenRequestStore; 

    // If we're passed the ENUM_FIRST flag, reconstruct a snapshot of the request store. 
    // 
    if (lIndex == XEPR_ENUM_FIRST)
    {
        if (NULL != this->m_pPendingRequestTable) { delete this->m_pPendingRequestTable; } 

        this->m_pPendingRequestTable = new PendingRequestTable; 
        if (NULL == this->m_pPendingRequestTable)
            goto MemoryErr; 

        if (S_OK != (hr = this->m_pPendingRequestTable->construct(hStoreRequest)))
            goto ErrorConstructPendingTable; 
        
        // All done, return.  
        goto CommonReturn; 
    }

    // We want the lIndex'th element the request store.  
    // First, ensure that the enumeration is initialized: 
    if (NULL == m_pPendingRequestTable)
        goto PointerErr; 

    // Index past the end of the table.  
    if (this->m_pPendingRequestTable->size() <= (DWORD)lIndex)
    { 
        hr = MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND);
        goto ErrorReturn; 
    }

    pCertContext = (*this->m_pPendingRequestTable)[(DWORD)lIndex]; 

    switch (lDesiredProperty)
        {
        case XEPR_REQUESTID:       hr = local.getRequestID      (pCertContext, (long *)ppProperty);             break; 
        case XEPR_CANAME:          hr = local.getCAName         (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_CAFRIENDLYNAME:  hr = local.getCAFriendlyName (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_CADNS:           hr = local.getCADNSName      (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_DATE:            hr = local.getDate           (pCertContext, (PFILETIME)ppProperty);          break; 
        case XEPR_V1TEMPLATENAME:  hr = local.getTemplateName   (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_V2TEMPLATEOID:   hr = local.getTemplateOID    (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        case XEPR_VERSION:         hr = local.getVersion        (pCertContext, (long *)ppProperty);             break; 
        case XEPR_HASH:            hr = local.getHash           (pCertContext, (PCRYPT_DATA_BLOB)ppProperty);   break; 
        default: 
            hr = E_INVALIDARG; 
        }

 CommonReturn: 
    local.FreeLocalScope(); 

    LeaveCriticalSection(&m_csXEnroll); 
    return hr; 

 ErrorReturn:
    goto CommonReturn; 

SET_HRESULT(MemoryErr, E_OUTOFMEMORY); 
SET_HRESULT(PointerErr, E_POINTER);
TRACE_ERROR(ErrorCertOpenRequestStore); 
TRACE_ERROR(ErrorConstructPendingTable);
}


HRESULT STDMETHODCALLTYPE
CCEnroll::removePendingRequestWStr
  (IN CRYPT_DATA_BLOB thumbPrintBlob
   )
{
    EquivalentHashCertContextFilter equivHashFilter(thumbPrintBlob); 
    PendingCertContextFilter        pendingCertFilter; 
    // combinedFilter now only allows PENDING requests which match the specified thumbprint. 
    CompositeCertContextFilter      combinedFilter(&equivHashFilter, &pendingCertFilter); 

    HCERTSTORE                      hStoreRequest = NULL;
    HRESULT                         hr;
    PCCERT_CONTEXT                  pCertContext  = NULL;

    EnterCriticalSection(&m_csXEnroll); 
    
    // Input validation. 
    if (NULL == thumbPrintBlob.pbData)
    {
        hr = E_INVALIDARG; 
        goto ErrorReturn; 
    }


    if ( NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
    {
        hr = E_UNEXPECTED; 
        goto ErrorReturn;
    }
    
    if (S_OK != (hr = FilteredCertEnumCertificatesInStore
                 (hStoreRequest, 
                  NULL, 
                  &combinedFilter, 
                  &pCertContext))) 
        goto ErrorReturn; 

    if (!CertDeleteCertificateFromStore(pCertContext))
    {
        hr = MY_HRESULT_FROM_WIN32(GetLastError()); 
        // CertDeleteCertificateFromStore *always* deletes the cert context. 
        pCertContext = NULL; 
        goto ErrorReturn;
    }
    
    pCertContext = NULL; 
    hr = S_OK; 
    
 CommonReturn:
    LeaveCriticalSection(&m_csXEnroll); 

    return hr; 

 ErrorReturn:
    if (pCertContext != NULL) { CertFreeCertificateContext(pCertContext); } 
    goto CommonReturn; 
}


HRESULT FilteredCertEnumCertificatesInStore(IN  HCERTSTORE          hStore, 
                                            IN  PCCERT_CONTEXT      pCertContext, 
                                            IN  CertContextFilter  *pFilter, 
                                            OUT PCCERT_CONTEXT     *pCertContextNext)
{
    BOOL           fFilterResult; 
    HRESULT        hr = S_OK; 
    PCCERT_CONTEXT pCertContextPrev = pCertContext; 

    while (NULL != (pCertContext = CertEnumCertificatesInStore(hStore, pCertContextPrev)))
    {
        if (S_OK != (hr = pFilter->accept(pCertContext, &fFilterResult)))
            return hr;

        if (fFilterResult) // We've found the next cert context in the filtered enumeration.  
        {
            *pCertContextNext = pCertContext; 
            return S_OK; 
        }
        pCertContextPrev = pCertContext; 
    }

    return MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND); 
}

static LPVOID MyLocalAlloc(ULONG cb) {
    return((LPVOID) LocalAlloc(LPTR, (UINT) cb));
}

static LPVOID MyLocalRealloc(LPVOID ptr, ULONG cb) {
    return((LPVOID) LocalReAlloc((HLOCAL) ptr, (UINT) cb, LMEM_MOVEABLE));
}

static void MyLocalFree(LPVOID ptr) {
    LocalFree((HLOCAL) ptr);
}


//
// From xtan, an explanation of the "no-COM" APIs:  
//   "In early time xenroll interface IDs were not in uuid.lib so this
//   was a convenient C interface for people to get xenroll COM interfaces without 
//   call CoCreateInstance. It is not in MSDN but it is in SDK headers. 
//   I created the same API for consistency when I created ICEnroll4."
//
void * WINAPI PGetIEnrollNoCOM(const IID &iid) {

        void *      pvoid               = NULL;
        IClassFactory * pIClassFactory  = NULL;
        HRESULT         hr                                  = S_OK;

        MyCoTaskMemAlloc        = MyLocalAlloc;
        MyCoTaskMemFree         = MyLocalFree;
        MyCoTaskMemRealloc      = MyLocalRealloc;

        if( S_OK != (hr = DllGetClassObject(CLSID_CEnroll2, IID_IClassFactory,  (void **) &pIClassFactory)) ) {
                pIClassFactory = NULL;
        }
        else if( S_OK != (hr = pIClassFactory->CreateInstance(NULL, iid, &pvoid)) ) {
        pvoid = NULL;
    }

        if(pIClassFactory != NULL) {
            pIClassFactory->Release();
            pIClassFactory = NULL;
        }

    SetLastError(hr);
        return(pvoid);
}

IEnroll * WINAPI PIEnrollGetNoCOM(void) 
{
    return( (IEnroll *) PGetIEnrollNoCOM(IID_IEnroll) );
}

IEnroll2 * WINAPI PIEnroll2GetNoCOM(void) 
{
    return( (IEnroll2 *) PGetIEnrollNoCOM(IID_IEnroll2) );
}

IEnroll4 * WINAPI PIEnroll4GetNoCOM(void) 
{
    return( (IEnroll4 *) PGetIEnrollNoCOM(IID_IEnroll4) );
}

HRESULT PendingRequestTable::resize(DWORD dwNewSize)
{
    TableElem * newTable = NULL;

    if (dwNewSize <= 0)
        return E_INVALIDARG; 

    newTable = (TableElem *)LocalAlloc(LPTR, sizeof(TableElem) * dwNewSize); 
    if (NULL == newTable)
        return E_OUTOFMEMORY; 

    if (NULL != this->table)
    {
        memcpy(newTable, this->table, this->dwElemSize * sizeof(TableElem)); 
        LocalFree(this->table);
    }

    this->dwElemSize = dwNewSize; 
    this->table      = newTable; 

    return S_OK; 
}

HRESULT PendingRequestTable::add(TableElem tePendingRequest)
{
    HRESULT hr; 

    if        (this->dwElemCount >  this->dwElemSize) { return E_UNEXPECTED; } 
    else if   (this->dwElemCount == this->dwElemSize) 
    { 
        // Need to allocate more memory: 
        DWORD dwNewSize = this->dwElemSize < 100 ? 100 : this->dwElemSize * 2; 

        if (S_OK != (hr = this->resize(dwNewSize)))
            return hr; 
    }
        
    this->table[this->dwElemCount++] = tePendingRequest; 
    return S_OK; 
}

PendingRequestTable::PendingRequestTable() : table(NULL), dwElemSize(0), dwElemCount(0)
{ }

PendingRequestTable::~PendingRequestTable()
{
    if (NULL != this->table)
    {
        for (DWORD dwIndex = 0; dwIndex < dwElemCount; dwIndex++)
        {
            CertFreeCertificateContext(this->table[dwIndex].pCertContext); 
        }

        LocalFree(this->table);
    }
}
        
        

HRESULT PendingRequestTable::construct(HCERTSTORE hStore)
{
    HRESULT                  hr                 = S_OK; 
    PendingCertContextFilter pendingFilter; 
    PCCERT_CONTEXT           pCertContext       = NULL;
    PCCERT_CONTEXT           pCertContextPrev   = NULL;
    TableElem                tePendingRequest; 

    // Enumerate all pending cert contexts, and add them to our table: 
    for (DWORD dwIndex = 0; TRUE; dwIndex++)
    {
        if (S_OK != (hr = FilteredCertEnumCertificatesInStore
                     (hStore, 
                      pCertContextPrev, 
                      &pendingFilter, 
                      &pCertContext)))
            break; 

        tePendingRequest.pCertContext = CertDuplicateCertificateContext(pCertContext); 
        this->add(tePendingRequest); 

        pCertContextPrev = pCertContext; 
    }

    return hr == MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND) ? S_OK : hr; 
}
