EternalWindows
証明書管理 / 証明書エクスポートダイアログ

以前、証明書ストアの実体がレジストリキーやフォルダであることを取り上げたことがありました。 この事実は、そのレジストリキーやフォルダに直接アクセスすることで、 そこに格納されている証明書を参照できることを意味しているように思えますが、 実際にはこれは上手くいきません。 レジストリキーに格納されているバイナリデータ、フォルダに格納されているファイルは、 システムのみ理解できる形式で保存されているため、 それを証明書として取り出したい場合は、正規のエクスポート手順を踏まなければならないのです。 証明書のエクスポートとしては、IEのインターネットオプションから表示できる 次のようなダイアログが知られています。

このダイアログでは、証明書のエクスポート形式を決定し、 最終的には証明書をファイルとして保存することになります。 DERという形式は、証明書を様々な処理系で扱えるようにエンコードする形式で、最も一般的です。 base64は、メールの文章を暗号化して送信するS/MIMEというプロトコルで利用される形式です。 PKCS #7は、証明書の証明のパスに含まれる証明書を含むことができる形式です。 つまり、その証明書に署名を行ったCAの証明書が含まれます。 上図では、PKCS #12形式でのエクスポートが無効にされていますが、 エクスポートする証明書に含まれる公開鍵に対応する秘密鍵が存在する場合は例外です。 この場合、上図のダイアログが表示される前に、秘密鍵のエクスポートを行うかが問われ、 そこで「はい」を選択した場合は、PKCS #12形式でのエクスポートのみが可能となります。 PKCS #12形式でのエクスポート時にはパスワードの入力が必要であり、 それはインポート時に指定することになります。

アプリケーションから上記のようなエクスポートダイアログを 表示するには、CryptUIWizExportを呼び出すことになります。 この関数を明示的に呼び出すことにより、証明書以外にCRLのエクスポートや、 UIを表示しないエクスポートもできるようになります。

BOOL WINAPI CryptUIWizExport(
  DWORD dwFlags,
  HWND hwndParent,
  LPCWSTR pwszWizardTitle,
  PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo,
  void* pvoid
);

dwFlagsは、エクスポートに関する定数を指定します。 hwndParentは、ダイアログの親ウインドウとするウインドウハンドルを指定します。 pwszWizardTitleは、ダイアログのタイトルとする文字列を指定します。 pExportInfoは、エクスポートに関する情報を維持するCRYPTUI_WIZ_EXPORT_INFO構造体のアドレスを指定します。 pvoidは、dwFlagsにCRYPTUI_WIZ_NO_UIかCRYPTUI_WIZ_NO_UI_EXCEPT_CSPを指定し、 pExportInfo.dwSubjectChoiceにCRYPTUI_WIZ_EXPORT_CERT_CONTEXTを指定している場合に意味を持ちます。 この場合、UIを表示せずに証明書をエクスポートするということから、 本来UI経由で入力する情報をCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO構造体に設定しておくことになります。

現在、dwFlagsに指定できる定数としてヘッダーファイルに定義されているのはCRYPTUI_WIZ_NO_UIのみです。 しかし、WindowsVistaでは、CRYPTUI_WIZ_NO_UI以外の値も考慮されるようになっています。 それらの値を指定する場合は、次のような名前として定義するべきといえます。

定数 意味
CRYPTUI_WIZ_NO_UI (0x0001) 一切のUIを表示せずにエクスポートを行う。 ただし、実際にはCSPによって作成されたUIは表示されるようである。
CRYPTUI_WIZ_IGNORE_NO_UI_FLAG_FOR_CSPS (0x0002) リファレンスには、CSPによって作成されたUIを表示しないと書かれているが、 実際にはそのようなUIは表示される。
CRYPTUI_WIZ_NO_UI_EXCEPT_CSP (0x0003) 基本的には、一切のUIを表示せずにエクスポートを行うが、 CSPによって作成されたUIは表示する。
CRYPTUI_WIZ_EXPORT_PRIVATE_KEY (0x0100) 証明書にCSPが関連付けられている場合、 PFX形式で証明書をエクスポートすることを強制する。 秘密鍵をエクスポートするかを問うページが表示されることはない。
CRYPTUI_WIZ_EXPORT_NO_DELETE_PRIVATE_KEY (0x0200) 「正しくエクスポートされたときは秘密キーを削除する」 というチェックボックスを選択不可にする。 ちなみに、このチェックボックスにチェックが入っていた場合、 秘密鍵を格納する鍵コンテナ自体が削除される。 PFX形式で証明書をエクスポートする場合は、この定数は意味を持つ。

CSPによって作成されたUIというのは、恐らく次のようなダイアログを指すものと思われます。

このダイアログは証明書をPFX形式でエクスポートする場合に表示されることがあります。 具体的には、CryptGenKeyで鍵ペアを作成するときにCRYPT_USER_PROTECTEDやCRYPT_FORCE_KEY_PROTECTION_HIGHを 指定していた場合に表示されます。 このような定数は、秘密鍵へのアクセスが暗黙のうちに行われないように通知するのが目的で、 キャンセルボタンが選択された場合は、秘密鍵へのアクセスは本来ならば失敗することになります。 ところが、不思議なことに、WindowsVistaではキャンセルボタンを選択してもエクスポートが成功してしまいます。

CRYPTUI_WIZ_EXPORT_INFO構造体は、次のように定義されています。

typedef struct _CRYPTUI_WIZ_EXPORT_INFO {
  DWORD dwSize;
  LPCWSTR pwszExportFileName;
  DWORD dwSubjectChoice;
  union {
    PCCERT_CONTEXT pCertContext;
    PCCTL_CONTEXT pCTLContext;
    PCCRL_CONTEXT pCRLContext;
    HCERTSTORE hCertStore;
  };
  DWORD cStores;
  HCERTSTORE* rghStores;
} CRYPTUI_WIZ_EXPORT_INFO,  *PCRYPTUI_WIZ_EXPORT_INFO;

dwSizeは、構造体のサイズをバイト単位で指定します。 pwszExportFileNameは、ファイルとして保存する際の名前を指定します。 UI経由でエクスポートする場合は、保存先ファイルを決定できるため、 このメンバを利用することは必須ではありません。 dwSubjectChoiceは、何をエクスポートするのかを表す定数を指定します。 詳しくは、共用体と共に後述します。 cStoresは、rghStoresの要素数を指定します。 rghStoresは、証明書チェーンを作成する際に参照する証明書ストアの配列を指定します。 dwSubjectChoiceがCRYPTUI_WIZ_EXPORT_CERT_CONTEXTである場合のみ意味を持ちます。

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

定数 意味
CRYPTUI_WIZ_EXPORT_CERT_CONTEXT 証明書をエクスポートすることを意味する。 pCertContextメンバを初期化することになる。
CRYPTUI_WIZ_EXPORT_CTL_CONTEXT CTLをエクスポートすることを意味する。 pCTLContextメンバを初期化することになる。
CRYPTUI_WIZ_EXPORT_CRL_CONTEXT CRLをエクスポートすることを意味する。 pCRLContextメンバを初期化することになる。
CRYPTUI_WIZ_EXPORT_CERT_STORE 証明書ストアをエクスポートすることを意味する。 hCertStoreメンバを初期化することになる。
CRYPTUI_WIZ_EXPORT_CERT_STORE_CERTIFICATES_ONLY 証明書ストアをエクスポートすることを意味するが、その証明書ストアには証明書以外は含まれない。 hCertStoreメンバを初期化することになる。

今回のプログラムは、CryptUIWizExportを呼び出してUI経由で証明書をエクスポートします。

#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;
	CRYPTUI_WIZ_EXPORT_INFO exportInfo;
	
	hStore = CertOpenSystemStore(0, TEXT("MY"));
	if (hStore == NULL)
		return 0;

	pContext = CryptUIDlgSelectCertificateFromStore(hStore, NULL, NULL, NULL, 0, 0, NULL);
	if (pContext == NULL)
		return 0;

	exportInfo.dwSize             = sizeof(CRYPTUI_WIZ_EXPORT_INFO);
	exportInfo.pwszExportFileName = NULL;
	exportInfo.dwSubjectChoice    = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT;
	exportInfo.pCertContext       = pContext;

	CryptUIWizExport(0, NULL, NULL, &exportInfo, NULL);

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

	return 0;
}

CryptUIWizExportを呼び出すためには、エクスポート対象とする証明書を事前に取得しておかなければなりません。 今回は、CryptUIDlgSelectCertificateFromStoreを呼び出して、MY証明書ストアに格納されている 証明書をUIで選択しようとしています。 別の証明書ストアを対象とする場合は、CertOpenSystemStoreの第2引数をCAやRootに変えることになるでしょう。 CryptUIWizExportの呼び出しに関わる部分を次に示します。

exportInfo.dwSize             = sizeof(CRYPTUI_WIZ_EXPORT_INFO);
exportInfo.pwszExportFileName = NULL;
exportInfo.dwSubjectChoice    = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT;
exportInfo.pCertContext       = pContext;

CryptUIWizExport(0, NULL, NULL, &exportInfo, NULL);

pwszExportFileNameメンバは、エクスポート先とするファイル名の初期値であり、NULLの場合は初期値がありません。 このため、UI上においてエクスポート先とするファイル名を明示的に決定することになります。 今回のエクスポート対象となるのは証明書であるため、 dwSubjectChoiceメンバにCRYPTUI_WIZ_EXPORT_CERT_CONTEXTを指定し、 pCertContextメンバに証明書のコンテキストを指定しています。

今回のプログラムでは、MY証明書ストアの証明書をエクスポートしようとしていますが、 そもそも、このような操作はどのような場合に必要となるでしょうか。 1つは、証明書を通信相手のコンピュータへ送る場合が考えられます。 関連する秘密鍵を持った証明書は署名を行うことができ、 この署名を検証するには秘密鍵に関連する公開鍵が必要になるため、 自分の証明書を通信相手に送ることになります。 もう1つは、証明書をバックアップしたり、別コンピュータへインポートしたい場合です。 このような場合、証明書はPKCS #12形式でエクスポートすることになるでしょう。 PKCS #12形式では、証明書と秘密鍵の両方が含まれるため、 インポート先でも秘密鍵を使った操作が行えることになります。 PKCS #12形式の証明書を自分以外の相手に送ることはありません。 自己署名入り証明書の作成手順。


戻る