Introduction
Have you ever needed to install a self-signed certificate into the IE Certificate Cache from a 'remote' web browser? Here is some sample code which might make things easier.
Background
HTTPS testing can often be a pain. Every time you browse to your test web server, a challenge dialog appears, the SSL certificate is out of date, etc. Next, you want someone else to test your application. Ideally, they can run a self-registering program to make things simpler.
Using the code
The code is simple. The steps are simple. Open an HTTPS connection to a web server, get the SSL certificate, and install the certificate into the Certificate Cache. The provided code relies on the Windows HTTP Services interfaces as well as some Cryptography API calls. The function CwinHttpsCertDlg::Register()
opens a WinHTTP session, creates a connection, and sends a request. At this point, we query the request for the certificate. The certificate is then placed in a Certificate Store.
Note: for the sake of this sample code, the certificate in placed in the "Root" store. Please be careful when adding certificates to your cache, remove them when testing is complete. Protect your keys!
Here are some excerpts from the code to 'get' a certificate from a web server:
DWORD dwFlags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID|
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID|
SECURITY_FLAG_IGNORE_UNKNOWN_CA;
hSession = WinHttpOpen( szUserAgent,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0 );
hConnect = WinHttpConnect( hSession,
m_csServer,
INTERNET_DEFAULT_HTTPS_PORT,
0 );
hRequest = WinHttpOpenRequest( hConnect,
L"GET",
L"",
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE );
bRet = WinHttpSetOption(
hRequest,
WINHTTP_OPTION_SECURITY_FLAGS,
&dwFlags,
sizeof(DWORD)
);
bRet = WinHttpSendRequest( hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0 );
bRet = WinHttpQueryOption(
hRequest,
WINHTTP_OPTION_SERVER_CERT_CONTEXT,
&pCert,
&dwLen
);
We now have a handle to a certificate, load the certificate into a store:
hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM,
0,
0,
CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"Root");
bRet = CertAddCertificateContextToStore(
hCertStore,
pCert,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL
);
CertFreeCertificateContext(pCert);
bRet = CertCloseStore( hCertStore, 0 );
Finally, you might be able to use the following script to generate certificates. It's a bash script for a *NIX box with OpenSSL installed.
PREFIX="foxbat.swiftview.com"
mkdir ssl
cd ssl
openssl genrsa 1024 > ${PREFIX}.key
chmod 600 ${PREFIX}.key
openssl req -new -key ${PREFIX}.key > ${PREFIX}.csr
openssl req -x509 -days 365 -key ${PREFIX}.key -in ${PREFIX}.csr > ${PREFIX}.crt
echo "Add the following lines to httpd.conf"
echo "SSLCertificateFile /opt/apache/conf/ssl/${PREFIX}.crt"
echo "SSLCertificateKeyFile /opt/apache/conf/ssl/${PREFIX}.key"
Points of interest
One annoying issue with this code, the Platform SDK did not contain the WinHTTP headers for libs. You will need to install a newer version of the SDK to make this compile correctly.