EternalWindows
自己署名入り証明書 / 拡張鍵用途

今回は、拡張情報の1つである拡張鍵用途(Extended Key Usage)について説明します。 この情報は、いわば証明書を利用する目的を表すものであり、 その種類はCryptEnumOIDInfoやIEのインターネットオプションから表示できる 次のダイアログで確認することができます。

拡張鍵用途の値は、CAから発行された証明書の種類を明確にしているという見方もできます。 たとえば、SSLサーバー証明書をCAに対して申請したならば、 発行される証明書にはサーバー認証という拡張鍵用途が含まれ、 コードサイニング証明書ならば、発行される証明書にはコード署名という拡張鍵用途が 含まれことになります。 ただし、拡張鍵用途が証明書の利用を限定できるかは、 その拡張鍵用途の値を確認するアプリケーションに依存します。 たとえば、exeファイルに署名を行うアプリケーションがあったとして、 そのアプリケーションがコード署名の拡張鍵用途を確認しない設計になっていれば、 SSLサーバー証明書として発行された証明書を署名に使うようなことも理論上では可能となります。 拡張鍵用途は、クリティカルの有無を問いません。

拡張鍵用途を追加する例を次に示します。

void AddEnhKeyUsage(PCERT_EXTENSION pCertExtension)
{
	CERT_ENHKEY_USAGE enhKeyUsage;
	DWORD             dwSize;
	LPBYTE            lpData;
	LPSTR             lpszIdentifier[] = {
		szOID_PKIX_KP_SERVER_AUTH, szOID_PKIX_KP_CODE_SIGNING
	};

	enhKeyUsage.cUsageIdentifier     = 2;
	enhKeyUsage.rgpszUsageIdentifier = lpszIdentifier;
	
	CryptEncodeObject(X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, &enhKeyUsage, NULL, &dwSize);
	lpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);
	CryptEncodeObject(X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, &enhKeyUsage, lpData, &dwSize);

	pCertExtension->pszObjId     = szOID_ENHANCED_KEY_USAGE;
	pCertExtension->fCritical    = FALSE;
	pCertExtension->Value.cbData = dwSize;
	pCertExtension->Value.pbData = lpData;
}

拡張鍵用途を表す構造体は、CERT_ENHKEY_USAGE構造体となっています。 この構造体は、目的を表すOIDの配列と要素数をメンバとして持ち、 上記のコードではszOID_PKIX_KP_SERVER_AUTH(サーバー認証) とszOID_PKIX_KP_CODE_SIGNING(コード署名)のOIDを指定しています。 拡張情報としての拡張鍵用途を表すOIDはszOID_ENHANCED_KEY_USAGEであり、 これはCryptEncodeObjectとCERT_EXTENSION構造体のpszObjIdメンバに指定されます。

拡張鍵用途と拡張プロパティ

CryptoAPIでは、拡張鍵用途を拡張情報としてではなく、 拡張プロパティとして設定することも可能となっています。 拡張プロパティとして拡張鍵用途を追加するには、CertAddEnhancedKeyUsageIdentifierを呼び出します。

CertAddEnhancedKeyUsageIdentifier(pContext, szOID_PKIX_KP_EMAIL_PROTECTION);

第1引数は証明書コンテキストのアドレスを指定し、 第2引数は目的を表すOIDを指定します。 szOID_PKIX_KP_EMAIL_PROTECTIONは、電子メールの保護という意味を持っています。 追加された拡張プロパティを取得したい場合は、CertGetEnhancedKeyUsageを呼び出します。

DWORD              i;
DWORD              dwSize;
PCERT_ENHKEY_USAGE pEnhKeyUsage;
PCCRYPT_OID_INFO   pOidInfo;

CertGetEnhancedKeyUsage(pContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &dwSize);
pEnhKeyUsage = (PCERT_ENHKEY_USAGE)HeapAlloc(GetProcessHeap(), 0, dwSize);
CertGetEnhancedKeyUsage(pContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pEnhKeyUsage, &dwSize);

for (i = 0; i < pEnhKeyUsage->cUsageIdentifier; i++) {
	pOidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pEnhKeyUsage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
	MessageBoxW(NULL, pOidInfo->pwszName, L"OK", MB_OK);
}

CertGetEnhancedKeyUsageの第2引数に指定しているCERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAGは、 拡張プロパティの拡張鍵用途を取得することを意味していますが、 CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAGを指定した場合は、 拡張情報としての拡張鍵用途を取得することができます。 1回目の呼び出しでは、第3引数にNULLを指定してデータのサイズを取得し、 2回目の呼び出しで確保したバッファを第3引数に指定します。 目的の数は、CERT_ENHKEY_USAGE構造体のcUsageIdentifierの値だけ存在するため、 ループ内で目的を表示するようにしています。 rgpszUsageIdentifierに格納されているのは目的を表すOIDであり、 これに関連付けられた名前を取得するにはCRYPT_OID_INFO構造体のpwszNameメンバが必要です。 OIDを基に構造体を取得するので、CryptFindOIDInfoの第1引数はCRYPT_OID_INFO_OID_KEYとなり、 また、そのOIDのグループが拡張鍵用途であるため、 第3引数にはCRYPT_ENHKEY_USAGE_OID_GROUP_IDを指定します。



戻る