CertGetCertificateChain trouble



Hi group,
I have a strange problem with CertGetCertificateChain. Here is what
happens:
1. I need to validate a server certificate (for which I am trying to
build the chain)
2. The first time I call CertGetCertificateChain, everything is good. I
get the complete chain.
3. However, CertGetCertificateChain returns incomplete chain the next
time (with the same server certificate).
4. When I call CertGetCertificateChain the second time, it
(CertGetCertificateChain) takes a long time to return (like >15sec)
5. The chain returned by CertGetCertificateChain the second time
contains only one certificate - the server cert itself.

I am not sure what's happening here. Because the chain is incomplete,
validation of server cert fails. Has anyone seen this case before?

Here is the code that I am using...

PCERTIFICATE_CHAIN_INFO GetCertChain(PCCERT_CONTEXT pCertContext)
{
BOOL bRetVal = true;
HCERTSTORE hMemStore = NULL;
CERT_CHAIN_ENGINE_CONFIG ChainConfig;
CERT_CHAIN_PARA ChainPara;
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
PCERTIFICATE_CHAIN_INFO pChainInfo = NULL;
CERT_ENHKEY_USAGE EnhkeyUsage;
CERT_USAGE_MATCH CertUsage;
HCERTCHAINENGINE hChainEngine;

do
{
if(pCertContext == NULL)
{
DebugMsg("Invalid argument. NULL Cert context found.\n");
break;
}

//memset(&ChainPara, 0, sizeof(ChainPara));
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
EnhkeyUsage.cUsageIdentifier = 0;
EnhkeyUsage.rgpszUsageIdentifier=NULL;
CertUsage.dwType = USAGE_MATCH_TYPE_AND;
CertUsage.Usage = EnhkeyUsage;
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
ChainPara.RequestedUsage=CertUsage;

//memset(&ChainConfig, 0, sizeof(CERT_CHAIN_ENGINE_CONFIG));
ChainConfig.cbSize = sizeof(CERT_CHAIN_ENGINE_CONFIG);
ChainConfig.hRestrictedRoot= NULL;
ChainConfig.hRestrictedTrust= NULL;
ChainConfig.hRestrictedOther= NULL ;
ChainConfig.cAdditionalStore=0 ;
ChainConfig.rghAdditionalStore = NULL ;
ChainConfig.dwFlags = CERT_CHAIN_CACHE_END_CERT;
ChainConfig.dwUrlRetrievalTimeout= 0 ;
ChainConfig.MaximumCachedCertificates=0 ;
ChainConfig.CycleDetectionModulus = 0;

//---------------------------------------------------------
// Create the nondefault certificate chain engine.
if(CertCreateCertificateChainEngine(
&ChainConfig,
&hChainEngine))
{
DebugMsg( "A chain engine has been created.\n");
}
else
{
DebugMsg("The engine creation function failed.");
break;
}

DWORD dwFlags = 0;
BOOL bRetVal = CertGetCertificateChain(NULL,
pCertContext,
NULL,
NULL,
&ChainPara,
dwFlags,
NULL,
&pChainContext);

if(bRetVal && pChainContext)
{
DebugMsg("CertGetCertificateChain Successful. Chain count = %d\n",
pChainContext->cChain);

//-------------------------------------------------------------------
// Open a memory store.
if((hMemStore = CertOpenStore(
CERT_STORE_PROV_MEMORY, // The memory provider type
0, // The encoding type is not needed
NULL, // Use the default HCRYPTPROV
0, // Accept the default dwFlags
NULL // pvPara is not used
)) == NULL)
{
DebugMsg("Failed to create virtual store.\n");
bRetVal = false;
break;
}

for(int i=0; i<pChainContext->cChain; i++)
{
DebugMsg("Number of elements = %d\n",
pChainContext->rgpChain[i]->cElement);
for(int j=0; j<pChainContext->rgpChain[i]->cElement; j++)
{
printf("Certificate# %d in chain (link# %d)\n", j, i);
char pszNameString[256];
CertGetNameString(pChainContext->rgpChain[i]->rgpElement[j]->pCertContext,
CERT_NAME_DNS_TYPE,
0,
NULL,
pszNameString,
128);

printf("Cert Subject: %s\n", pszNameString);

CertGetNameString(pChainContext->rgpChain[i]->rgpElement[j]->pCertContext,
CERT_NAME_DNS_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
pszNameString,
128);
printf("Issuer Name: %s\n", pszNameString);

if(pChainContext->rgpChain[i]->rgpElement[j]->pCertContext)
{
if(!CertAddCertificateContextToStore(hMemStore,
pChainContext->rgpChain[i]->rgpElement[j]->pCertContext,
CERT_STORE_ADD_NEW, NULL) &&
GetLastError() != CRYPT_E_EXISTS)
{
DebugMsg("failed to add certificate to virtual store. Error =
<%d>\n", GetLastError());
bRetVal = false;
break;
}
}
else
{
DebugMsg("malformed chain element! Found NULL cert context while
parsing chain\n");
}
}
}
}
else
{
DebugMsg("CertGetCertificateChain Failed. RetVal = %d, "
"pChainContext = <0x%x>, Error Code = <%d>\n", bRetVal,
pChainContext, GetLastError());
}

CertFreeCertificateChainEngine(hChainEngine);
}while(false);

if(bRetVal)
{
pChainInfo = new CERTIFICATE_CHAIN_INFO;
pChainInfo->RevId = CERTIFICATE_CHAIN_REV_ID;
pChainInfo->hCertStore = hMemStore;
pChainInfo->pChainContext = pChainContext;
}
else
{
pChainInfo = NULL;
if(pChainContext)
CertFreeCertificateChain(pChainContext);

//BUG BUG BUG: Possible Resource leak!!!!!
//ToDo: Call CertDeleteCertificateFromStore on the memory store.
CertCloseStore(hMemStore, CERT_CLOSE_STORE_CHECK_FLAG);
}

return pChainInfo;
}

Can you think of any reason why CertGetCertificateChain would fail to
create the complete chain?

Thank you,
-Prabhu

.