EternalWindows
証明書管理 / 証明書選択ダイアログ

前節で紹介したCryptUIDlgCertMgrは、証明書をGUIで表示できるものの、 特定の証明書を選択して、それを関数の呼び出し側に返すようなことはできませんでした。 今回紹介するCryptUIDlgSelectCertificate及びCryptUIDlgSelectCertificateFromStoreは、 次のような証明書を選択するためのダイアログを表示し、 選択した証明書をCERT_CONTEXT型で返すことができます。

このダイアログを簡単に表示したい場合は、CryptUIDlgSelectCertificateFromStoreを呼び出しますが、 CryptUIDlgSelectCertificateのほうがいくつか優れた機能を持っているため、 まずはこの関数の使い方について見ていきたいと思います。 CryptUIDlgSelectCertificateは、次のように定義されています。

PCCERT_CONTEXT WINAPI CryptUIDlgSelectCertificate(
  PCCRYPTUI_SELECTCERTIFICATE_STRUCT pcsc
);

pcscは、表示するダイアログの情報を含んでいるCRYPTUI_SELECTCERTIFICATE_STRUCT構造体の アドレスを指定します。 戻り値のPCCERT_CONTEXT型は選択された証明書を表し、 不要になった場合はCertFreeCertificateContextを呼び出して開放することになります。 CRYPTUI_SELECTCERTIFICATE_STRUCT構造体の dwFlagsメンバにCRYPTUI_SELECTCERT_MULTISELECTが指定されている場合は、 関数の戻り値はNULLとなります。

CRYPTUI_SELECTCERTIFICATE_STRUCT構造体の定義を次に示します。

typedef struct _CRYPTUI_SELECTCERTIFICATE_STRUCT {
  DWORD dwSize;
  HWND hwndParent;
  DWORD dwFlags;
  LPCTSTR szTitle;
  DWORD dwDontUseColumn;
  LPCTSTR szDisplayString;
  PFNCFILTERPROC pFilterCallback;
  PFNCCERTDISPLAYPROC pDisplayCallback;
  void* pvCallbackData;
  DWORD cDisplayStores;
  HCERTSTORE* rghDisplayStores;
  DWORD cStores;
  HCERTSTORE* rghStores;
  DWORD cPropSheetPages;
  LPCPROPSHEETPAGE rgPropSheetPages;
  HCERTSTORE hSelectedCertStore;
} CRYPTUI_SELECTCERTIFICATE_STRUCT,  *PCRYPTUI_SELECTCERTIFICATE_STRUCT;

dwSizeは、構造体のサイズをバイト単位で指定します。 hwndParentは、親ウインドウのハンドルを指定します。 親ウインドウが存在しない場合は、NULLを指定しても構いません。 dwFlagsは、0かCRYPTUI_SELECTCERT_MULTISELECTのいずれかを指定します。 CRYPTUI_SELECTCERT_MULTISELECTを指定した場合、証明書を複数選択できることになり、 その選択した証明書がhSelectedCertStoreに格納されることになります。 szTitleは、ダイアログのタイトルを表す文字列を指定します。 NULLを指定した場合「証明書の選択」というタイトルになります。 dwDontUseColumnは、ダイアログに追加したくないカラムを表す定数を指定します。 szDisplayStringは、ダイアログの説明文を表す文字列を指定します。 NULLを指定した場合「使用する証明書を選択してください」というタイトルになります。 pFilterCallbackは、ダイアログに表示する証明書を限定するために 利用するコールバック関数のアドレスを指定します。 pDisplayCallbackは、「証明書の表示」ボタンが押されたときに呼ばれる コールバック関数のアドレスを指定します。 pvCallbackDataは、先に示した2つのコールバック関数に渡すアプリケーション定義値を指定します。 cDisplayStoresは、rghDisplayStoresの要素数を指定します。 rghDisplayStoresは、ダイアログに表示したい証明書を格納した証明書ストアの配列を指定します。 cStoresは、rghStoresの要素数を指定します。 rghStoresは、証明のパスを構築する際に参照する証明書ストアの配列を指定します。 選択した証明書の発行者がシステムストアに存在しないものの、 その発行者の証明書が手元にあるような場合に利用します。 cPropSheetPagesは、rgPropSheetPagesの要素数を指定します。 rgPropSheetPagesは、PROPSHEETPAGE構造体の配列を指定します。 このメンバは、「証明書の表示」ボタンが押されたときに表示されるダイアログに 独自のタブを追加したい場合に利用します。 hSelectedCertStoreは、選択された証明書を格納する証明書ストアのハンドルを指定します。 このハンドルは、CertOpenStoreでメモリ上に作成した証明書のもとなるでしょう。 dwFlagsにCRYPTUI_SELECTCERT_MULTISELECTが指定されていない場合は、NULLを指定します。

dwDontUseColumnに指定できる定数を次に示します。

定数 フラグ
CRYPTUI_SELECT_ISSUEDTO_COLUMN 「発行先」を表示しない。
CRYPTUI_SELECT_ISSUEDBY_COLUMN 「発行者」を表示しない。
CRYPTUI_SELECT_INTENDEDUSE_COLUMN 「目的」を表示しない。
CRYPTUI_SELECT_FRIENDLYNAME_COLUMN 「フレンドリ名」を表示しない。
CRYPTUI_SELECT_LOCATION_COLUMN 「場所」を表示しない。
CRYPTUI_SELECT_EXPIRATION_COLUMN 「有効期限」を表示しない。

CryptUIDlgSelectCertificateには、証明書表示ダイアログの拡張、表示する証明書のフィルタ、 複数証明書の選択という主に3つの機能があります。 まず、証明書表示ダイアログの拡張ですが、 これにはpDisplayCallbackとrgPropSheetPagesが関わってきます。 pDisplayCallbackの型であるPFNCCERTDISPLAYPROCを次に示します。

BOOL WINAPI * PFNCCERTDISPLAYPROC(
  PCCERT_CONTEXT pCertContext,
  HWND hWndSelCertDlg,
  void* pvCallbackData
);

pCertContextは、選択された証明書が指定されます。 hWndSelCertDlgは、現在表示されている証明書選択ダイアログのウインドウハンドルが指定されます。 pvCallbackDataは、CRYPTUI_SELECTCERTIFICATE_STRUCTのpvCallbackDataメンバに指定した値です。 戻り値はTRUEのときに証明書表示ダイアログを表示せず、 FALSEのときには証明書表示ダイアログを表示されます。

pDisplayCallbackでTRUEを返すということは、既定でないオリジナルの証明書表示ダイアログを表示するということです。 このとき、そのダイアログの親ウインドウをhWndSelCertDlgとし、 ダイアログの内容はpCertContextの内容を考慮したものとなるでしょう。 FALSEを返した場合は、標準の証明書表示ダイアログが表示されることになりますが、 このダイアログに独自のプロパティシートを追加しているような場合は、 少し煩わしい問題が発生します。 それは、そのプロパティシートのプロシージャに、 選択された証明書が送られないという点です。 この場合、pDisplayCallbackで取得したpCertContextをグローバルに保存しておくなどの工夫が必要になるでしょう。 証明書表示ダイアログにプロパティシートを追加する方法は、次節で説明します。

今回のプログラムは、CryptUIDlgSelectCertificateを呼び出して、 証明書フィルタの機能を実現しています。 CryptUIDlgSelectCertificateのプロトタイプや構造体の定義は、 現在のWindows SDKのcryptuiapi.hには定義されていないため、 明示的にコード上に記述しています。

#include <windows.h>
#include <cryptuiapi.h>

#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "cryptui.lib")

typedef BOOL (WINAPI *PFNCCERTDISPLAYPROC)(PCCERT_CONTEXT, HWND, void *);

typedef struct _CRYPTUI_SELECTCERTIFICATE_STRUCT {
  DWORD dwSize;
  HWND hwndParent;
  DWORD dwFlags;
  LPCTSTR szTitle;
  DWORD dwDontUseColumn;
  LPCTSTR szDisplayString;
  PFNCFILTERPROC pFilterCallback;
  PFNCCERTDISPLAYPROC pDisplayCallback;
  void* pvCallbackData;
  DWORD cDisplayStores;
  HCERTSTORE* rghDisplayStores;
  DWORD cStores;
  HCERTSTORE* rghStores;
  DWORD cPropSheetPages;
  LPCPROPSHEETPAGE rgPropSheetPages;
  HCERTSTORE hSelectedCertStore;
} CRYPTUI_SELECTCERTIFICATE_STRUCT,  *PCRYPTUI_SELECTCERTIFICATE_STRUCT;

typedef PCCERT_CONTEXT (WINAPI *LPFNCRYPTUIDLGSELECTCERTIFICATE)(CRYPTUI_SELECTCERTIFICATE_STRUCT *);

BOOL WINAPI FilterProc(PCCERT_CONTEXT pCertContext, BOOL* pfInitialSelectedCert, void* pvCallbackData);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	LPFNCRYPTUIDLGSELECTCERTIFICATE  lpfnCryptUIDlgSelectCertificate;
	CRYPTUI_SELECTCERTIFICATE_STRUCT selectCert;
	HMODULE                          hmod;
	HCERTSTORE                       hStore;
	HCERTSTORE                       hSelectedStore;
	PCCERT_CONTEXT                   pContext;
	TCHAR                            szBuf[256];

	hmod = LoadLibrary(TEXT("cryptui.dll"));
	if (hmod == NULL)
		return 0;

	lpfnCryptUIDlgSelectCertificate = (LPFNCRYPTUIDLGSELECTCERTIFICATE)GetProcAddress(hmod, "CryptUIDlgSelectCertificateW");
	if (lpfnCryptUIDlgSelectCertificate == NULL) {
		FreeLibrary(hmod);
		return 0;
	}
	
	hStore = CertOpenSystemStore(0, TEXT("CA"));
	if (hStore == NULL) {
		FreeLibrary(hmod);
		return 0;
	}

	selectCert.dwSize             = sizeof(CRYPTUI_SELECTCERTIFICATE_STRUCT);
	selectCert.hwndParent         = NULL;
	selectCert.dwFlags            = 0;
	selectCert.szTitle            = NULL;
	selectCert.dwDontUseColumn    = 0;
	selectCert.szDisplayString    = NULL;
	selectCert.pFilterCallback    = FilterProc;
	selectCert.pDisplayCallback   = NULL;
	selectCert.pvCallbackData     = NULL;
	selectCert.cDisplayStores     = 1;
	selectCert.rghDisplayStores   = &hStore;
	selectCert.cStores            = 0;
	selectCert.rghStores          = 0;
	selectCert.cPropSheetPages    = 0;
	selectCert.rgPropSheetPages   = NULL;
	selectCert.hSelectedCertStore = NULL;

	pContext = lpfnCryptUIDlgSelectCertificate(&selectCert);
	if (pContext != NULL) {
		CertGetNameString(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szBuf, sizeof(szBuf));
		MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
		CertFreeCertificateContext(pContext);
	}

	FreeLibrary(hmod);
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);

	return 0;
}

BOOL WINAPI FilterProc(PCCERT_CONTEXT pCertContext, BOOL* pfInitialSelectedCert, void* pvCallbackData)
{
	PCERT_EXTENSION pExtension;

	pExtension = CertFindExtension(szOID_ENHANCED_KEY_USAGE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension);

	return pExtension != NULL ? TRUE : FALSE;
}

CRYPTUI_SELECTCERTIFICATE_STRUCT構造体で、最低限初期化しなければならないメンバは、 dwSizeメンバとcDisplayStoresメンバ、そしてrghDisplayStoresです。 rghDisplayStoresは、ダイアログに表示したい証明書ストアを指定し、 今回の場合はCAシステムストアの証明書が表示されることになります。 pFilterCallbackメンバにフィルタ関数のアドレスを指定しているのは、 証明書のフィルタ機能を利用するためです。

BOOL WINAPI FilterProc(PCCERT_CONTEXT pCertContext, BOOL* pfInitialSelectedCert, void* pvCallbackData)
{
	PCERT_EXTENSION pExtension;

	pExtension = CertFindExtension(szOID_ENHANCED_KEY_USAGE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension);

	return pExtension != NULL ? TRUE : FALSE;
}

このコールバック関数でFALSEを返した場合、pCertContextで示される証明書は ダイアログに追加されないことになっています。 今回は証明書を追加するかどうかの判断を、 その証明書が拡張鍵用途を持っているかどうかを基準にしています。 拡張鍵用途は証明書の拡張情報の1つで、いわばその証明書がどのような目的で使われるか表します。 CertFindExtensionの第1引数に拡張鍵用途を表すOIDを指定すると、 そのOIDで示される拡張情報のアドレスが返ることになるため、 これがNULLでないときは、その証明書は拡張鍵用途を含んでいるということになります。 ダイアログを実際に確認してみたら分かるように、 表示されている全ての証明書は何らかの目的を持っているはずです。

CryptUIDlgSelectCertificateFromStoreの利用

既に見てきたようにCryptUIDlgSelectCertificateの呼び出し方は複雑ですが、 証明書のフィルタや複数選択を必要としない場合は、 呼び出し方が簡単なCryptUIDlgSelectCertificateFromStoreを利用するとよいでしょう。

#include <windows.h>
#include <cryptuiapi.h>

#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "cryptui.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	HCERTSTORE     hStore;
	PCCERT_CONTEXT pContext;
	TCHAR          szBuf[256];
	
	hStore = CertOpenSystemStore(0, TEXT("CA"));
	if (hStore == NULL)
		return 0;
	
	pContext = CryptUIDlgSelectCertificateFromStore(hStore, NULL, L"タイトル", L"説明文", CRYPTUI_SELECT_FRIENDLYNAME_COLUMN, 0, NULL);
	if (pContext == NULL) {
		CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
		return 0;
	}

	CertGetNameString(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szBuf, sizeof(szBuf));
	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);

	CertFreeCertificateContext(pContext);
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);

	return 0;
}

第1引数は証明書ストアのハンドルです。 第2引数は、ダイアログの親ウインドウのハンドルですが、NULLを指定しても構いません。 第3引数と第4引数はその値が意味する通り、ダイアログのタイトルと説明文になります。 NULLを指定した場合、デフォルトのタイトルと説明文が表示されます。 第5引数は、ダイアログで無効にするカラムを表す定数を指定です。 第6引数と第7引数は使用されないため、0とNULLを指定します。 今回のCryptUIDlgSelectCertificateの呼び出しを上記のコードに置き換えても、 問題なく動作するはずです。



戻る