//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       cscsp.cpp
//
//--------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include <tchar.h>
#include "initcert.h"
#include "cscsp.h"
#include "csdisp.h"

TCHAR const g_wszRegKeyCSP[] = wszREGKEYCSP;
TCHAR const g_wszRegKeyEncryptionCSP[] = wszREGKEYENCRYPTIONCSP;
TCHAR const g_wszRegProviderType[] = wszREGPROVIDERTYPE;
TCHAR const g_wszRegProvider[] = wszREGPROVIDER;
TCHAR const g_wszRegHashAlgorithm[] = wszHASHALGORITHM;
TCHAR const g_wszRegEncryptionAlgorithm[] = wszENCRYPTIONALGORITHM;
TCHAR const g_wszRegMachineKeyset[] = wszMACHINEKEYSET;
TCHAR const g_wszRegKeySize[] = wszREGKEYSIZE;


BOOL
myCertSrvCryptAcquireContext(
    OUT HCRYPTPROV *phProv,
    IN WCHAR const *pwszContainer,
    IN WCHAR const *pwszProvider,
    IN DWORD        dwProvType,
    IN DWORD        dwFlags,
    IN BOOL         fMachineKeyset)
{
    HRESULT hr;

    if (fMachineKeyset)
    {
        dwFlags |= CRYPT_MACHINE_KEYSET;
    }
    if (!CryptAcquireContext(
			phProv,
			pwszContainer,
			pwszProvider,
			dwProvType,
			dwFlags))
    {
	hr = myHLastError();
	_JumpErrorStr2(
	    hr,
	    error,
	    (CRYPT_MACHINE_KEYSET & dwFlags)?
		"CryptAcquireContext(Machine)" : "CryptAcquireContext(User)",
	    pwszContainer,
	    (CRYPT_DELETEKEYSET & dwFlags)? hr : S_OK);
    }
    hr = S_OK;
          
error:
    if (S_OK != hr)
    {
	SetLastError(hr);
    }
    return(S_OK == hr);
}


HRESULT
myGetCertSrvCSP(
    IN BOOL fEncryptionCSP,
    IN WCHAR const *pwszSanitizedCAName,
    OUT DWORD *pdwProvType,
    OUT WCHAR **ppwszProvName,
    OUT ALG_ID *pidAlg,
    OUT BOOL *pfMachineKeyset,
    OPTIONAL OUT DWORD *pdwKeySize)
{
    HRESULT hr;
    HKEY    hCertSrvCSPKey = NULL;
    DWORD   dwValueType;
    DWORD   dwValueSize;
    WCHAR  *pwszPath = NULL;
    WCHAR  *pwszProvName = NULL;
    WCHAR const *pwszRegKeyCSP;
    DWORD cwc;
    
    if (NULL != ppwszProvName)
    {
	*ppwszProvName = NULL;
    }
    if (NULL != pdwKeySize)
    {
	*pdwKeySize = 0;
    }
    if (NULL == pwszSanitizedCAName ||
	NULL == pdwProvType ||
	NULL == ppwszProvName ||
	NULL == pidAlg ||
	NULL == pfMachineKeyset)
    {
        hr = E_POINTER;
        _JumpError(hr, error, "NULL parm");
    }

    pwszRegKeyCSP = fEncryptionCSP? g_wszRegKeyEncryptionCSP : g_wszRegKeyCSP;
    cwc = WSZARRAYSIZE(wszREGKEYCONFIGPATH_BS) +
	    wcslen(pwszSanitizedCAName) +
	    1 +
	    wcslen(pwszRegKeyCSP);
    pwszPath = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
    if (NULL == pwszPath)
    {
        hr = E_OUTOFMEMORY;
        _JumpError(hr, error, "LocalAlloc");
    }

    wcscpy(pwszPath, wszREGKEYCONFIGPATH_BS);
    wcscat(pwszPath, pwszSanitizedCAName);
    wcscat(pwszPath, L"\\");
    wcscat(pwszPath, pwszRegKeyCSP);
    CSASSERT(cwc == wcslen(pwszPath));

    hr = RegOpenKeyEx(
		HKEY_LOCAL_MACHINE,
		pwszPath,
		0,
		KEY_ALL_ACCESS,
		&hCertSrvCSPKey);
    _JumpIfErrorStr2(hr, error, "RegOpenKeyEx", pwszPath, ERROR_FILE_NOT_FOUND);

    dwValueSize = 0;
    hr = RegQueryValueEx(
		    hCertSrvCSPKey,
		    g_wszRegProvider,
		    0,
		    &dwValueType,
		    NULL,
		    &dwValueSize);
    _JumpIfError(hr, error, "RegQueryValueEx");

    if (REG_SZ != dwValueType)
    {
        hr = E_INVALIDARG;
        _JumpError(hr, error, "Invalid reg type");
    }

    pwszProvName = (WCHAR *) LocalAlloc(
				    LMEM_FIXED,
				    dwValueSize + sizeof(WCHAR));
    if (NULL == pwszProvName)
    {
        hr = E_OUTOFMEMORY;
        _JumpError(hr, error, "LocalAlloc");
    }
    hr = RegQueryValueEx(
		    hCertSrvCSPKey,
		    g_wszRegProvider,
		    0,
		    &dwValueType,
		    (BYTE *) pwszProvName,
		    &dwValueSize);
    _JumpIfError(hr, error, "RegQueryValueEx");

    pwszProvName[dwValueSize / sizeof(WCHAR)] = L'\0';


    dwValueSize = sizeof(*pdwProvType);
    hr = RegQueryValueEx(hCertSrvCSPKey,
                          g_wszRegProviderType,
                          0,
                          &dwValueType,
                          (BYTE *) pdwProvType,
                          &dwValueSize);
    _JumpIfError(hr, error, "RegQueryValueEx");

    if (REG_DWORD != dwValueType)
    {
        hr = E_INVALIDARG;
        _JumpError(hr, error, "Invalid reg type");
    }

    dwValueSize = sizeof(*pidAlg);
    hr = RegQueryValueEx(
		    hCertSrvCSPKey,
		    fEncryptionCSP? 
			g_wszRegEncryptionAlgorithm :
			g_wszRegHashAlgorithm,
		    0,
		    &dwValueType,
		    (BYTE *) pidAlg,
		    &dwValueSize);
    if (S_OK != hr && fEncryptionCSP)
    {
	_PrintErrorStr2(
		    hr,
		    "RegQueryValueEx",
		    g_wszRegEncryptionAlgorithm,
		    ERROR_FILE_NOT_FOUND);
	dwValueSize = sizeof(*pidAlg);
	hr = RegQueryValueEx(
			hCertSrvCSPKey,
			g_wszRegHashAlgorithm,
			0,
			&dwValueType,
			(BYTE *) pidAlg,
			&dwValueSize);
    }
    _JumpIfError(hr, error, "RegQueryValueEx");

    if (REG_DWORD != dwValueType)
    {
        hr = E_INVALIDARG;
        _JumpError(hr, error, "Invalid reg type");
    }

    dwValueSize = sizeof(*pfMachineKeyset);
    hr = RegQueryValueEx(hCertSrvCSPKey,
                          g_wszRegMachineKeyset,
                          0,
                          &dwValueType,
                          (BYTE *) pfMachineKeyset,
                          &dwValueSize);
    _JumpIfError(hr, error, "RegQueryValueEx");

    if (REG_DWORD != dwValueType)
    {
        hr = E_INVALIDARG;
        _JumpError(hr, error, "Invalid reg type");
    }

    if (NULL != pdwKeySize)
    {
	dwValueSize = sizeof(*pdwKeySize);
	hr = RegQueryValueEx(hCertSrvCSPKey,
			      g_wszRegKeySize,
			      0,
			      &dwValueType,
			      (BYTE *) pdwKeySize,
			      &dwValueSize);
	_JumpIfError(hr, error, "RegQueryValueEx");

	if (REG_DWORD != dwValueType)
	{
	    hr = E_INVALIDARG;
	    _JumpError(hr, error, "Invalid reg type");
	}
    }

    *ppwszProvName = pwszProvName;
    pwszProvName = NULL;
    hr = S_OK;

error:
    if (NULL != pwszProvName)
    {
        LocalFree(pwszProvName);
    }
    if (NULL != pwszPath)
    {
        LocalFree(pwszPath);
    }
    if (NULL != hCertSrvCSPKey)
    {
        RegCloseKey(hCertSrvCSPKey);
    }
    return(myHError(hr));
}


//+------------------------------------------------------------------------
//
//  Function:   myGetSigningOID( . . . . )
//
//  Synopsis:   Determine algorithm identifer for cert creation
//  Arguments:  csp_provider_type, hash_algorithm_id.
//  Returns:    object identifier
//
//-------------------------------------------------------------------------

HRESULT
myGetSigningOID(
    OPTIONAL IN HCRYPTPROV hProv,	// hProv OR pwszProvName & dwProvType
    OPTIONAL IN WCHAR const *pwszProvName,
    OPTIONAL IN DWORD dwProvType,
    IN ALG_ID idHashAlg,
    OUT CHAR **ppszAlgId)
{
    HRESULT  hr;

    PCCRYPT_OID_INFO pcOIDInfo; //don't free it
    ALG_ID      aidAlgKey[] = {idHashAlg, 0};
    HCRYPTPROV  hProvT = NULL;
    int         i;
    DWORD       dwFlags;
    DWORD       cbData;
    PROV_ENUMALGS enumalgs;
    BOOL        fFoundSignID = FALSE;

    *ppszAlgId = NULL;
    if (NULL == hProv)
    {
	CSASSERT(NULL != pwszProvName);

	if (!myCertSrvCryptAcquireContext(
				&hProvT,
				NULL,
				pwszProvName,
				dwProvType,
				CRYPT_VERIFYCONTEXT,
				FALSE))
	{
	    hr = myHLastError();
	    _JumpErrorStr(hr, error, "myCertSrvCryptAcquireContext", pwszProvName);
	}
	hProv = hProvT;
    }

    // find public key id

    dwFlags = CRYPT_FIRST;
    for (i = 0; ; i++)
    {
        cbData = sizeof(enumalgs);
        if (!CryptGetProvParam(
			    hProv,
			    PP_ENUMALGS,
			    (BYTE *) &enumalgs,
			    &cbData,
			    dwFlags))
        {
            hr = myHLastError();
            if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
            {
                // out of for loop
                break;
            }
	    _JumpError(hr, error, "CryptGetProvParam");
        }
        if (ALG_CLASS_SIGNATURE == GET_ALG_CLASS(enumalgs.aiAlgid))
        {
            fFoundSignID = TRUE;
            aidAlgKey[1] = enumalgs.aiAlgid;
            break;
        }
	dwFlags = 0;
    }

    if (fFoundSignID)
    {
        pcOIDInfo = CryptFindOIDInfo(
                        CRYPT_OID_INFO_SIGN_KEY,
                        aidAlgKey,
                        CRYPT_SIGN_ALG_OID_GROUP_ID); // only signing
        if (NULL == pcOIDInfo)
        {
            hr = E_INVALIDARG;
            _JumpError(hr, error, "unsupported signing algorithm");
        }
	hr = myDupStringA(pcOIDInfo->pszOID, ppszAlgId);
	_JumpIfError(hr, error, "myDupStringA");
    }
    hr = S_OK;

error:
    if (NULL != hProvT)
    {
        CryptReleaseContext(hProvT, 0);
    }
    CSASSERT(S_OK == hr || FAILED(hr));
    return(hr);
}


HRESULT
myValidateKeyForSigning(
    IN HCRYPTPROV hProv,
    OPTIONAL IN CERT_PUBLIC_KEY_INFO const *pPublicKeyInfo,
    IN ALG_ID algId)
{
    HRESULT hr;
    BYTE abRandom[64];
    HCRYPTKEY hKey = NULL;
    HCRYPTHASH hHash = NULL;
    BYTE *pbSignature = NULL;
    DWORD cbSignature;

    // create a supported hash
    if (!CryptCreateHash(hProv, algId, 0, 0, &hHash))
    {
        hr = myHLastError();
	DBGPRINT((DBG_SS_ERROR, "algId = %x\n", algId));
        _JumpError(hr, error, "CryptCreateHash");
    }
    // create some random data
    if (!CryptGenRandom(hProv, ARRAYSIZE(abRandom), abRandom))
    {
        hr = myHLastError();
        _JumpError(hr, error, "CryptGenRandom");
    }
    if (!CryptHashData(hHash, abRandom, ARRAYSIZE(abRandom), 0))
    {
        hr = myHLastError();
        _JumpError(hr, error, "CryptHashData");
    }
    // sign the hash, get size first
    if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &cbSignature))
    {
        hr = myHLastError();
        _JumpError(hr, error, "CryptSignHash");
    }
    pbSignature = (BYTE *) LocalAlloc(LMEM_FIXED, cbSignature);
    if (NULL == pbSignature)
    {
        hr = E_OUTOFMEMORY;
        _JumpError(hr, error, "LocalAlloc");
    }
    if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &cbSignature))
    {
        hr = myHLastError();
        _JumpError(hr, error, "CryptSignHash");
    }

    if (NULL != pPublicKeyInfo)
    {
	// import public key into provider

	if (!CryptImportPublicKeyInfo(
			hProv,
			X509_ASN_ENCODING,
			const_cast<CERT_PUBLIC_KEY_INFO *>(pPublicKeyInfo),
			&hKey))
	{
	    hr = myHLastError();
	    _JumpError(hr, error, "CryptImportPublicKeyInfo");
	}
    }
    else
    {
        // get public key from container

        if (!CryptGetUserKey(hProv, AT_SIGNATURE, &hKey))
        {
            hr = myHLastError();
            _JumpError(hr, error, "CryptGetUserKey");
        }
    }

    if (!CryptVerifySignature(hHash, pbSignature, cbSignature, hKey, NULL, 0))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptVerifySignature");
    }
    hr = S_OK;

error:
    if (NULL != hHash)
    {
        CryptDestroyHash(hHash);
    }
    if (NULL != hKey)
    {
        CryptDestroyKey(hKey);
    }
    if (NULL != pbSignature)
    {
        LocalFree(pbSignature);
    }
    CSASSERT(S_OK == hr || FAILED(hr));
    return(hr);
}


HRESULT
myValidateKeyForEncrypting(
    IN HCRYPTPROV hProv,
    IN CERT_PUBLIC_KEY_INFO const *pPublicKeyInfo,
    IN ALG_ID algId)
{
    HRESULT hr;
    HCRYPTKEY hKeyPub = NULL;
    HCRYPTKEY hKeySym = NULL;
    HCRYPTKEY hKeyPri = NULL;
    BYTE *pbKeySym = NULL;
    DWORD cbKeySym;
    BYTE abRandom[64];
    BYTE *pbEncrypted = NULL;
    BYTE *pbDecrypted = NULL;
    DWORD cbEncrypted;
    DWORD cbDecrypted;

    // import public key into provider

    if (!CryptImportPublicKeyInfo(
			hProv,
			X509_ASN_ENCODING,
			const_cast<CERT_PUBLIC_KEY_INFO *>(pPublicKeyInfo),
			&hKeyPub))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptImportPublicKeyInfo");
    }

    // Generate a default sized symmetric session key

    if (!CryptGenKey(hProv, algId, CRYPT_EXPORTABLE, &hKeySym))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptGenKey");
    }

    // create some random data

    if (!CryptGenRandom(hProv, ARRAYSIZE(abRandom), abRandom))
    {
        hr = myHLastError();
        _JumpError(hr, error, "CryptGenRandom");
    }
#if 1
    DBGPRINT((DBG_SS_CERTLIBI, "Plain text size = %d\n", ARRAYSIZE(abRandom)));
    DBGDUMPHEX((
	DBG_SS_CERTLIBI,
	DH_NOADDRESS | DH_NOTABPREFIX | 8,
	abRandom,
	ARRAYSIZE(abRandom)));
#endif
    hr = myCryptEncrypt(
		    hKeySym,
		    abRandom,
		    ARRAYSIZE(abRandom),
		    &pbEncrypted,
		    &cbEncrypted);
    _JumpIfError(hr, error, "myCryptEncrypt");

#if 1
    DBGPRINT((DBG_SS_CERTLIBI, "Encrypted size = %d\n", cbEncrypted));
    DBGDUMPHEX((
	DBG_SS_CERTLIBI,
	DH_NOADDRESS | DH_NOTABPREFIX | 8,
	pbEncrypted,
	cbEncrypted));
#endif

    hr = myCryptExportKey(hKeySym, hKeyPub, SIMPLEBLOB, 0, &pbKeySym, &cbKeySym);
    _JumpIfError(hr, error, "myCryptExportKey");

    CryptDestroyKey(hKeySym);
    hKeySym = NULL;

#if 1
    DBGPRINT((DBG_SS_CERTLIBI, "SIMPLEBLOB:\n"));
    DBGDUMPHEX((
	DBG_SS_CERTLIBI,
	DH_NOADDRESS | DH_NOTABPREFIX | 8,
	pbKeySym,
	cbKeySym));
#endif

    // get private key from container and import session key blob

    if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKeyPri))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptGetUserKey");
    }
    if (!CryptImportKey(
		    hProv,
		    pbKeySym,
		    cbKeySym,
		    hKeyPri,
		    CRYPT_EXPORTABLE,
		    &hKeySym))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptImportKey");
    }
    hr = myCryptDecrypt(
		    hKeySym,
		    pbEncrypted,
		    cbEncrypted,
		    &pbDecrypted,
		    &cbDecrypted);
    _JumpIfError(hr, error, "myCryptDecrypt");

#if 1
    DBGPRINT((DBG_SS_CERTLIBI, "Decrypted size = %d\n", cbDecrypted));
    DBGDUMPHEX((
	DBG_SS_CERTLIBI,
	DH_NOADDRESS | DH_NOTABPREFIX | 8,
	pbDecrypted,
	cbDecrypted));
#endif

    if (ARRAYSIZE(abRandom) != cbDecrypted ||
	0 != memcmp(abRandom, pbDecrypted, ARRAYSIZE(abRandom)))
    {
	hr = NTE_BAD_KEY;
	_JumpIfError(hr, error, "Decrypted data mismatch");
    }
    hr = S_OK;

error:
    if (NULL != hKeyPub)
    {
        CryptDestroyKey(hKeyPub);
    }
    if (NULL != hKeySym)
    {
        CryptDestroyKey(hKeySym);
    }
    if (NULL != hKeyPri)
    {
        CryptDestroyKey(hKeyPri);
    }
    if (NULL != pbEncrypted)
    {
	LocalFree(pbEncrypted);
    }
    if (NULL != pbDecrypted)
    {
	LocalFree(pbDecrypted);
    }
    if (NULL != pbKeySym)
    {
	LocalFree(pbKeySym);
    }
    CSASSERT(S_OK == hr || FAILED(hr));
    return(hr);
}


HRESULT
myValidateHashForSigning(
    IN WCHAR const *pwszContainer,
    IN WCHAR const *pwszProvName,
    IN DWORD dwProvType,
    IN BOOL fMachineKeyset,
    IN OPTIONAL CERT_PUBLIC_KEY_INFO const *pPublicKeyInfo,
    IN ALG_ID algId)
{
    HRESULT hr;
    HCRYPTPROV hProv = NULL;

    // get private key handler
    if (!myCertSrvCryptAcquireContext(
			       &hProv,
			       pwszContainer,
			       pwszProvName,
			       dwProvType,
			       0,
			       fMachineKeyset))
    {
        hr = myHLastError();
        _JumpError(hr, error, "myCertSrvCryptAcquireContext");
    }

    hr = myValidateKeyForSigning(hProv, pPublicKeyInfo, algId);
    _JumpIfError(hr, error, "myValidateKeyForSigning");

error:
    if (NULL != hProv)
    {
        CryptReleaseContext(hProv, 0);
    }
    CSASSERT(S_OK == hr || FAILED(hr));
    return(hr);
}


HRESULT
GenerateTemporaryContainerName(
    OUT WCHAR **ppwszKeyContainerName)
{
    HRESULT hr;
    GUID guid;
    WCHAR awcbuf[MAX_PATH];
    DWORD cb;
    DWORD cwc;

    myGenerateGuidSerialNumber(&guid);

    wcscpy(awcbuf, L"KeyVerification--");
    cwc = wcslen(awcbuf);
    cb = sizeof(awcbuf) - cwc * sizeof(WCHAR);
    hr = MultiByteIntegerToWszBuf(
			FALSE,
			sizeof(guid),
			(BYTE const *) &guid,
			&cb,
			&awcbuf[cwc]);
    _JumpIfError(hr, error, "MultiByteIntegerToWszBuf");

    hr = myDupString(awcbuf, ppwszKeyContainerName);
    _JumpIfError(hr, error, "myDupString");

error:
    return(hr);
}


VOID
DeleteRSAKeyContainer(
    IN WCHAR const *pwszKeyContainerName,
    IN BOOL fMachineKeyset)
{
    HCRYPTPROV hProv;
    DWORD dwFlags = CRYPT_DELETEKEYSET;

    if (fMachineKeyset)
    {
        dwFlags = CRYPT_MACHINE_KEYSET;
    }
    CryptAcquireContext(
		    &hProv,
		    pwszKeyContainerName,
		    NULL,		// pwszProvName
		    PROV_RSA_FULL,
		    dwFlags);
}


HRESULT
myValidateKeyBlob(
    IN BYTE const *pbKey,
    IN DWORD cbKey,
    IN CERT_PUBLIC_KEY_INFO const *pPublicKeyInfo,
    IN BOOL fV1Cert,
    OPTIONAL OUT CRYPT_KEY_PROV_INFO *pkpi)
{
    HRESULT hr;
    WCHAR *pwszKeyContainerName = NULL;
    HCRYPTPROV hProv = NULL;
    HCRYPTKEY hKey = NULL;
    CRYPT_KEY_PROV_INFO kpi;
    BOOL fMatchingKey;
    WCHAR wszPassword[MAX_PATH];

    hr = GenerateTemporaryContainerName(&pwszKeyContainerName);
    _JumpIfError(hr, error, "GenerateTemporaryContainerName");

    DeleteRSAKeyContainer(pwszKeyContainerName, FALSE);

    DBGPRINT((DBG_SS_CERTLIBI, "Key Container: %ws\n", pwszKeyContainerName));

    if (!CryptAcquireContext(
			&hProv,
			pwszKeyContainerName,
			NULL,
			PROV_RSA_FULL,
			CRYPT_NEWKEYSET))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptAcquireContext");
    }

    if (!CryptImportKey(
		    hProv,
		    pbKey,
		    cbKey,
		    NULL,		// hPubKey
		    CRYPT_EXPORTABLE,
		    &hKey))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptImportKey");
    }
    CryptDestroyKey(hKey);
    hKey = NULL;

    ZeroMemory(&kpi, sizeof(kpi));
    kpi.pwszContainerName = pwszKeyContainerName;
    kpi.dwProvType = PROV_RSA_FULL;
    kpi.dwKeySpec = AT_KEYEXCHANGE;
    if (!CryptGetUserKey(hProv, kpi.dwKeySpec, &hKey))
    {
	hr = myHLastError();
	_PrintIfError2(hr, "CryptGetUserKey", hr);

	kpi.dwKeySpec = AT_SIGNATURE;
	if (!CryptGetUserKey(hProv, kpi.dwKeySpec, &hKey))
	{
	    hr = myHLastError();
	    _JumpIfError(hr, error, "CryptGetUserKey");
	}
    }
    CryptDestroyKey(hKey);
    hKey = NULL;

    CryptReleaseContext(hProv, 0);
    hProv = NULL;

    if (!CryptAcquireContext(
			&hProv,
			pwszKeyContainerName,
			NULL,
			PROV_RSA_FULL,
			0))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CryptAcquireContext");
    }
    if (AT_SIGNATURE == kpi.dwKeySpec)
    {
	hr = myValidateKeyForSigning(hProv, pPublicKeyInfo, CALG_SHA1);
	_PrintIfError(hr, "myValidateKeyForSigning");
    }
    else
    {
	hr = myValidateKeyForEncrypting(hProv, pPublicKeyInfo, CALG_RC4);
	_PrintIfError(hr, "myValidateKeyForEncrypting");
    }
    if (S_OK != hr)
    {
	_JumpError(hr, error, "Key Validation");
    }

    hr = myVerifyPublicKey(
		    NULL,		// pCert
		    fV1Cert,
		    &kpi,		// pKeyProvInfo
		    pPublicKeyInfo,	// pPublicKeyInfo
		    &fMatchingKey);
    if (S_OK != hr)
    {
	_JumpError(hr, error, "myVerifyPublicKey");
    }
    if (!fMatchingKey)
    {
	hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
	_JumpError(hr, error, "Key doesn't match cert");
    }
    if (NULL != pkpi)
    {
	*pkpi = kpi;
	pwszKeyContainerName = NULL;
    }

error:
    if (NULL != hKey)
    {
	CryptDestroyKey(hKey);
    }
    if (NULL != hProv)
    {
	CryptReleaseContext(hProv, 0);
    }
    if (NULL != pwszKeyContainerName)
    {
	DeleteRSAKeyContainer(pwszKeyContainerName, FALSE);
	LocalFree(pwszKeyContainerName);
    }
    return(hr);
}


/*
 *      myEnumProviders
 *
 *      Purpose:
 *                Enumerate the providers.
 *
 *      Parameters:
 *                IN  dwIndex        - Index to the providers to enumerate
 *                IN  pdwReserved    - Reserved for future use
 *                IN  dwFlags        - Flags parameter
 *                OUT pdwProvType    - The type of the provider
 *                OUT ppwszProvName  - Name of the enumerated provider
 */

HRESULT
myEnumProviders(
    IN DWORD dwIndex,
    IN DWORD *pdwReserved,
    IN DWORD dwFlags,
    OUT DWORD *pdwProvType,
    OUT WCHAR **ppwszProvName)
{
    HRESULT hr;
    char *pszProvName = NULL;
    DWORD cbProvName;

    *ppwszProvName = NULL;
    
    while (TRUE)
    {
	if (!CryptEnumProvidersA(
			dwIndex,
			pdwReserved,
			dwFlags,
			pdwProvType,
			pszProvName,
			&cbProvName))
	{
	    hr = myHLastError();
	    _JumpError2(hr, error, "CryptEnumProvidersA", hr);
	}
	if (NULL != pszProvName)
	{
	    break;
	}

        // allocate ansi string buffer

        pszProvName = (char *) LocalAlloc(LMEM_FIXED, cbProvName);
        if (NULL == pszProvName)
        {
            hr = E_OUTOFMEMORY;
            _JumpError(hr, error, "LocalAlloc");
        }
    }

    if (!myConvertSzToWsz(ppwszProvName, pszProvName, -1))
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "myConvertSzToWsz");
    }
    hr = S_OK;

error:
    if (NULL != pszProvName)
    {
        LocalFree(pszProvName);
    }
    return(hr);
}