Selecting a client certificate

Environment: VC6 NT4 IE 4,5

This article covers the basics of selecting a client certificate for use within WinInet. I have put this text together, in the hope it may prevent others suffering the hair loss I have during the last week.

First of all I am assuming you know the basics of setting up a HTTPS connection, bellow is a brief outline to refresh your memory. The INTERNET_FLAG_SECURE flag can be set within the InternetConnect command to force the use of SSL.


    // Initialize wininet.
    hOpen = InternetOpen (...)
    
    // connect to remote server
    hConnect = InternetConnect ( hOpen, strServerName , ....)

    // Open a HTTP request
    hReq = HttpOpenRequest (hConnect, "GET", strObject, ....)

    
    // Send request
    HttpSendRequest (hReq, ....)

    // read results
    InternetReadFile (hReq, bBuf, cbBuf, &cbRead)
    
    // use the returned HTML page
    printf(bBuf);

If the server that we are making the request to requires a client certificate then the HttpSendRequest function will fail with the error ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED. At this point we must select a certificate.

There are basically 2 ways of selecting a certificate. First (the easiest, and the way IE does it), is to ask the user. See Dialog bellow.

This can be done programmatically using the following code:-


...
while ( !HttpSendRequest( hReq, NULL, 0, NULL, 0 ) )
{
    dwError = GetLastError();
    if ( dwError == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED )
    {
       // Return ERROR_SUCCESS regardless of clicking on OK or Cancel
       if( InternetErrorDlg( GetDesktopWindow(), 
                             hReq,
                             ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
                             FLAGS_ERROR_UI_FILTER_FOR_ERRORS       |
                             FLAGS_ERROR_UI_FLAGS_GENERATE_DATA     |
                             FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, 
                             NULL) != ERROR_SUCCESS )
       {
           return ;
       }
    }
}
... 

See Q224282 for more details.

However if you dont want the user to see the dialog, then you will need to actually select the certificate programmatically. This sounds simple but, due to the extreme lack of documentation becomes something of a nightmare.

Anyhow it can basically be accomplished using InternetSetOptions INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT flag. This, you may notice has no documentation. The sample code for using this flag is included. But the guts of it are here:-


if (!HttpSendRequest (hReq, NULL, 0, NULL, 0) )
    {
        dwError = GetLastError () ;
        if ( dwError == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED)
        {
            //This works but there is no way to iterate through the client certs
            DWORD dwCert = 0;
            InternetSetOption(hReq, INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT, 
                              &dwCert, sizeof(dwCert));

           goto again;
        }
    }

This will select the first client certificate registered with WinInet. The bad news is if there are more than one Cert on the machine then you dont know which one you got!

As the most common reason for wanting to avoid the selection dialog is because the application is running as a service, I will provide a link to an MS article that describes how to set the registry up to cope with finding the certs when running as system. Q190542.

I will also include some code I picked up from a news group posting with regards to enumerating the client certificates installed. Although this is inrelated to the article directly, it may prove useful. 

I would also like to thank Scott Davis. For his Dev Studio HTML Plugin.

Downloads

Download source - 2KB