EternalWindows
証明書管理 / 証明書の検索

証明書を列挙するのではなく、特定の証明書のみを取得したいような場合は、 CertFindCertificateInStoreを呼び出すことになります。

PCCERT_CONTEXT WINAPI CertFindCertificateInStore(
  HCERTSTORE hCertStore, 
  DWORD dwCertEncodingType, 
  DWORD dwFindFlags, 
  DWORD dwFindType, 
  const void *pvFindPara, 
  PCCERT_CONTEXT pPrevCertContext 
);

hCertStoreは、証明書ストアのハンドルを指定します。 dwCertEncodingTypeは、エンコーディングタイプを指定します。 エンコーディングタイプについては、現段階ではX509_ASN_ENCODINGを指定するものと考えてください。 dwFindFlagsは、dwFindTypeの値によって意味が決まりますが、基本的には0を指定します。 dwFindTypeは、証明書をどのように検索するかを示す定数を指定します。 pvFindParaは、検索をするためのデータを指定します。 pPrevCertContextは、検索に一致した証明書が複数ある場合に指定します。 CERT_FIND_ANYによる証明書の列挙及び、 CERT_FIND_ISSUER_STRで指定する発行者やCERT_FIND_SUBJECT_STRで指定する所有者を 含む証明書は複数存在することが考えられるため、 2回目以降のCertFindCertificateInStoreの呼び出しでは、 1つ前に取得した証明書が順番を示す指標となります。 戻り値は、検索に一致した証明書コンテキストが返ります。 NULLの場合は、適切な証明書が見つからなかったことを意味します。

dwFindTypeに指定できる定数の一部を次に示します。

定数 意味
CERT_FIND_ANY この定数は、CertFindCertificateInStoreで証明書の列挙を行うときに指定する。 pvFindParaはNULL。 2回目以降の呼び出しでは、pPrevCertContextに1つ前の証明書を指定する。
CERT_FIND_CERT_ID 発行者とシリアルナンバーを基に検索を行う。 pvFindParaは、CERT_ID構造体のアドレス。
CERT_FIND_ENHKEY_USAGE 特定の拡張鍵用途を含む証明書を取得する。 pvFindParaは、CERT_ENHKEY_USAGE構造体のアドレス。
CERT_FIND_EXISTING 指定した証明書と同一の証明書を検索する。 pvFindParaは、CERT_CONTEXT構造体のアドレス。
CERT_FIND_ISSUER_STR 指定した発行者名を含んでいる証明書を検索する。 pvFindParaは、発行者を表すUNICODE文字列。
CERT_FIND_SUBJECT_STR 指定した発行先名を含んでいる証明書を検索する。 pvFindParaは、証明書の発行先を表すUNICODE文字列。

証明書コンテキストが不要になった場合は、CertFreeCertificateContextで開放します。

BOOL WINAPI CertFreeCertificateContext(
  PCCERT_CONTEXT pCertContext 
);

pCertContextは、CERT_CONTEXT構造体のアドレスを指定します。

今回のプログラムは、指定した発行者名と一致する証明書を取得し、その証明書の発行先名を表示します。 発行者や発行先は、IEのインターネットオプションで「証明書」を選択することで確認できます。

#include <windows.h>

#pragma comment (lib, "crypt32.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 = CertFindCertificateInStore(hStore, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, L"Microsoft Internet Authority", NULL);
	if (pContext == NULL)
		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;
}

CertFindCertificateInStoreの呼び出しは、次のようになっています。

pContext = CertFindCertificateInStore(hStore, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, L"Microsoft Internet Authority", NULL);

第4引数がCERT_FIND_ISSUER_STRとなっていることから、 第5引数には証明書の発行者の名前をUNICODE文字列で指定することになります。 hStoreは、CAシステムストアを表しているため、 この証明書ストアから第5引数の名前を持った証明書が検索されます。 上記のコードは、同じ発行者を持った証明書が複数見つかったとしても、 常に最初に見つかった証明書だけを処理しようとしていますが、 残りの証明書も取得したいような場合は、次のようなコードを記述することになります。

pContext = NULL;
for (;;) {
	pContext = CertFindCertificateInStore(hStore, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, L"Microsoft Internet Authority", pContext);
	if (pContext == NULL)
		break;
	
	CertGetNameString(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szBuf, sizeof(szBuf));

	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
}

このコードでは、CertFindCertificateInStoreの最終引数に証明書コンテキストを指定しているため、 その指定された証明書の次に見つかった証明書が返ることになり、結果的に証明書の列挙を行えます。 1回目のCertFindCertificateInStoreの呼び出しでは、まだ証明書を見つけていないため、 pContextは事前にNULLで初期化しておくことになります。 CertFindCertificateInStoreに指定した最終引数は内部で開放されることになっているため、 この場合はCertFreeCertificateContextを呼び出す必要はありません。

実際のところ、CertFindCertificateInStoreの呼び出しによって、 複数の証明書が検索されるというのはあまり好ましいことではありません。 本来ならば、目的の証明書のみ単一で検索されることが理想です。 証明書は、発行者名とシリアルナンバーで一意に識別できるようになっているため、 CERT_FIND_CERT_IDを指定すれば確実に目的の証明書を検索することができようになります。

証明書コンテキストの作成

多くの場合、証明書は証明書ストアから取得しますが、 場合によっては証明書をファイルから取得することも考えられます。 たとえば、証明書ストアからエクスポートされた証明書がこの例に当てはまるでしょう。 ファイルとして格納されていたとしても、 証明書のバイトデータとサイズがあれば、CertCreateCertificateContextで 証明書コンテキストを作成することができます。

#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)
{
	HANDLE         hFile;
	LPBYTE         lpData;
	DWORD          dwSize;
	DWORD          dwReadByte;
	PCCERT_CONTEXT pContext;

	hFile = CreateFile(TEXT("sample.cer"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return 0;
	
	dwSize = GetFileSize(hFile, NULL);
	lpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);
	ReadFile(hFile, lpData, dwSize, &dwReadByte, NULL);
	
	pContext = CertCreateCertificateContext(X509_ASN_ENCODING, lpData, dwSize);

	CryptUIDlgViewContext(CERT_STORE_CERTIFICATE_CONTEXT, pContext, NULL, 0, 0, NULL);

	HeapFree(GetProcessHeap(), 0, lpData);
	CertFreeCertificateContext(pContext);
	CloseHandle(hFile);

	return 0;
}

このプログラムは、カレントディレクトリにsample.cerという証明書ファイルがあるものとし、 それをファイルサイズの値だけ読み取ります。 これにより、ファイルに格納されている全てのデータとそのサイズが取得できるため、 これをCertCreateCertificateContextに指定すれば、証明書コンテキストを取得することができます。 CryptUIDlgViewContextという関数は証明書の詳細を表示するダイアログで、 証明書コンテキストの1つの利用例としてこの関数を取り上げることにしています。 この関数の詳細については、後の節で説明します。 なお、CertCreateCertificateContextは、DER形式以外のバイトデータを変更することができないので、 その点は注意しておいてください。



戻る