EternalWindows
自己署名入り証明書 / サブジェクト鍵識別子

今回は、拡張情報の1つであるサブジェクト鍵識別子(Subject Key Identifier)について説明します。 サブジェクトとは証明書に記述されている発行先、つまり証明書の所有者のことであり、 鍵というのはその所有者の公開鍵です。 この所有者の公開鍵を識別するための値がサブジェクト鍵識別子と定義されていますが、 具体的にはどういった目的で利用されることになるのでしょうか。 たとえば、あるユーザーがCAより証明書を複数個発行してもらっているような場合、 そのユーザーは鍵ペアを証明書の数だけ管理しているはずです。 この場合、ときとして、ある鍵ペアの公開鍵が、複数個ある証明書のどれに 格納されているのかを特定したいようなことがありますが、 このときに参照されるのがサブジェクト鍵識別子となります。 サブジェクト鍵識別子は、クリティカルとして記されるべきではありません。

所有者の公開鍵を識別する値というのは、公開鍵のハッシュ値のことです。 このハッシュ値をサブジェクト鍵識別子として拡張情報に追加することになります。 ハッシュ値の作成から取得までには、CryptCreateHash、CryptHashData、CryptGetHashParamという 定石の手順がありますが、幸いにも公開鍵のハッシュ値についてはCryptHashPublicKeyInfoを 呼び出すだけで取得することができます。

BOOL WINAPI CryptHashPublicKeyInfo(
  HCRYPTPROV hCryptProv,
  ALG_ID Algid,
  DWORD dwFlags,
  DWORD dwCertEncodingType,
  PCERT_PUBLIC_KEY_INFO pInfo,
  BYTE *pbComputedHash,
  DWORD *pcbComputedHash
);

hCryptProvは、CSPのハンドルを指定します。 Algidは、ハッシュアルゴリズムを示すIDを指定します。 dwFlagsは、0を指定します。 dwCertEncodingTypeは、エンコーディングタイプを指定します。 pInfoは、公開鍵の情報を格納したCERT_PUBLIC_KEY_INFOのアドレスを指定します。 pbComputedHashは、作成されたハッシュ値を受け取るバッファを指定します。 pcbComputedHashは、バッファのサイズを格納した変数のアドレスを指定します。

次に、サブジェクト鍵識別子を追加する例を示します。

void AddSubjectKeyIdentifier(PCERT_EXTENSION pCertExtension, HCRYPTPROV hProv, DWORD dwKeySpec)
{
	PCERT_PUBLIC_KEY_INFO pPublicKeyInfo;
	CRYPT_DATA_BLOB       subjectKeyIdentifier;
	LPBYTE                lpPublicKeyHash;
	DWORD                 dwSize;
	LPBYTE                lpData;
	
	CryptExportPublicKeyInfo(hProv, dwKeySpec, X509_ASN_ENCODING, NULL, &dwSize);
	pPublicKeyInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, dwSize);
	CryptExportPublicKeyInfo(hProv, dwKeySpec, X509_ASN_ENCODING, pPublicKeyInfo, &dwSize);
	
	CryptHashPublicKeyInfo(hProv, CALG_SHA1, 0, X509_ASN_ENCODING, pPublicKeyInfo, NULL, &dwSize);
	lpPublicKeyHash = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);
	CryptHashPublicKeyInfo(hProv, CALG_SHA1, 0, X509_ASN_ENCODING, pPublicKeyInfo, lpPublicKeyHash, &dwSize);

	subjectKeyIdentifier.cbData = dwSize;
	subjectKeyIdentifier.pbData = lpPublicKeyHash;

	CryptEncodeObject(X509_ASN_ENCODING, szOID_SUBJECT_KEY_IDENTIFIER, &subjectKeyIdentifier, NULL, &dwSize);
	lpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);
	CryptEncodeObject(X509_ASN_ENCODING, szOID_SUBJECT_KEY_IDENTIFIER, &subjectKeyIdentifier, lpData, &dwSize);

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

	HeapFree(GetProcessHeap(), 0, lpPublicKeyHash);
	HeapFree(GetProcessHeap(), 0, pPublicKeyInfo);
}

公開鍵のハッシュ値を取得するには、まず公開鍵自体が必要になるため、 CSPのハンドルと鍵ペアの用途を受け取り、 それをCryptExportPublicKeyInfoに指定します。 CryptHashPublicKeyInfoの1回目の呼び出しでは、ハッシュ値のサイズを取得することに専念し、 2回目の呼び出しで実際にハッシュ値を取得します。 取得したハッシュ値とサイズはCRYPT_DATA_BLOB構造体に指定し、 これをサブジェクト鍵識別子と扱います。 構造体の初期化が終われば、後はエンコードと拡張情報への設定処理を行うだけです。 この部分は、前節とほぼ同じ要領になりますが、OIDが サブジェクト鍵識別子用を表す値に変更されていることに注意してください。

サブジェクト鍵識別子と拡張プロパティ

証明書の拡張プロパティには、暗黙のうちに公開鍵のハッシュ値が格納されることになっています。 拡張プロパティはWindows特有のデータ形式であり、 これがサブジェクト鍵識別子に取って代わるデータとなるようなことはありませんが、 ハッシュ値を取得する手順の容易さとしては、 拡張情報のサブジェクト鍵識別子を参照するよりも、 拡張プロパティの方が優れています。 次に、コード例を示します。

CertGetCertificateContextProperty(pContext, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &dwSize);
lpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);
CertGetCertificateContextProperty(pContext, CERT_KEY_IDENTIFIER_PROP_ID, lpData, &dwSize);

拡張プロパティに暗黙のうちに格納されるハッシュ値は、SHA形式でハッシュされています。 したがって、CryptHashPublicKeyInfoにCALG_SHA1を指定して得られる値と同一になります。



戻る