Understanding the new WinInet option INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT

With the release of Internet Explorer 8 comes a new option you can query for with programming with the WinInet APIs: INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT.  The MSDN documentation tells you it allows you to get the PCCERT_CHAIN_CONTEXT, and not much more.  It does not show you how to properly get the Context.  I like to see things in action so here is some sample code.

This option is documented here: https://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx (note that if you are not using the latest SDK headers, the value for this option is also documented here).

To sum it up, use this option to get the Server Certificate Chain Context  to use with other Crypto API functions  To understand this option you can modify the HttpDump example in the Platforms SDK and see how this option can be used.   Since this is an option for the request you get this option using the request handle after you execute the request.  I am not a Crypto API guru so I stole some code examples from MSDN just to dump out some information about the Certificate Chain. 
 
The most important (and undocumented) part of getting this option is to pass a Pointer to a  PCCERT_CHAIN_CONTEXT. Below is a code snippet that I added to the HttpDump sample in the Windows 7 SDK (RC).

First, there is a problem with that sample when compiled for unicode.  I fixed it with this replacement code:

C++ code listing for sample (Copy Code):

dwSize=0;
TCHAR *myBuff = NULL;

// First time we will find out the size of the headers.

HttpQueryInfo (hReq,HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &dwSize, NULL);
myBuff = new TCHAR [dwSize+1];
// Now we call HttpQueryInfo again to get the headers.
if (!HttpQueryInfo (hReq,HTTP_QUERY_RAW_HEADERS_CRLF, (LPVOID) myBuff,
&dwSize, NULL))
{
ErrorOut (GetLastError(), TEXT("HttpQueryInfo"));
}
else
{
*(myBuff + dwSize) = '\0';
wcout << myBuff << endl;
}

delete myBuff;

Next I added this code just before closing the request handle ( 'if (!InternetCloseHandle (hReq) )' ).

C++ code listing for sample (Copy Code):

PCCERT_CHAIN_CONTEXT CertCtx=NULL;
DWORD cbCertSize =

sizeof(&CertCtx);

//GetCertificate information

if (InternetQueryOption(hReq,INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT ,(LPVOID)&CertCtx,&cbCertSize))
{
//---------------------------------------------------------------
// Display some of the contents of the chain.
PCCERT_CHAIN_CONTEXT pChainContext=CertCtx;
printf("The size of the chain context is %d. \n",pChainContext->cbSize);
printf("%d simple chains found.\n",pChainContext->cChain);
printf("\nError status for the chain:\n");
switch(pChainContext->TrustStatus.dwErrorStatus)
{
case CERT_TRUST_NO_ERROR :
printf("No error found for this certificate or chain.\n");
break;
case CERT_TRUST_IS_NOT_TIME_VALID:
printf("This certificate or one of the certificates in the certificate chain is not time-valid.\n");
break;
case CERT_TRUST_IS_NOT_TIME_NESTED:
printf("Certificates in the chain are not properly time-nested.\n");
break;
case CERT_TRUST_IS_REVOKED:
printf("Trust for this certificate or one of the certificates in the certificate chain has been revoked.\n");
break;
case CERT_TRUST_IS_NOT_SIGNATURE_VALID:
printf("The certificate or one of the certificates in the certificate chain does not have a valid signature.\n");
break;
case CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
printf("The certificate or certificate chain is not valid in its proposed usage.\n");
break;
case CERT_TRUST_IS_UNTRUSTED_ROOT:
printf("The certificate or certificate chain is based on an untrusted root.\n");
break;
case CERT_TRUST_REVOCATION_STATUS_UNKNOWN:
printf("The revocation status of the certificate or one of the certificates in the certificate chain is unknown.\n");
break;
case CERT_TRUST_IS_CYCLIC :
printf("One of the certificates in the chain was issued by a certification authority that the original certificate had certified.\n");
break;
case CERT_TRUST_IS_PARTIAL_CHAIN:
printf("The certificate chain is not complete.\n");
break;
case CERT_TRUST_CTL_IS_NOT_TIME_VALID:
printf("A CTL used to create this chain was not time-valid.\n");
break;
case CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID:
printf("A CTL used to create this chain did not have a valid signature.\n");
break;
case CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE:
printf("A CTL used to create this chain is not valid for this usage.\n");
} // End switch

printf(

"\nInfo status for the chain:\n");
switch(pChainContext->TrustStatus.dwInfoStatus)
{
case 0:
printf("No information status reported.\n");
break;
case CERT_TRUST_HAS_EXACT_MATCH_ISSUER :
printf("An exact match issuer certificate has been found for this certificate.\n");
break;
case CERT_TRUST_HAS_KEY_MATCH_ISSUER:
printf("A key match issuer certificate has been found for this certificate.\n");
break;
case CERT_TRUST_HAS_NAME_MATCH_ISSUER:
printf("A name match issuer certificate has been found for this certificate.\n");
break;
case CERT_TRUST_IS_SELF_SIGNED:
printf("This certificate is self-signed.\n");
break;
case CERT_TRUST_IS_COMPLEX_CHAIN:
printf("The certificate chain created is a complex chain.\n");
break;
case CERT_TRUST_HAS_PREFERRED_ISSUER:
printf("The certificate chain has a preferred issuer.\n");
break;
case CERT_TRUST_HAS_ISSUANCE_CHAIN_POLICY:
printf("The certificate chain has issuance chain policy.\n");
break;
case CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS:
printf("The certificate chain valid name contraints.\n");
break;
case CERT_TRUST_IS_PEER_TRUSTED:
printf("The certificate chain is peer trusted.\n");
break;
case CERT_TRUST_HAS_CRL_VALIDITY_EXTENDED:
printf("The certificate chain has CRL validity extended.\n");
break;
case CERT_TRUST_IS_FROM_EXCLUSIVE_TRUST_STORE:
printf("The certificate chain was found in a store specified by hExclusiveRoot or hExclusiveTrustedPeople.\n");
break;
} // end switch

CERT_SIMPLE_CHAIN *simpleCertificateChainWithinContext = NULL;
for (int i=0; i<pChainContext->cChain; i++)
{
simpleCertificateChainWithinContext=pChainContext->rgpChain[i];
// for each certificate chain in this context...
for (int simpleCertChainIndex = 0;
simpleCertChainIndex < simpleCertificateChainWithinContext->cElement;
simpleCertChainIndex++)
{

// get the CertContext from the array

PCCERT_CONTEXT pCertContext =
simpleCertificateChainWithinContext->rgpElement[simpleCertChainIndex]->pCertContext;

//-------------------------------------------------------------------
// Find and print the name of the subject of the certificate
// just retrieved.
TCHAR pszNameString[256];
if(CertGetNameString(
pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
pszNameString,
128))
{
wprintf(L"Certificate for %s has been retrieved.\n", pszNameString);
}
else
{
wprintf(L"CertGetName failed. \n");
}

// Get the issuer now...

if(CertGetNameString( pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
pszNameString,
128))
{
wprintf(L"Certificate issuer is %s.\n\n", pszNameString);
}
else
{
wprintf(L"CertGetName failed. \n");
}
}
}

//important! Free the CertCtx
CertFreeCertificateChain(CertCtx);
}
else
{
ErrorOut (GetLastError(), TEXT("HttpQueryInfo"));
}

The above code simply illustrates getting the CertContext.  I will leave the actual implementation and usage of this context to all of you Crypto API types!  Please drop me a message if you found this blog useful!