EternalWindows
証明書管理 / 証明書ストアの保存と登録

証明書を証明書ストアから取り出し、それをファイルとして保存することはよくありますが、 証明書ストアに入っている全ての証明書を保存するつもりなのであれば、 証明書ストア自体を保存したほうが効率的であるといえます。 証明書ストアを保存するには、CertSaveStoreを呼び出します。

BOOL WINAPI CertSaveStore(
  HCERTSTORE hCertStore, 
  DWORD dwMsgAndCertEncodingType, 
  DWORD dwSaveAs, 
  DWORD dwSaveTo, 
  void *pvSaveToPara, 
  DWORD dwFlags 
);

hCertStoreは、保存したい証明書ストアのハンドルを指定します。 dwMsgAndCertEncodingTypeは、エンコーディングタイプを指定します。 dwSaveAsは、CERT_STORE_SAVE_AS_STORE、 またはCERT_STORE_SAVE_AS_PKCS7を指定します。 dwSaveToは、pvSaveToParaに指定する値の種類を指定します。 pvSaveToParaは、証明書ストアの保存先を指定します。 dwFlagsは、予約されているため0を指定します。

dwSaveToには、CERT_STORE_SAVE_TO_FILEとCERT_STORE_SAVE_TO_MEMORYのいずれかを指定することができます。 前者の場合、pvSaveToParaにはオープンされているファイルハンドルを指定し、 後者の場合は、pvSaveToParaにCRYPT_DATA_BLOB構造体のアドレスを指定することになります。 次に、証明書ストアをファイルに保存する例を次に示します。

#include <windows.h>

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	HCERTSTORE hStore;
	HANDLE     hFile;
	
	hFile = CreateFile(TEXT("sample.sto"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return 0;

	hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG, TEXT("CA"));
	if (hStore == NULL)
		return 0;

	CertSaveStore(hStore, 0, CERT_STORE_SAVE_AS_STORE, CERT_STORE_SAVE_TO_FILE, hFile, 0);
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
	CloseHandle(hFile);
	
	MessageBox(NULL, TEXT("証明書ストアを保存しました。"), TEXT("OK"), MB_OK);

	return 0;
}

証明書ストアをファイルに保存するには、保存対象となるファイルハンドルが必要です。 これは、CreateFileに書き込みを表すGENERIC_WRITE指定して取得します。 作成するファイルの名前の拡張子は.stoとなっていますが、 証明書ストアに専用の拡張子は存在しないため、自由に変更して構いません。 次に、保存する証明書ストアのハンドルを取得するために、CertOpenStoreを呼び出します。 CertOpenSystemStoreが返すハンドルは、ここでは無効とされるようなので注意してください。 CertSaveStoreが制御を返した時点ではまだファイルへの書き込みは行われず、 CertCloseStoreの呼び出しによって実際に書き込みが行われます。 なお、PKCS #7形式として保存したい場合は、第2引数にX509_ASN_ENCODING | PKCS_7_ASN_ENCODINGを指定し、 第3引数にCERT_STORE_SAVE_AS_PKCS7を指定します。 また、ファイルの拡張子も.p7bにすることになるでしょう。

保存した証明書ストアを既存システムストアの物理ストアに追加すれば、 証明書を間接的に追加できるように思えますが、残念ながらこれは上手くいきません。 後述するCertRegisterPhysicalStoreは、レジストリにPhysicalStoresというサブキーを作成しますが、 物理ストアを列挙するCertEnumPhysicalStoreは、 既存システムストアが指定されている場合においては、 明示的に追加された物理ストアを考慮しないようになっているようです。 ただし、CertRegisterSystemStoreの呼び出しなどによって明示的に作成されたシステムストアは、 明示的に追加された物理ストアを考慮します。

BOOL WINAPI CertRegisterSystemStore(
  const void *pvSystemStore, 
  DWORD dwFlags, 
  PCERT_SYSTEM_STORE_INFO pStoreInfo, 
  void *pvReserved 
);

pvSystemStoreは、作成するシステムストアの名前を指定します。 dwFlagsは、システムストアの位置を表す定数を指定します。 pStoreInfoとpvReservedは、共にNULLを指定します。 ちなみに、システムストアの明示的な作成は、 次のようにCertOpenStoreを呼び出すことで実現することもできます。

hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, szSystemStore);

CERT_STORE_PROV_SYSTEMを指定してCertOpenStoreを呼び出せば、 それはシステムストアのオープンと解釈されます。 CertOpenStoreの既定の動作では、システムストアが存在しない場合は新しくそれを作成します。 また、CertRegisterSystemStoreで作成したシステムストアが、 .Default物理ストアのサブキーを作成しないのに対して、 CertOpenStoreが作成するシステムストアはそれを作成します。

物理ストアを明示的に追加するには、CertRegisterPhysicalStoreを呼び出します。 この関数を呼び出すことによって、 システムストアを表すレジストリキーの下にPhysicalStoresというサブキーが作成され、 さらにその下に物理ストアを表すレジストリキーが作成されます。

BOOL WINAPI CertRegisterPhysicalStore(
  const void *pvSystemStore, 
  DWORD dwFlags, 
  LPCWSTR pwszStoreName, 
  PCERT_PHYSICAL_STORE_INFO pStoreInfo, 
  void *pvReserved 
);

pvSystemStoreは、物理ストアを追加したいシステムストアの名前を指定します。 dwFlagsは、システムストアの位置を表す定数を指定します。 pwszStoreNameは、追加する物理ストアの名前を指定します。 pStoreInfoは、CERT_PHYSICAL_STORE_INFO構造体のアドレスを指定します。 pvReservedは、NULLを指定します。 CERT_PHYSICAL_STORE_INFO構造体は、次のように定義されています。

typedef struct _CERT_PHYSICAL_STORE_INFO {
  DWORD               cbSize;
  LPSTR               pszOpenStoreProvider;
  DWORD               dwOpenEncodingType;
  DWORD               dwOpenFlags;
  CRYPT_DATA_BLOB     OpenParameters;
  DWORD               dwFlags;
  DWORD               dwPriority;
} CERT_PHYSICAL_STORE_INFO, *PCERT_PHYSICAL_STORE_INFO;

cbSizeは、構造体のサイズをバイト単位で指定します。 pszOpenStoreProviderは、ストアプロバイダタイプを指定します。 dwOpenEncodingTypeは、エンコーディングタイプを指定します。 dwOpenFlagsは、追加した物理ストアをCertOpenStoreでオープンするときのフラグを指定します。 OpenParametersは、追加した物理ストアをCertOpenStoreでオープンするときのパラメータを指定します。 dwFlagsは、CERT_PHYSICAL_STORE_ADD_ENABLE_FLAGを指定します。 dwPriorityは、物理ストアの優先順位を指定します。

今回のプログラムは、まずCertRegisterSystemStoreでシステムストアを作成し、 その後にCertRegisterPhysicalStoreで物理ストアを作成します。 物理ストアのストアプロバイダはCERT_STORE_PROV_FILENAMEとし、 これは証明書ストアがファイルに格納されていることを前提としているため、 事前にCertSaveStoreで証明書ストアを保存しておいてください。

#include <windows.h>

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	DWORD                    dwLocation = CERT_SYSTEM_STORE_CURRENT_USER;
	TCHAR                    szSystemStore[] = TEXT("samplesys");
	WCHAR                    szPhysicalStore[] = L"samplephysi";
	char                     szFilePath[] = "c:\\sample.sto";
	BOOL                     bResult;
	CERT_PHYSICAL_STORE_INFO physicalStore;

	bResult = CertRegisterSystemStore(szSystemStore, dwLocation, NULL, NULL);
	if (!bResult) {
		MessageBox(NULL, TEXT("システムストアの作成に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}

	physicalStore.cbSize                = sizeof(CERT_PHYSICAL_STORE_INFO);
	physicalStore.pszOpenStoreProvider  = (LPSTR)CERT_STORE_PROV_FILENAME_A;
	physicalStore.dwOpenEncodingType    = 0;
	physicalStore.dwOpenFlags           = CERT_STORE_OPEN_EXISTING_FLAG;
	physicalStore.OpenParameters.cbData = sizeof(szFilePath);
	physicalStore.OpenParameters.pbData = (PBYTE)szFilePath;
	physicalStore.dwFlags               = CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG;
	physicalStore.dwPriority            = 1;
	
	bResult = CertRegisterPhysicalStore(szSystemStore, dwLocation, szPhysicalStore, &physicalStore, NULL);
	if (!bResult) {
		CertUnregisterSystemStore(szSystemStore, dwLocation);
		MessageBox(NULL, TEXT("物理ストアの作成に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}

	MessageBox(NULL, TEXT("証明書ストアを作成しました。"), TEXT("OK"), MB_OK);

	return 0;
}

初期化された変数の値から分かるように、 このプログラムはシステムストアの名前をsamplesysとし、 物理ストアの名前をsamplephysiとしています。 よって、実行結果は次のようにレジストリキーに反映されます。

見て分かるように、SystemCertificates以下にsamplesysというシステムストアが作成され、 さらにその下にsamplephysiという物理ストアが作成されています。 ただし、この物理ストアはストアプロバイダタイプがCERT_STORE_PROV_FILENAMEであるため、 実際にレジストリに作成された物理ストアに証明書が格納されるわけではありません。 あくまで証明書は、OpenParametersが示すc:\\sample.stoに格納されています。 ファイル名だけでは位置を特定できないため、必ずファイルパスに設定しておきます。

物理ストアをレジストリに作成するのは、あくまで他のアプリケーションからのオープンを想定してのことです。 ファイルに保存した証明書ストアを操作するのが自作アプリケーションだけであれば、 CertOpenStoreの引数に何を指定すればよいのかが分かりますから、 システムストアも物理ストアも作成する必要はありません。 レジストリエントリとして格納されている値は、 CertRegisterPhysicalStoreの呼び出しに使ったCERT_PHYSICAL_STORE_INFO構造体の値を反映し、 他のアプリケーションからのCertOpenStore呼び出しのヒントとなります。

physicalStore.cbSize                = sizeof(CERT_PHYSICAL_STORE_INFO);
physicalStore.pszOpenStoreProvider  = (LPSTR)CERT_STORE_PROV_FILENAME_A;
physicalStore.dwOpenEncodingType    = 0;
physicalStore.dwOpenFlags           = CERT_STORE_OPEN_EXISTING_FLAG;
physicalStore.OpenParameters.cbData = sizeof(szFilePath);
physicalStore.OpenParameters.pbData = (PBYTE)szFilePath;
physicalStore.dwFlags               = CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG;
physicalStore.dwPriority            = 1;

pszOpenStoreProviderはストアプロバイダであり、 ファイルに証明書ストアが格納されていることを示すCERT_STORE_PROV_FILENAME_Aを指定しています。 CERT_STORE_PROV_FILENAMEと指定するとCERT_STORE_PROV_FILENAME_Wと解釈されるため、 ファイルのパスをchar型で宣言する場合は、明示的に_Aをつけるようにします。 当然ながら、CERT_STORE_PROV_FILENAMEとしても何の問題もありません。 dwOpenEncodingTypeはエンコーディングタイプであり、 OpenParametersで指定したデータを暗号化するための定数を指定します。 CERT_STORE_PROV_FILENAMEではdwOpenEncodingTypeは有効なメンバであり、 ファイルのパスがそのままレジストリエントリとして書き込まれないように エンコーディングタイプを指定すべきなのかもしれませんが、今回は0を指定します。 dwOpenFlagsは、作成した物理ストアをオープンするときに、 どのようなフラグを指定してオープンすべきなのかを指定します。 OpenParametersは、ファイルのパスとサイズをそれぞれ指定します。 dwFlagsはCERT_PHYSICAL_STORE_ADD_ENABLE_FLAGを指定するべきであり、 優先順位表すdwPriorityは1で問題ありません。

物理ストアを削除したい場合は、CertUnregisterPhysicalStoreを呼び出してください。

CertUnregisterPhysicalStore(szSystemStore, CERT_SYSTEM_STORE_CURRENT_USER, szPhysicalStore);

第1引数がシステム名で第2引数がその位置、第3引数が物理ストアの名前となります。 この関数を呼び出した結果、明示的に追加した物理ストアが全て存在しなくなっても、 PhysicalStoresサブキー自体は削除されることはありません。

最後に、CertEnumPhysicalStoreで呼び出されるコールバック関数内で、 物理ストアをオープンする方法について説明します。 他のアプリケーションはこの関数を通じて物理ストアを発見すると思われます。

BOOL WINAPI CertEnumPhysicalStoreCallback(const void *pvSystemStore, DWORD dwFlags, LPCWSTR pwszStoreName, PCERT_PHYSICAL_STORE_INFO pStoreInfo, void *pvReserved, void *pvArg)
{
	HCERTSTORE hStore;

	hStore = CertOpenStore(pStoreInfo->pszOpenStoreProvider, pStoreInfo->dwOpenEncodingType, 0, pStoreInfo->dwOpenFlags, pStoreInfo->OpenParameters.pbData);
	・
	・
	・	
	return TRUE;
}

CertEnumPhysicalStoreCallbackで得られるCERT_PHYSICAL_STORE_INFO構造体には、 CertRegisterPhysicalStoreで初期化した通りの値が格納されているはずです。 後は、この構造体のメンバをCertOpenStoreの適切な引数に指定すれば、 引数の値を意識することなく証明書ストアをオープンすることができます。


戻る