//+-------------------------------------------------------------------------
//
// RdMkCert - Remote Desktop internal certificate generator
//
//            Generates NetMeeting default user certificates. The NetMeeting
//            root key and certificate are stored as a program resource.
//
// ClausGi    7/29/98 created based on MAKECERT
//
//--------------------------------------------------------------------------

#include "global.h"
#include <memtrack.h>

#ifdef DEBUG
HDBGZONE    ghDbgZone = NULL;
static PTCHAR _rgZonesNmMkCert[] = { TEXT("rdmkcert"), };
#endif /* DEBUG */

//+-------------------------------------------------------------------------
//  contants
//--------------------------------------------------------------------------

//allow max 10 extensions per certificate
#define MAX_EXT_CNT 10

//+-------------------------------------------------------------------------
//  globals
//--------------------------------------------------------------------------

WCHAR*   g_wszSubjectKey            = L"_RdMkCert";
WCHAR*     g_wszSubjectStore          = WSZNMSTORE;
DWORD     g_dwSubjectStoreFlag          = CERT_SYSTEM_STORE_CURRENT_USER;

DWORD     g_dwIssuerKeySpec          = AT_SIGNATURE;
DWORD    g_dwSubjectKeySpec         = AT_KEYEXCHANGE;

WCHAR   *g_wszSubjectDisplayName = NULL; // BUGBUG set this?

LPWSTR  g_wszIssuerProviderName   = NULL;
LPWSTR    g_wszSubjectProviderName    = NULL;

WCHAR*   g_wszSubjectX500Name;

DWORD g_dwProvType = PROV_RSA_FULL;

HMODULE    hModule=NULL;

BOOL MakeCert(DWORD dwFlags);

BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD fdwReason, LPVOID)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
        {
            hModule = hDllInst;
            ASSERT (hModule != NULL);
            DBGINIT(&ghDbgZone, _rgZonesNmMkCert);
            DisableThreadLibraryCalls (hDllInst);
            DBG_INIT_MEMORY_TRACKING(hDllInst);
            break;
        }

        case DLL_PROCESS_DETACH:
        {
            DBG_CHECK_MEMORY_TRACKING(hDllInst);
            hModule = NULL;
            break;
        }

        default:
            break;
    }
    return (TRUE);
}

//
// X.509 cert strings must be from X.208 printable character set... this
// function enforces that.
//

static const char szPrintable[] = " '()+,-./:=?\"";   // along with A-Za-z0-9

VOID MkPrintableString ( LPSTR szString )
{
    CHAR * p = szString;

    while ( *p )
    {
        if (!(('a' <= *p && *p <='z') ||
              ('A' <= *p && *p <='Z') ||
              ('0' <= *p && *p <='9') ||
              _StrChr(szPrintable,*p)))
        {
            *p = '-';
        }
        p++;
    }
}

DWORD WINAPI NmMakeCert(     LPCSTR szFirstName,
                            LPCSTR szLastName,
                            LPCSTR szEmailName,
                            LPCSTR szCity,
                            LPCSTR szCountry,
                            DWORD flags)
{
    DWORD dwRet = -1;

    WARNING_OUT(("NmMakeCert called"));

    // Form the unencoded X500 subject string. It would be nice to
    // use official constants for the below... CertRDNValueToString?

    UINT cbX500Name = ( szFirstName ? lstrlen(szFirstName) : 0 ) +
                      ( szLastName ? lstrlen(szLastName) : 0 ) +
                      ( szEmailName ? lstrlen(szEmailName) : 0 ) +
                      ( szCity ? lstrlen(szCity) : 0 ) +
                      ( szCountry ? lstrlen(szCountry) : 0 ) +
                      128; // Extra is for RDN OID strings: CN= etc.

    char * pX500Name = new char[cbX500Name];

    if ( NULL == pX500Name )
    {
        ERROR_OUT(("couldn't allocate %d bytes for x500 name", cbX500Name));
        goto cleanup;
    }

    ASSERT( ( szFirstName && *szFirstName ) || ( szLastName && *szLastName ) );

    wsprintf( pX500Name, "CN=\"%s %s\"", szFirstName ? szFirstName : "", szLastName ? szLastName : "" );

    if ( szEmailName && *szEmailName )
        wsprintf( pX500Name + lstrlen(pX500Name), ", E=\"%s\"", szEmailName );

    if ( szCity && *szCity )
        wsprintf( pX500Name + lstrlen(pX500Name), ", S=\"%s\"", szCity );

    if ( szCountry && *szCountry )
        wsprintf( pX500Name + lstrlen(pX500Name), ", C=\"%s\"", szCountry );

    MkPrintableString ( pX500Name );

    g_wszSubjectX500Name = AnsiToUnicode ( pX500Name );

    ASSERT(g_wszSubjectX500Name);

    if ( flags & NMMKCERT_F_LOCAL_MACHINE )
    {
        // We are being asked to generate a local machine cert...
        // change the subject store flag and the key container name
        g_dwSubjectStoreFlag          = CERT_SYSTEM_STORE_LOCAL_MACHINE;
        g_wszSubjectKey = L"_RdMkMchCert";
    }

    // If we're on NT5 we have to generate the cert using the
    // PROV_RSA_SCHANNEL provider, on other platforms this provider type
    // doesn't exist.

    OSVERSIONINFO       osVersion;

    ZeroMemory(&osVersion, sizeof(osVersion));
    osVersion.dwOSVersionInfoSize = sizeof(osVersion);
    GetVersionEx(&osVersion);

    if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT &&
        osVersion.dwMajorVersion >= 5)
    {
        g_dwProvType = PROV_RSA_SCHANNEL;
    }

    // Get to work and make the certificate
    if (!MakeCert(flags))
    {
        WARNING_OUT(("NmMakeCert failed."));
    }
    else
    {
        dwRet = 1;
    }

cleanup:

    if ( NULL != g_wszSubjectX500Name )
    {
        delete g_wszSubjectX500Name;
    }

    if ( NULL != pX500Name )
    {
        delete pX500Name;
    }

    return dwRet;
}


// RUNDLL entry point for certificate uninstall... the prototype is given
// by RUNDLL32.EXE requirements!
void CALLBACK NmMakeCertCleanup ( HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow )
{
    // Clean up exisint certs and private keys
    MakeCert(NMMKCERT_F_CLEANUP_ONLY);
    g_dwSubjectStoreFlag          = CERT_SYSTEM_STORE_LOCAL_MACHINE;
    g_wszSubjectKey = L"_RdMkMchCert";
    MakeCert(NMMKCERT_F_LOCAL_MACHINE|NMMKCERT_F_CLEANUP_ONLY);
}


//+=========================================================================
//  Local Support Functions
//==========================================================================

//+=========================================================================
//  MakeCert support functions
//==========================================================================

BOOL VerifyIssuerKey( IN HCRYPTPROV hProv,
        IN PCERT_PUBLIC_KEY_INFO pIssuerKeyInfo);
HCRYPTPROV GetSubjectProv(OUT LPWSTR *ppwszTmpContainer);

BOOL GetPublicKey(
    HCRYPTPROV hProv,
    PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo
    );
BOOL EncodeSubject(
    OUT BYTE **ppbEncoded,
    IN OUT DWORD *pcbEncoded
    );
BOOL CreateSpcCommonName(
    OUT BYTE **ppbEncoded,
    IN OUT DWORD *pcbEncoded
    );
BOOL CreateEnhancedKeyUsage(
    OUT BYTE **ppbEncoded,
    IN OUT DWORD *pcbEncoded
    );

BOOL    SaveCertToStore(HCRYPTPROV    hProv,
                        HCERTSTORE        hStore,
                        DWORD        dwFlag,
                        BYTE        *pbEncodedCert,
                        DWORD        cbEncodedCert,
                        LPWSTR        wszPvk,
                        DWORD        dwKeySpecification,
                        LPWSTR        wszCapiProv,
                        DWORD        dwCapiProvType);


//+-------------------------------------------------------------------------
//  Get the root's certificate from the program's resources
//--------------------------------------------------------------------------
PCCERT_CONTEXT GetRootCertContext()
{
    PCCERT_CONTEXT    pCert = NULL;
    HRSRC            hRes;

    //
    // The root certificate is stored as a resource of ours.
    // Load it...
    //
    if (0 != (hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_ROOTCERTIFICATE),
                        "CER"))) {
        HGLOBAL hglobRes;
        if (NULL != (hglobRes = LoadResource(hModule, hRes))) {
            BYTE *pbRes;
            DWORD cbRes;

            cbRes = SizeofResource(hModule, hRes);
            pbRes = (BYTE *) LockResource(hglobRes);

            if (cbRes && pbRes)
                pCert = CertCreateCertificateContext(X509_ASN_ENCODING,
                                                    pbRes, cbRes);
            if ( NULL == pCert )
            {
                DWORD dwError = GetLastError();
            }

            UnlockResource(hglobRes);
            FreeResource(hglobRes);
        }
    }

    if (pCert == NULL)
    {
        ERROR_OUT(("Error creating root cert: %x", GetLastError()));
    }
    return pCert;
}

//+-------------------------------------------------------------------------
//  Get the root's private key from the program's resources and create
//  a temporary key provider container
//--------------------------------------------------------------------------
HCRYPTPROV GetRootProv(OUT LPWSTR *ppwszTmpContainer)
{
    HCRYPTPROV        hProv = 0;
    HRSRC            hRes;
    WCHAR            wszRootSig[] = L"Root Signature";

    *ppwszTmpContainer = NULL;

    if (0 != (hRes = FindResource(hModule,MAKEINTRESOURCE(IDR_PVKROOT),"PVK")))
    {
        HGLOBAL hglobRes;
        if (NULL != (hglobRes = LoadResource(hModule, hRes))) {
            BYTE *pbRes;
            DWORD cbRes;

            cbRes = SizeofResource(hModule, hRes);
            pbRes = (BYTE *) LockResource(hglobRes);
            if (cbRes && pbRes) {
                PvkPrivateKeyAcquireContextFromMemory(
                    g_wszIssuerProviderName,
                    PROV_RSA_FULL,
                    pbRes,
                    cbRes,
                    NULL,               // hwndOwner
                    wszRootSig,
                    &g_dwIssuerKeySpec,
                    &hProv
                    );
            }
            UnlockResource(hglobRes);
            FreeResource(hglobRes);
        }
    }

    if (hProv == 0)
    {
        ERROR_OUT(("couldn't create root key provider: %x", GetLastError()));
    }
    return hProv;
}

//+-------------------------------------------------------------------------
//  Make the subject certificate. If the subject doesn't have a private
//  key, then, create.
//--------------------------------------------------------------------------
BOOL MakeCert(DWORD dwFlags)
{
    BOOL fResult;

    HCRYPTPROV        hIssuerProv = 0;
    LPWSTR            pwszTmpIssuerContainer = NULL;
    PCCERT_CONTEXT    pIssuerCertContext = NULL;
    PCERT_INFO        pIssuerCert =NULL; // not allocated

    HCRYPTPROV        hSubjectProv = 0;
    LPWSTR            pwszTmpSubjectContainer = NULL;

    PCERT_PUBLIC_KEY_INFO pSubjectPubKeyInfo = NULL;         // not allocated
    PCERT_PUBLIC_KEY_INFO pAllocSubjectPubKeyInfo = NULL;
    BYTE *pbSubjectEncoded = NULL;
    DWORD cbSubjectEncoded =0;
    BYTE *pbSpcCommonNameEncoded = NULL;
    DWORD cbSpcCommonNameEncoded =0;
    BYTE *pbCertEncoded = NULL;
    DWORD cbCertEncoded =0;
    BYTE *pbEKUEncoded = NULL;
    DWORD cbEKUEncoded = 0;

    CERT_INFO Cert;
    GUID SerialNumber;
    HCERTSTORE                hStore=NULL;

    CERT_EXTENSION rgExt[MAX_EXT_CNT];
    DWORD cExt = 0;

    CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = {
        szOID_RSA_MD5RSA, 0, 0
    };

    if (0 == (hSubjectProv = GetSubjectProv(&pwszTmpSubjectContainer)))
        goto ErrorReturn;


#define TEMP_CLEAN_CODE
#ifdef TEMP_CLEAN_CODE
    // open the system store where we used to generate certs
    hStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        hSubjectProv,
        CERT_STORE_NO_CRYPT_RELEASE_FLAG | g_dwSubjectStoreFlag,
        L"MY" );

        if ( hStore )
        {
                //
                // Delete all old certs
                //
                PCCERT_CONTEXT pCertContext = NULL;

                // Clear out any certificate(s) we may have added before
                while ( pCertContext = CertEnumCertificatesInStore(
                                                                                hStore, (PCERT_CONTEXT)pCertContext ))
                {
                        DWORD dwMagic;
                        DWORD cbMagic;

                        cbMagic = sizeof(dwMagic);

                        if (CertGetCertificateContextProperty(pCertContext,
                                CERT_FIRST_USER_PROP_ID, &dwMagic, &cbMagic) &&
                                cbMagic == sizeof(dwMagic) && dwMagic == NMMKCERT_MAGIC )
                        {
                                CertDeleteCertificateFromStore(pCertContext);
                                // Restart the enumeration
                                pCertContext = NULL;
                                continue;
                        }
                }
                CertCloseStore(hStore,0);
        }
#endif // TEMP_CLEAN_CODE

    // open a new cert store
    hStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        hSubjectProv,
        CERT_STORE_NO_CRYPT_RELEASE_FLAG | g_dwSubjectStoreFlag,
        g_wszSubjectStore);

    if(hStore==NULL)
        goto ErrorReturn;

    // Empty the store
    PCCERT_CONTEXT pCertContext;
    while ( pCertContext = CertEnumCertificatesInStore ( hStore, NULL ))
    {
        if ( !CertDeleteCertificateFromStore ( pCertContext ))
        {
            WARNING_OUT(("Failed to delete certificate: %x", GetLastError()));
            break;
        }
    }

    // If NMMKCERT_F_CLEANUP_ONLY is set, we are done
    if ( dwFlags & NMMKCERT_F_CLEANUP_ONLY )
    {
        // We've just deleted the existing certs, now delete the
        // private key container and exit.
        CryptAcquireContextU(
                &hSubjectProv,
                g_wszSubjectKey,
                g_wszSubjectProviderName,
                g_dwProvType,
                CRYPT_DELETEKEYSET |
                    ( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
                        CRYPT_MACHINE_KEYSET : 0 ));
        fResult = TRUE;
        goto CommonReturn;
    }

    //
    // Get access to the subject's (public) key, creating it if necessary
    //
    if (!GetPublicKey(hSubjectProv, &pAllocSubjectPubKeyInfo))
        goto ErrorReturn;
    pSubjectPubKeyInfo = pAllocSubjectPubKeyInfo;


    //
    // Encode the subject name
    //
    if (!EncodeSubject(&pbSubjectEncoded, &cbSubjectEncoded))
        goto ErrorReturn;

    //
    // Get access to the issuer's (private) key
    //
    hIssuerProv= GetRootProv(&pwszTmpIssuerContainer);

    if (NULL == (pIssuerCertContext = GetRootCertContext()))
        goto ErrorReturn;

    pIssuerCert = pIssuerCertContext->pCertInfo;

    if (!VerifyIssuerKey(hIssuerProv, &pIssuerCert->SubjectPublicKeyInfo))
        goto ErrorReturn;

    //
    // Update the CERT_INFO
    //
    ClearStruct(&Cert);
    Cert.dwVersion = CERT_V3;

    CoCreateGuid(&SerialNumber);
    Cert.SerialNumber.pbData = (BYTE *) &SerialNumber;
    Cert.SerialNumber.cbData = sizeof(SerialNumber);

    Cert.SignatureAlgorithm = SignatureAlgorithm;
    Cert.Issuer.pbData = pIssuerCert->Subject.pbData;
    Cert.Issuer.cbData = pIssuerCert->Subject.cbData;

    {
        SYSTEMTIME st;

        // Valid starting now...
        GetSystemTimeAsFileTime(&Cert.NotBefore);

        // Ending in 2039 (arbitrarily)
        ClearStruct(&st);
        st.wYear  = 2039;
        st.wMonth = 12;
        st.wDay   = 31;
        st.wHour  = 23;
        st.wMinute= 59;
        st.wSecond= 59;
        SystemTimeToFileTime(&st, &Cert.NotAfter);
    }

    Cert.Subject.pbData = pbSubjectEncoded;
    Cert.Subject.cbData = cbSubjectEncoded;
    Cert.SubjectPublicKeyInfo = *pSubjectPubKeyInfo;

    // Cert Extensions

    if (!CreateEnhancedKeyUsage(
            &pbEKUEncoded,
            &cbEKUEncoded))
        goto ErrorReturn;

    rgExt[cExt].pszObjId = szOID_ENHANCED_KEY_USAGE;
    rgExt[cExt].fCritical = FALSE;
    rgExt[cExt].Value.pbData = pbEKUEncoded;
    rgExt[cExt].Value.cbData = cbEKUEncoded;
    cExt++;

    if (g_wszSubjectDisplayName) {
        if (!CreateSpcCommonName(
                &pbSpcCommonNameEncoded,
                &cbSpcCommonNameEncoded))
            goto ErrorReturn;
        rgExt[cExt].pszObjId = szOID_COMMON_NAME;
        rgExt[cExt].fCritical = FALSE;
        rgExt[cExt].Value.pbData = pbSpcCommonNameEncoded;
        rgExt[cExt].Value.cbData = cbSpcCommonNameEncoded;
        cExt++;
    }

    Cert.rgExtension = rgExt;
    Cert.cExtension = cExt;

    //
    // Sign and encode the certificate
    //
    cbCertEncoded = 0;
    CryptSignAndEncodeCertificate(
        hIssuerProv,
        g_dwIssuerKeySpec,
        X509_ASN_ENCODING,
        X509_CERT_TO_BE_SIGNED,
        &Cert,
        &Cert.SignatureAlgorithm,
        NULL,                       // pvHashAuxInfo
        NULL,                       // pbEncoded
        &cbCertEncoded
        );
    if (cbCertEncoded == 0) {
        ERROR_OUT(("CryptSignAndEncodeCertificate failed: %x", GetLastError()));
        goto ErrorReturn;
    }
    pbCertEncoded = new BYTE[cbCertEncoded];
    if (pbCertEncoded == NULL) goto ErrorReturn;
    if (!CryptSignAndEncodeCertificate(
            hIssuerProv,
            g_dwIssuerKeySpec,
            X509_ASN_ENCODING,
            X509_CERT_TO_BE_SIGNED,
            &Cert,
            &Cert.SignatureAlgorithm,
            NULL,                       // pvHashAuxInfo
            pbCertEncoded,
            &cbCertEncoded
            )) {
        ERROR_OUT(("CryptSignAndEncodeCertificate(2) failed: %x", GetLastError()));
        goto ErrorReturn;
    }

    // Output the encoded certificate to an cerificate store

    ASSERT(g_wszSubjectStore);
    ASSERT(AT_KEYEXCHANGE == g_dwSubjectKeySpec);

    if((!SaveCertToStore(hSubjectProv,
            hStore,
            g_dwSubjectStoreFlag,
            pbCertEncoded,
            cbCertEncoded,
            g_wszSubjectKey,
            g_dwSubjectKeySpec,
            g_wszSubjectProviderName,
            g_dwProvType)))
    {
        ERROR_OUT(("SaveCertToStore failed: %x", GetLastError()));
        goto ErrorReturn;

    }

    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    fResult = FALSE;
CommonReturn:

    PvkFreeCryptProv(hSubjectProv, g_wszSubjectProviderName,
                    g_dwProvType,pwszTmpSubjectContainer);

    //free the cert store
    if(hStore)
         CertCloseStore(hStore, 0);
    if (pIssuerCertContext)
        CertFreeCertificateContext(pIssuerCertContext);
    if (pAllocSubjectPubKeyInfo)
        delete (pAllocSubjectPubKeyInfo);
    if (pbSubjectEncoded)
        delete (pbSubjectEncoded);
    if (pbEKUEncoded)
        delete (pbEKUEncoded);
    if (pbSpcCommonNameEncoded)
        delete (pbSpcCommonNameEncoded);
    if (pbCertEncoded)
        delete (pbCertEncoded);
    if (hIssuerProv)
        CryptReleaseContext(hIssuerProv,0);

    return fResult;
}

//+-------------------------------------------------------------------------
//  save the certificate to a certificate store.  Attach private key information
//  to the certificate
//--------------------------------------------------------------------------
BOOL    SaveCertToStore(
                HCRYPTPROV hProv,
                HCERTSTORE hStore,        DWORD dwFlag,
                BYTE *pbEncodedCert,    DWORD cbEncodedCert,
                LPWSTR wszPvk,
                DWORD dwKeySpecification,
                LPWSTR wszCapiProv,        DWORD dwCapiProvType)
{
        BOOL                    fResult=FALSE;
        PCCERT_CONTEXT            pCertContext=NULL;
        CRYPT_KEY_PROV_INFO        KeyProvInfo;

        HCRYPTPROV              hDefaultProvName=NULL;
        DWORD                   cbData=0;
        LPSTR                   pszName=NULL;
        LPWSTR                  pwszName=NULL;

        //init
        ClearStruct(&KeyProvInfo);

        //add the encoded certificate to store
        if(!CertAddEncodedCertificateToStore(
                    hStore,
                    X509_ASN_ENCODING,
                    pbEncodedCert,
                    cbEncodedCert,
                    CERT_STORE_ADD_REPLACE_EXISTING,
                    &pCertContext))
            goto CLEANUP;

        //add properties to the certificate
        KeyProvInfo.pwszContainerName=wszPvk;
        KeyProvInfo.pwszProvName=wszCapiProv,
        KeyProvInfo.dwProvType=dwCapiProvType,
        KeyProvInfo.dwKeySpec=dwKeySpecification;

        if ( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE )
        {
            // If this is a local machine cert, set the keyset flags
            // indicating that the private key will be under HKLM
            KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
        }

        ASSERT(AT_KEYEXCHANGE == dwKeySpecification);

        //if wszCapiProv is NULL, we get the default provider name
        if(NULL==wszCapiProv)
        {
            //get the default provider
            if(CryptAcquireContext(&hDefaultProvName,
                                    NULL,
                                    NULL,
                                    KeyProvInfo.dwProvType,
                                    CRYPT_VERIFYCONTEXT))
            {

                //get the provider name
                if(CryptGetProvParam(hDefaultProvName,
                                    PP_NAME,
                                    NULL,
                                    &cbData,
                                    0) && (0!=cbData))
                {

                    if(pszName= new CHAR[cbData])
                    {
                        if(CryptGetProvParam(hDefaultProvName,
                                            PP_NAME,
                                            (BYTE *)pszName,
                                            &cbData,
                                            0))
                        {
                            pwszName= AnsiToUnicode(pszName);

                            KeyProvInfo.pwszProvName=pwszName;
                        }
                    }
                }
            }
        }

        //free the provider as we want
        if(hDefaultProvName)
            CryptReleaseContext(hDefaultProvName, 0);

        hDefaultProvName=NULL;

        //add property related to the key container
        if(!CertSetCertificateContextProperty(
                pCertContext,
                CERT_KEY_PROV_INFO_PROP_ID,
                0,
                &KeyProvInfo))
            goto CLEANUP;

        //
        // Load the display name from resource and create a blob to
        // set the cert friendly name.
        //
        CHAR szFriendlyName[128];

        if (!LoadString(hModule, IDS_DEFNAME, szFriendlyName,
                                    sizeof(szFriendlyName)))
        {
            ERROR_OUT(("LoadString failed: %d", GetLastError()));
            goto CLEANUP;
        }

        WCHAR *pwszFriendlyName;

        pwszFriendlyName = AnsiToUnicode ( szFriendlyName );

        if ( NULL == pwszFriendlyName )
        {
            ERROR_OUT(("AnsiToUnicode failed"));
            goto CLEANUP;
        }

        CRYPT_DATA_BLOB FriendlyName;

        FriendlyName.pbData = (PBYTE)pwszFriendlyName;
        FriendlyName.cbData = ( lstrlenW(pwszFriendlyName) + 1 ) *
                                sizeof(WCHAR);

        if(!CertSetCertificateContextProperty(
                pCertContext,
                CERT_FRIENDLY_NAME_PROP_ID,
                0,
                &FriendlyName))
            goto CLEANUP;

        //
        // Add magic ID
        //
        CRYPT_DATA_BLOB MagicBlob;
        DWORD dwMagic;

        dwMagic = NMMKCERT_MAGIC;
        MagicBlob.pbData = (PBYTE)&dwMagic;
        MagicBlob.cbData = sizeof(dwMagic);

        if(!CertSetCertificateContextProperty(
                pCertContext,
                CERT_FIRST_USER_PROP_ID,
                0,
                &MagicBlob))
            goto CLEANUP;

        fResult=TRUE;

CLEANUP:

        if (pwszFriendlyName)
            delete pwszFriendlyName;

        //free the cert context
        if(pCertContext)
            CertFreeCertificateContext(pCertContext);

        if(pszName)
            delete (pszName);

        if(pwszName)
           delete pwszName;

        if(hDefaultProvName)
            CryptReleaseContext(hDefaultProvName, 0);

        return fResult;

}

//+-------------------------------------------------------------------------
//  Verify the issuer's certificate. The public key in the certificate
//  must match the public key associated with the private key in the
//  issuer's provider
//--------------------------------------------------------------------------
BOOL VerifyIssuerKey(
    IN HCRYPTPROV hProv,
    IN PCERT_PUBLIC_KEY_INFO pIssuerKeyInfo
    )
{
    BOOL fResult;
    PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL;
    DWORD cbPubKeyInfo;

    // Get issuer's public key
    cbPubKeyInfo = 0;
    CryptExportPublicKeyInfo(
        hProv,
        g_dwIssuerKeySpec,
        X509_ASN_ENCODING,
        NULL,               // pPubKeyInfo
        &cbPubKeyInfo
        );
    if (cbPubKeyInfo == 0)
    {
        ERROR_OUT(("CryptExportPublicKeyInfo failed: %x", GetLastError()));
        goto ErrorReturn;
    }
    if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) new BYTE[cbPubKeyInfo]))
        goto ErrorReturn;
    if (!CryptExportPublicKeyInfo(
            hProv,
            g_dwIssuerKeySpec,
            X509_ASN_ENCODING,
            pPubKeyInfo,
            &cbPubKeyInfo
            )) {
        ERROR_OUT(("CrypteExportPublicKeyInfo(2) failed: %x", GetLastError()));
        goto ErrorReturn;
    }

    if (!CertComparePublicKeyInfo(
            X509_ASN_ENCODING,
            pIssuerKeyInfo,
            pPubKeyInfo)) {
        // BUGBUG:: This might be the test root with an incorrectly
        // encoded public key. Convert to the capi representation and
        // compare.
        BYTE rgProvKey[256]; //BUGBUG needs appropriate constant or calc
        BYTE rgCertKey[256]; //BUGBUG needs appropriate constant or calc
        DWORD cbProvKey = sizeof(rgProvKey);
        DWORD cbCertKey = sizeof(rgCertKey);

        if (!CryptDecodeObject(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB,
                    pIssuerKeyInfo->PublicKey.pbData,
                    pIssuerKeyInfo->PublicKey.cbData,
                    0,                  // dwFlags
                    rgProvKey,
                    &cbProvKey)                             ||
            !CryptDecodeObject(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB,
                    pPubKeyInfo->PublicKey.pbData,
                    pPubKeyInfo->PublicKey.cbData,
                    0,                  // dwFlags
                    rgCertKey,
                    &cbCertKey)                             ||
                cbProvKey == 0 || cbProvKey != cbCertKey    ||
                memcmp(rgProvKey, rgCertKey, cbProvKey) != 0) {
            ERROR_OUT(("mismatch: %x", GetLastError()));
            goto ErrorReturn;
        }
    }

    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    fResult = FALSE;
CommonReturn:
    if (pPubKeyInfo)
        delete (pPubKeyInfo);
    return fResult;
}

//+-------------------------------------------------------------------------
//  Get the subject's private key provider
//--------------------------------------------------------------------------
HCRYPTPROV GetSubjectProv(OUT LPWSTR *ppwszTmpContainer)
{
    HCRYPTPROV    hProv=0;
    WCHAR        wszKeyName[40] = L"Subject Key";
    int            ids;
    WCHAR       *wszRegKeyName=NULL;
    BOOL        fResult;
    HCRYPTKEY    hKey=NULL;
    GUID        TmpContainerUuid;

    //try to get the hProv from the private key container
    if(S_OK != PvkGetCryptProv(NULL,
                                wszKeyName,
                                g_wszSubjectProviderName,
                                g_dwProvType,
                                NULL,
                                g_wszSubjectKey,
                                &g_dwSubjectKeySpec,
                                ppwszTmpContainer,
                                &hProv))
        hProv=0;

    //generate the private keys
    if (0 == hProv)
    {
        //now that we have to generate private keys, generate
        //AT_KEYEXCHANGE key

        // If there is an existing container with the name of the
        // one we are about to create, attempt to delete it first so
        // that creating it won't fail. This should only happen if the
        // container exists but we were unable to acquire a context to
        // it previously.
        CryptAcquireContextU(
                &hProv,
                g_wszSubjectKey,
                g_wszSubjectProviderName,
                g_dwProvType,
                CRYPT_DELETEKEYSET |
                    ( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
                        CRYPT_MACHINE_KEYSET : 0 ));

        // Open a new key container
        if (!CryptAcquireContextU(
                &hProv,
                g_wszSubjectKey,
                g_wszSubjectProviderName,
                g_dwProvType,
                CRYPT_NEWKEYSET |
                    ( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
                        CRYPT_MACHINE_KEYSET : 0 )))
        {
            ERROR_OUT(("CryptAcquireContext failed: %x", GetLastError()));
            goto CreateKeyError;
        }

        //generate new keys in the key container - make sure its EXPORTABLE
        //for SCHANNEL! (Note: remove that when SCHANNEL no longer needs it).
        if (!CryptGenKey( hProv, g_dwSubjectKeySpec, CRYPT_EXPORTABLE, &hKey))
        {
            ERROR_OUT(("CryptGenKey failed: %x", GetLastError()));
            goto CreateKeyError;
        }
        else
            CryptDestroyKey(hKey);

        //try to get the user key
        if (CryptGetUserKey( hProv, g_dwSubjectKeySpec, &hKey))
        {
            CryptDestroyKey(hKey);
        }
        else
        {
            // Doesn't have the specified public key
            CryptReleaseContext(hProv, 0);
            hProv=0;
        }

        if (0 == hProv )
        {
            ERROR_OUT(("sub key error: %x", GetLastError()));
            goto ErrorReturn;
        }
    } //hProv==0

    goto CommonReturn;

CreateKeyError:
ErrorReturn:
    if (hProv)
    {
        CryptReleaseContext(hProv, 0);
        hProv = 0;
    }
CommonReturn:
    if(wszRegKeyName)
        delete (wszRegKeyName);

    return hProv;
}



//+-------------------------------------------------------------------------
//  Allocate and get the public key info for the provider
//--------------------------------------------------------------------------
BOOL GetPublicKey(
    HCRYPTPROV hProv,
    PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo
    )
{
    BOOL fResult;

    PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL;
    DWORD cbPubKeyInfo;

    cbPubKeyInfo = 0;
    CryptExportPublicKeyInfo(
        hProv,
        g_dwSubjectKeySpec,
        X509_ASN_ENCODING,
        NULL,               // pPubKeyInfo
        &cbPubKeyInfo
        );
    if (cbPubKeyInfo == 0) {
        ERROR_OUT(("CryptExportPublicKeyInfo failed: %x", GetLastError()));
        goto ErrorReturn;
    }
    if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) new BYTE[cbPubKeyInfo]))
        goto ErrorReturn;
    if (!CryptExportPublicKeyInfo(
            hProv,
            g_dwSubjectKeySpec,
            X509_ASN_ENCODING,
            pPubKeyInfo,
            &cbPubKeyInfo
            )) {
        ERROR_OUT(("CryptExportPublicKeyInfo(2) failed: %x", GetLastError()));
        goto ErrorReturn;
    }

    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    fResult = FALSE;
    if (pPubKeyInfo) {
        delete (pPubKeyInfo);
        pPubKeyInfo = NULL;
    }
CommonReturn:
    *ppPubKeyInfo = pPubKeyInfo;
    return fResult;
}

//+-------------------------------------------------------------------------
//  Convert and encode the subject's X500 formatted name
//--------------------------------------------------------------------------
BOOL EncodeSubject(
        OUT BYTE **ppbEncoded,
        IN OUT DWORD *pcbEncoded
        )
{
    BOOL            fResult;
    DWORD            cbEncodedSubject=0;
    BYTE            *pbEncodedSubject=NULL;
    BYTE            *pbEncoded = NULL;
    DWORD            cbEncoded;

    //encode the wszSubjectX500Name into an encoded X509_NAME

    if(!CertStrToNameW(
        X509_ASN_ENCODING,
        g_wszSubjectX500Name,
        0,
        NULL,
        NULL,
        &cbEncodedSubject,
        NULL))
    {
        ERROR_OUT(("CertStrToNameW failed: %x", GetLastError()));
        goto ErrorReturn;
    }

    pbEncodedSubject = new BYTE[cbEncodedSubject];
    if (pbEncodedSubject == NULL) goto ErrorReturn;

    if(!CertStrToNameW(
        X509_ASN_ENCODING,
        g_wszSubjectX500Name,
        0,
        NULL,
        pbEncodedSubject,
        &cbEncodedSubject,
        NULL))
    {
        ERROR_OUT(("CertStrToNameW(2) failed: %x", GetLastError()));
        goto ErrorReturn;
    }

    cbEncoded=cbEncodedSubject;
    pbEncoded=pbEncodedSubject;

    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    if (pbEncoded) {
        delete (pbEncoded);
        pbEncoded = NULL;
    }
    cbEncoded = 0;
    fResult = FALSE;
CommonReturn:
    *ppbEncoded = pbEncoded;
    *pcbEncoded = cbEncoded;
    return fResult;
}


// The test root's public key isn't encoded properly in the certificate.
// It's missing a leading zero to make it a unsigned integer.
static BYTE rgbTestRoot[] = {
    #include "root.h"
};
static CERT_PUBLIC_KEY_INFO TestRootPublicKeyInfo = {
    szOID_RSA_RSA, 0, NULL, sizeof(rgbTestRoot), rgbTestRoot, 0
};

static BYTE rgbTestRootInfoAsn[] = {
    #include "rootasn.h"
};

//+-------------------------------------------------------------------------
//  X509 Extensions: Allocate and Encode functions
//--------------------------------------------------------------------------

BOOL CreateEnhancedKeyUsage(
    OUT BYTE **ppbEncoded,
    IN OUT DWORD *pcbEncoded
    )
{
    BOOL               fResult = TRUE;
    LPBYTE             pbEncoded =NULL;
    DWORD              cbEncoded;
    PCERT_ENHKEY_USAGE pUsage =NULL;

    //
    // Allocate a cert enhanced key usage structure and fill it in
    //

    pUsage = (PCERT_ENHKEY_USAGE) new BYTE[sizeof(CERT_ENHKEY_USAGE) +
                                                2 * sizeof(LPSTR)];
    if ( pUsage != NULL )
    {
        pUsage->cUsageIdentifier = 2;
        pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage+sizeof(CERT_ENHKEY_USAGE));

        pUsage->rgpszUsageIdentifier[0] = szOID_PKIX_KP_CLIENT_AUTH;
        pUsage->rgpszUsageIdentifier[1] = szOID_PKIX_KP_SERVER_AUTH;
    }
    else
    {
        fResult = FALSE;
    }

    //
    // Encode the usage
    //

    if ( fResult == TRUE )
    {
        fResult = CryptEncodeObject(
                       X509_ASN_ENCODING,
                       szOID_ENHANCED_KEY_USAGE,
                       pUsage,
                       NULL,
                       &cbEncoded
                       );

        if ( fResult == TRUE )
        {
            pbEncoded = new BYTE[cbEncoded];
            if ( pbEncoded != NULL )
            {
                fResult = CryptEncodeObject(
                               X509_ASN_ENCODING,
                               szOID_ENHANCED_KEY_USAGE,
                               pUsage,
                               pbEncoded,
                               &cbEncoded
                               );
            }
            else
            {
                fResult = FALSE;
            }
        }
    }

    //
    // Cleanup
    //

    delete (pUsage);

    if ( fResult == TRUE )
    {
        *ppbEncoded = pbEncoded;
        *pcbEncoded = cbEncoded;
    }
    else
    {
        delete (pbEncoded);
    }

    return( fResult );
}

BOOL CreateSpcCommonName(
        OUT BYTE **ppbEncoded,
        IN OUT DWORD *pcbEncoded
        )
{
    BOOL fResult;
    BYTE *pbEncoded = NULL;
    DWORD cbEncoded;

    CERT_NAME_VALUE NameValue;

    NameValue.dwValueType = CERT_RDN_UNICODE_STRING;
    NameValue.Value.pbData =  (BYTE *) g_wszSubjectDisplayName;
    NameValue.Value.cbData =0;

    cbEncoded = 0;
    CryptEncodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME_VALUE,
            &NameValue,
            NULL,           // pbEncoded
            &cbEncoded
            );
    if (cbEncoded == 0) {
        ERROR_OUT(("CryptEncodeObject failed: %x", GetLastError()));
        goto ErrorReturn;
    }
    pbEncoded = new BYTE[cbEncoded];
    if (pbEncoded == NULL) goto ErrorReturn;
    if (!CryptEncodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME_VALUE,
            &NameValue,
            pbEncoded,
            &cbEncoded
            )) {
        ERROR_OUT(("CryptEncodeObject failed: %x", GetLastError()));
        goto ErrorReturn;
    }

    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    if (pbEncoded) {
        delete (pbEncoded);
        pbEncoded = NULL;
    }
    cbEncoded = 0;
    fResult = FALSE;
CommonReturn:
    *ppbEncoded = pbEncoded;
    *pcbEncoded = cbEncoded;
    return fResult;
}