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

今回は、拡張情報の1つである鍵用途(KeyUsage)について説明します。 これは、拡張情報の中でも比較的に分かり易い部類に入るでしょう。 鍵用途という言葉が指している鍵は、証明書に含まれている公開鍵を意味し、 正にその公開鍵の用途を定義します。 CSPでは、鍵の用途をAT_KEYEXCHANGEやAT_SIGNATUREで表していましたが、 これをより厳密に定義するのが鍵用途ということになります。 次に、鍵用途として指定可能な定数の一部を示します。

定数 意味
CERT_DIGITAL_SIGNATURE_KEY_USAGE 鍵で署名を行えることを意味する。
CERT_NON_REPUDIATION_KEY_USAGE 鍵で否認の防止ができることを意味する。
CERT_KEY_ENCIPHERMENT_KEY_USAGE 鍵を鍵の暗号化に利用できることを意味する。
CERT_DATA_ENCIPHERMENT_KEY_USAGE 鍵をデータの暗号化に利用できることを意味する。
CERT_KEY_AGREEMENT_KEY_USAGE 鍵を交換できることを意味する。
CERT_KEY_CERT_SIGN_KEY_USAGE 鍵を証明書の署名の検証に利用できることを意味する。
CERT_CRL_SIGN_KEY_USAGE 鍵が証明書失効情報の署名の検証に利用できることを意味する。

鍵用途を拡張情報に含める場合は、それをクリティカルとしてマークするべきとされています。 つまり、目的の動作を行う前に、その目的に適合する鍵用途が有効になっているかを確認し、 有効でない場合は、目的の動作を行ってはならないということになります。 たとえば、AT_SIGNATUREの鍵が証明書に格納されているような場合、 その鍵では暗号化を行うことができないわけですから、 鍵用途にはCERT_DATA_ENCIPHERMENT_KEY_USAGEが含まれることはなく、 アプリケーションからすれば、鍵で暗号化を行えないものと判断することができます。

次に、鍵用途を初期化する例を示します。

void AddKeyUsage(PCERT_EXTENSION pCertExtension, DWORD dwKeySpec)
{
	CRYPT_BIT_BLOB keyUsage;
	BYTE           identifier;
	DWORD          dwSize;
	LPBYTE         lpData;

	if (dwKeySpec == AT_KEYEXCHANGE)
		identifier = CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE | CERT_KEY_AGREEMENT_KEY_USAGE;
	else if (dwKeySpec == AT_SIGNATURE)
		identifier = CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE | CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE;
	else
		;
	
	keyUsage.cbData      = 1;
	keyUsage.pbData      = &identifier;
	keyUsage.cUnusedBits = 0;

	CryptEncodeObject(X509_ASN_ENCODING, szOID_KEY_USAGE, &keyUsage, NULL, &dwSize);
	lpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);
	CryptEncodeObject(X509_ASN_ENCODING, szOID_KEY_USAGE, &keyUsage, lpData, &dwSize);

	pCertExtension->pszObjId     = szOID_KEY_USAGE;
	pCertExtension->fCritical    = TRUE;
	pCertExtension->Value.cbData = dwSize;
	pCertExtension->Value.pbData = lpData;
}

鍵用途を初期化するうえで重要となるのは、CSPの視点から見た鍵の用途、 つまりAT_KEYEXCHANGEやAT_SIGNATUREを、証明書の鍵としての用途に置き換えることです。 CERT_DIGITAL_SIGNATURE_KEY_USAGEは署名を行えるのか有無であり、 これはどちらの鍵でも行うことができますが、 暗号化はAT_KEYEXCHANGEでなければできないため、 暗号化に関する定数はAT_KEYEXCHANGEの場合に利用されることになります。 一方、AT_SIGNATUREの場合は署名用の定数で初期化されることになりますが、 ここにCERT_KEY_CERT_SIGN_KEY_USAGEを含んでよいのは、 自己署名入り証明書を作成するときに限られます。 CERT_KEY_CERT_SIGN_KEY_USAGEは証明書の署名を検証できるかを表しますが、 これを検証できる鍵というのは、署名に使った秘密鍵の対となる公開鍵でしかなく、 その対なる公開鍵を格納するのは、自己署名入り証明書に限られるからです。

鍵用途を表す定数を初期化することができれば、 鍵用途として定義しているCRYPT_BIT_BLOB構造体を初期化します。 cbDataに指定する値はバイト単位であるため、sizeof(BYTE)ではなく1を指定します。 CryptEncodeObjectからpCertExtensionの初期化は、 いつものようにOIDの部分が変化する程度ですが、 鍵用途はクリティカルとするべきであるため、fCriticalをTRUEに設定しています。

鍵用途の取得

何らかの拡張情報の値が必要となる場合は、 その拡張情報のOIDをCertFindExtensionに指定してCERT_EXTENSION構造体を取得し、 それをCryptDecodeObjectに指定するのが一般的です。 しかし、鍵用途に関してはCertGetIntendedKeyUsageを呼び出すだけで、鍵用途を取得することができます。

BYTE identifier;

CertGetIntendedKeyUsage(X509_ASN_ENCODING, pContext->pCertInfo, &identifier, 1);
if (identifier & CERT_DATA_ENCIPHERMENT_KEY_USAGE)
	MessageBox(NULL, TEXT("データの暗号化を行うことができます。"), TEXT("OK"), MB_OK);


戻る