EternalWindows
証明書検証 / CRLの役割

証明書を発行したCAは、ときとしてその証明書を利用できないようにすることがあります。 たとえば、証明書に対応する秘密鍵をユーザーが紛失したような場合、 意図しない第三者がその秘密鍵を利用することが考えられますから、 不特定多数の者が公開鍵でデータを暗号化することのないように、証明書を利用できないようにしておく必要があります。 このような利用できなくなった証明書は失効された証明書と呼ばれ、 証明書の検証時には無効な証明書として扱われることになります。

証明書を失効させるというのは、証明書自体に失効されたことを示す値を付けることではありません。 CAがCRL(証明書失効リスト)というものを作成し、そこに失効させたい証明書のシリアルナンバーを追加しているのです。 このようにすれば、CRLを確認することで、どの証明書が失効されているのかを特定することができます。 次の図は、CRLをダイアログで表示したものです。

バージョンは、CRLのバージョンとなります。これは、基本的に2となります。 発行者は、CRLを作成したCAの名前になります。 有効開始日は、このCRLが有効になる日時を表します。 次の更新予定は、新しいCRLが発行される日時を表します。 署名アルゴリズムは、CRLの作成時に使用した署名アルゴリズムを指定します。 残りの基本制限やキー使用法は、拡張情報の一種です。 CRLも証明書と同じように拡張情報を持つことができます。

失効リストタブでは、失効された証明書のシリアルナンバーと失効日が確認できます。 この2つの情報は失効エントリとして存在し、 失効された証明書の数だけ失効エントリは存在します。 また、失効された証明書の発行者は、CRLの発行者と必ず一致します。 つまり、CAは、自身が発行していない証明書をCRLに追加するようなことがあってはいけません。 証明書は、シリアルナンバーと発行者名で一意に識別することができますから、 失効エントリに格納されているシリアルナンバーとCRL情報の発行者を使用して、 失効された証明書のCERT_CONTEXTを取得することができます。 ちなみに、CRLは順次、新しいものが発行されていきますから、 その時々によっては失効されている証明書が存在しないこともあります。

アプリケーション内では、CRLをCRL_CONTEXT型として表すことになっています。 この構造体は、次のように定義されています。

typedef struct _CRL_CONTEXT {
  DWORD           dwCertEncodingType;
  BYTE*           pbCrlEncoded;
  DWORD           cbCrlEncoded;
  PCRL_INFO       pCrlInfo;
  HCERTSTORE      hCertStore;
} CRL_CONTEXT, *PCRL_CONTEXT;
typedef const CRL_CONTEXT *PCCRL_CONTEXT;

dwCertEncodingTypeは、エンコーディングタイプを指定します。 pbCrlEncodedは、CRLのバイトデータを指定します。 cbCrlEncodedは、pbCrlEncodedのサイズを指定します。 pCrlInfoは、CRL情報を表すCRL_INFO構造体のアドレスを指定します。 hCertStoreは、システムストアのハンドルを指定します。 CRL_INFO構造体は、次のように定義されています。

typedef struct _CRL_INFO {
  DWORD                        dwVersion;
  CRYPT_ALGORITHM_IDENTIFIER   SignatureAlgorithm;
  CERT_NAME_BLOB               Issuer;
  FILETIME                     ThisUpdate;
  FILETIME                     NextUpdate;
  DWORD                        cCRLEntry;
  PCRL_ENTRY                   rgCRLEntry;
  DWORD                        cExtension;
  PCERT_EXTENSION              rgExtension;
} CRL_INFO, *PCRL_INFO;

dwVersionは、CRLのバージョンを指定します。 SignatureAlgorithmは、署名アルゴリズムを指定します。 Issuerは、CRLの発行者を指定します。 ThisUpdateは、CRLの今回更新日を指定します。 NextUpdateは、CRLの次回更新日を指定します。 cCRLEntryは、rgCRLEntryの要素数を指定します。 rgCRLEntryは、CRLエントリを表すCRL_ENTRY構造体の配列を指定します。 cExtensionは、rgExtensionの要素数を指定します。 rgExtensionは、拡張情報を表すCERT_EXTENSION構造体のアドレスを指定します。 CRL_ENTRY構造体は、次のように定義されています。

typedef struct _CRL_ENTRY {
  CRYPT_INTEGER_BLOB    SerialNumber;
  FILETIME              RevocationDate;
  DWORD                 cExtension;
  PCERT_EXTENSION       rgExtension;
} CRL_ENTRY, *PCRL_ENTRY;

SerialNumberは、失効された証明書のシリアルナンバーを指定します。 RevocationDateは、失効された日時を指定します。 cExtensionは、rgExtensionの要素数を指定します。 rgExtensionは、拡張情報を表すCERT_EXTENSION構造体のアドレスを指定します。 CRLエントリの拡張情報には、失効理由を示す理由コードなどがあります。

さて、それではユーザーは、どのようにしてCRLを取得することになるのでしょうか。 これは証明書の検証時などに、アプリケーション内で暗黙的に ネットワーク上からダウンロードすることになっているため、 ユーザーが手動でCRL配布ポイントにアクセスするようなことはありません。 ただし、ダウンロードしたCRLはローカルコンピュータ上にキャッシュすることも可能であり、 キャッシュされたCRLのみで証明書を検証する方法もよく用いられます。 また、CRLは証明書と同じくシステムストアに格納することも可能です。

CRLとシステムストア

CertOpenSystemStoreなどでオープンできるシステムストアは、 その内部に証明書ストア、CRLを格納するストア、そしてCTLを格納するストアという、 合計3つのストアを維持しています。 IEのインターネットオプションから表示できるダイアログに証明書だけが列挙されるのは、 証明書ストア内の要素のみを列挙しているからであり、 決してCRLが一切存在しないということを意味するものではありません。 次のようなコードを記述すれば、CRLを格納するストアからCRLを取得することができます。

#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;
	PCCRL_CONTEXT pCrlContext;
	
	hStore = CertOpenSystemStore(0, TEXT("CA"));
	if (hStore == NULL)
		return 0;
	
	pCrlContext = CertEnumCRLsInStore(hStore, NULL);
	if (pCrlContext == NULL) {
		CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
		return 0;
	}

	CryptUIDlgViewContext(CERT_STORE_CRL_CONTEXT, pCrlContext, NULL, NULL, 0, NULL);
	
	CertFreeCRLContext(pCrlContext);
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);

	return 0;
}

CertEnumCRLsInStoreは、第1引数に指定されたシステムストアからCRLを列挙します。 この例では、最初に列挙されたCRLのみを取得し、 それをCryptUIDlgViewContextで表示しています。 このとき、第1引数にCERT_STORE_CRL_CONTEXTを指定するのが重要です。 不要になったCRLは、CertFreeCRLContextで開放します。



戻る