Recently I've been doing quite a bit of work with TLS\SSL communications and the
Windows Cryptography API in general. In one project I needed to present a dialog which
allowed the end user to specify a certificate to use which was stored in one of
the Certificate stores which Windows uses for persistence. To obtain the
functionality I wanted, I ended up writing code which GetProcAddress'ed
for 3 Windows Crypto API's namely "CryptUIDlgSelectCertificateFromStore" (which
is only available on XP or 2003), "CertSelectCertificate" (which is available on
Windows 2000 but which seems to be deprecated) and finally
"CryptUIDlgSelectCertificateFromStore" (which also requires XP or 2003). Because
the code would potentially be running on Windows 2000, I had to fall back on
"CertSelectCertificate" as a last resort. This function unfortunately does not
provide a way to customize which certificates are initially shown selected. I
also tried getting the selection mechanism to work correctly using the
pFilterCallback member of the structure you pass to "CryptUIDlgSelectCertificate"
but it did not seem to work and ignored what settings I had passed to it. As
time was tight with the project and there seemed to be very little samples for
this area of the CryptoAPI, I decided to put up with the limitations of all the
built in dialogs and vowed that I would revisit this area when I got time.
Thus was born this class which addresses all my perceived limitations of
these built in dialogs. Here's what the dialog looks like running on a computer which has had a number of
certificates installed:
The enclosed zip file contains
the source and a sample dialog based appwizard generated application to show how to use it.
Features
- Simple CDialog based class which integrates with MFC and which implements the certificate selection
functionality.
- All the code is fully UNICODE compliant.
- The dialog is wider than the built in dialogs meaning that there is less
issues with column names being clipped.
- All localizable content for the dialog is taken from the resources,
meaning that the class is easily localizable.
- The caption and prompt text can be customized
("CSelectCertificateDlg::m_sTitle" &
"CSelectCertificateDlg::m_sDisplayString"). If no values are provided for
these, then the standard values as shown in the screen capture above (and
the same as the built in dialogs) will be used.
- The details of the certificate can be examined via the "View
Certificate" button which uses the Windows Cryptography function "CryptUIDlgViewCertificate".
You can programmatically exclude the button by using
"CSelectCertificateDlg::m_bHideViewCertificateButton". When the "View
Certificate" button is not being shown, the code automatically moves over
the "OK" and "Cancel" buttons to the right to preserve the standard UI look.
- The presence or absence of each column can be customized through
"CSelectCertificateDlg::m_bExclude*Column". Please note that unlike the
built in dialogs, CSelectCertificateDlg does not show the "Location" column
as I was unable to find any documentation on what exactly this column
showed. In all the tests I did it always displayed "Not Available". If you
know what should go in here, then please drop me a note and I can easily add
it in for the next release.
- The code tries to match the UI of the built in dialogs as much as
possible, for example the list control uses an image list and the general
location of all the UI elements is much the same.
- Which certificate store's certificates are shown is determined by
"CSelectCertificateDlg::m_hCertStore".
- The dialog can operate in a multi select or single select mode. High
level helper functions are provided
(CSelectCertificateDlg::GetCertificatesShownCount, CSelectCertificateDlg::GetCertificateShown
and CSelectCertificateDlg::GetNextSelectedCerfificateShown) to get the
dialog state after it has been shown.
- The class performs automatic clean up of all certificate resources it
uses. You should read the various notes in "SelectCertificateDlg.h" on
who is responsible for the lifetime management of the various resources
which the class provides access to through the functions discussed in the
previous point.
- Label Tips are enabled on the list control (unlike the built in
dialogs!) meaning that column text which overflows the column boundaries are
shown as tooltips. This is especially important for the "Intended Purposes"
column which can contain 30+ enhanced key usage strings. In addition the
code to display the enhanced key usage strings should provide a good
tutorial on how you can obtain these various properties from a certificate
using the CryptoAPI.
- Provides a production quality sample on how to use the various Crypto
Certificate and Certificate Store APIs. This area seems to be sorely lacking
good examples for the novice developer who has to deal with the complicated
area of the Windows Cryptography API.
- As mentioned in the intro above. CSelectCertificateDlg provides a key
feature which is missing from the built in dialogs, namely customization of
the initial selection state. If you would like to select a specific single
certificate then you can use CSelectCertificateDlg::SetInitialSelectedCert.
If you do not provide a certificate, then by default the first certificate
enumerated will be shown selected (which is the same behaviour as the built
in dialogs). If neither of these modes or operation are appropriate, then
almost all of the class's functionality can be customized through the use of
virtual functions. For further info, please take a look at the functions AddCertificate, AddCertificateToUI and EnumerateCertificates functions.
- Finally since the code is open source, all the code is provided, so even
if you are happy using one of the built in Certificate selection API calls
instead of using CSelectCertificateDlg, you can see through example how each
of them would implement their functionality (of course the MS implementation
would probably be developed directly to the Windows API instead of using the
MFC framework!).
Copyright
- You are allowed to include the source code in any product (commercial, shareware,
freeware or otherwise) when your product is released in binary form.
- You are allowed to modify the source code in any way you want except you
cannot modify the copyright details at the top of each module.
- If you want to distribute source code with your application, then you are
only allowed to distribute versions released by the author. This is to maintain
a single distribution point for the source code.
Updates
v1.14 (16 April 2022)
- Updated copyright details.
- Updated the code to use C++ uniform initialization for all variable
declarations.
v1.13 (29 November 2021)
- Fixed more Clang-Tidy static code analysis warnings in the code.
v1.12 (10 May 2020)
- Fixed more Clang-Tidy static code analysis warnings in the code.
v1.11 (27 March 2020)
- Updated copyright details.
- Fixed more Clang-Tidy static code analysis warnings in the code.
- Reimplemented m_Certificates & m_CertificateSelectionState arrays using
std::vector.
v1.10 (29 December 2019)
- Fixed various Clang-Tidy static code analysis warnings in the code.
v1.09 (12 November 2019)
- Updated initialization of various structs to use C++ 11 list
initialization
- Replaced BOOL with bool in a number of places throughout the codebase
v1.08 (21 May 2019)
- Updated copyright details.
- Updated the code to clean compile on VC 2019
v1.07 (23 September 2018)
- Updated copyright details.
- Fixed a number of C++ core guidelines compiler warnings. These changes
mean that the code will now only compile on VC 2017 or later.
v1.06 (1 May 2017)
- Updated copyright details.
- Updated the code to compile cleanly using /permissive-.
v1.05 (19 November 2015)
- Verified the code compiles cleanly on VC 2015.
- Updated the sample apps main icon.
- Updated copyright details.
- CryptUIDlgViewCertificate is now referenced via import library
rather than by using GetProcAddress.
- Separator used for "Intended Use" column is now setup by calling
GetLocaleInfo.
v1.04 (18 January 2015)
- Updated copyright details.
- Updated the code and sample app to clean compile on VC 2010 - VC 2013
v1.03 (3 November 2012)
- Updated copyright details.
- Updated the sample app to open the system certificate store in read only
mode. This avoids access denied errors on Windows Vista and later.
- Code no longer uses LoadLibrary without an absolute path when loading
cryptui DLL. This avoids DLL planting security issues.
v1.02 (28 December 2008)
- Updated copyright details
- Removed VC 6 style AppWizard comments from the code
- The code has now been updated to support VC 2005 or later only
- Code now compiles cleanly using Code Analysis (/analyze)
- Updated code to compile correctly using _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
define
- Added explicit support for the following OID's:
szOID_PKIX_KP_OCSP_SIGNING, szOID_PKIX_OCSP_NOCHECK, szOID_PKIX_OCSP_NONCE,
szOID_KP_CTL_USAGE_SIGNING, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE,
szOID_KP_MOBILE_DEVICE_SOFTWARE, szOID_KP_SMART_DISPLAY,
szOID_KP_CSP_SIGNATURE, szOID_DRM_INDIVIDUALIZATION &
szOID_KP_KERNEL_MODE_CODE_SIGNING
v1.01 (5 July 2007)
- Updated copyright messages.
- Updated the sample app to clean compile on VC 2005.
- Addition of a CSELECTCERTIFICATEDLG_EXT_CLASS preprocessor macro which
makes the class easier to use in an extension dll.
v1.0 (27 August 2006)