EternalWindows
自己署名入り証明書 / 署名の設定

一通りCERT_INFO構造体の初期化が終われば、 後はそれをX.509形式にエンコードし、署名を設定することになります。 これにより、証明書のデータが完成します。

BOOL WINAPI CryptSignAndEncodeCertificate(
  HCRYPTPROV hCryptProv,
  DWORD dwKeySpec,
  DWORD dwCertEncodingType,
  LPCSTR lpszStructType,
  const void *pvStructInfo,
  PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
  const void *pvHashAuxInfo,
  PBYTE pbEncoded,
  DWORD *pcbEncoded
);

hCryptProvは、鍵ペアを格納している鍵コンテナを開いたCSPのハンドルを指定します。 dwKeySpecは、鍵コンテナに格納されている鍵ペアの用途を指定します。 この用途で識別される鍵ペアの秘密鍵で署名が行われます。 dwCertEncodingTypeは、エンコーディングタイプを指定します。 lpszStructTypeは、エンコード対象となるデータを表す定数を指定します。 証明書の場合は、X509_CERT_TO_BE_SIGNEDを指定します。 pvStructInfoは、エンコードと署名を行う対象となるデータを指定します。 証明書の場合は、CERT_INFO構造体のアドレスを指定します。 pSignatureAlgorithmは、署名アルゴリズムを示すIDを指定します。 pvHashAuxInfoは、予約されているためNULLを指定します。 pbEncodedは、エンコードと署名されたデータを受け取るバッファを指定します。 pcbEncodedは、pbEncodedのサイズを格納した変数のアドレスを指定します。

CryptSignAndEncodeCertificateを呼び出す例を次に示します。 CSPのハンドルは、前節の手順で取得したものとします。

certInfo.SignatureAlgorithm.pszObjId = szOID_RSA_SHA1RSA;

CryptSignAndEncodeCertificate(hProv, dwKeySpec, X509_ASN_ENCODING, (LPSTR)X509_CERT_TO_BE_SIGNED, &certInfo, &certInfo.SignatureAlgorithm, NULL, NULL, lpdwCertSize);
lpCertData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, *lpdwCertSize);
CryptSignAndEncodeCertificate(hProv, dwKeySpec, X509_ASN_ENCODING, (LPSTR)X509_CERT_TO_BE_SIGNED, &certInfo, &certInfo.SignatureAlgorithm, NULL, lpCertData, lpdwCertSize);

CERT_INFO構造体には、署名アルゴリズムのIDを指定するSignatureAlgorithmメンバがあるため、 忘れずに初期化しておきましょう。 ここでは、SHAアルゴリズムとしていますが、MD5アルゴリズムを示すszOID_RSA_MD5RSAを指定することもできます。 1回目のCryptSignAndEncodeCertificateでは、バッファのサイズを取得することに専念し、 2回目の呼び出しで実際にデータを受け取ります。

さて、証明書のデータを取得することができれば、 後はそれをファイルとして保存したり、証明書ストアに追加したりすることができます。 エンコードされたバイトデータを証明書として証明書ストアに 追加するには、CertAddEncodedCertificateToStoreを呼び出します。

BOOL WINAPI CertAddEncodedCertificateToStore(
  HCERTSTORE hCertStore, 
  DWORD dwCertEncodingType, 
  const BYTE *pbCertEncoded, 
  DWORD cbCertEncoded, 
  DWORD dwAddDisposition, 
  PCCERT_CONTEXT *ppCertContext 
);

hCertStoreは、証明書ストアのハンドルを指定します。 dwCertEncodingTypeは、エンコーディングタイプを指定します。 pbCertEncodedは、エンコードされた証明書データを指定します。 cbCertEncodedは、pbCertEncodedに指定したデータのサイズを指定します。 dwAddDispositionは、既に証明書が存在している場合の動作を表す定数を指定します。 ppCertContextは、作成された証明書コンテキストを 受け取る変数のアドレスを指定します。 取得した証明書コンテキストは、CertFreeCertificateContextで開放する必要があります。 証明書コンテキストを取得するつもりがない場合は、NULLを指定することができます。

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

定数 意味
CERT_STORE_ADD_ALWAYS 追加しようとする証明書と同一の証明書が存在しても、証明書を追加する。
CERT_STORE_ADD_NEW 追加しようとする証明書と同一の証明書が存在する場合は、関数は失敗する。
CERT_STORE_ADD_REPLACE_EXISTING 追加しようとする証明書と同一の証明書が存在する場合は、それを置き換える。

dwAddDispositionに指定できる定数の動作は、少し曖昧であると思えるところがあります。 たとえば、CERT_STORE_ADD_REPLACE_EXISTINGを指定して、 既存の証明書の追加を行っているのにも関わらず、 新しい証明書として追加されることがあります。 そもそも、証明書が同一であるかを何で判断しているのが不確定ですが、 少なくとも、シリアル番号と発行者が同じである証明書を同一であると判断しないように思えます。

次にCertAddEncodedCertificateToStoreを呼び出す例を示します。

HCERTSTORE     hStore;
PCCERT_CONTEXT pContext;

hStore = CertOpenSystemStore(0, TEXT("MY"));
if (hStore == NULL)
	return FALSE;

if (!CertAddEncodedCertificateToStore(hStore, X509_ASN_ENCODING, lpCertData, dwCertSize, CERT_STORE_ADD_REPLACE_EXISTING, &pContext)) {
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
	return FALSE;
}

まず、追加対象となる証明書ストアをCertOpenSystemStoreでオープンします。 追加する証明書は個人用の自己署名入り証明書であるため、MY証明書ストアを対象とします。 CertAddEncodedCertificateToStoreに指定しているlpCertDataとdwCertSizeは、 それぞれ証明書データとそのサイズを表し、CryptSignAndEncodeCertificateで取得したものを指定しています。 CertAddEncodedCertificateToStoreが成功した場合、証明書ストアには確かに証明書が追加されることになりますが、 実際にはその証明書は、IEのインターネットオプションから表示できる証明書ダイアログで確認できないかもしれません。 次節ではこの問題について取り上げ、これまで紹介してきたコード片を1つにまとめます。

MY証明書ストアの実体

MY証明書ストアは、システムストアの一種でありながら、 その実体がフォルダであるという特徴があります。 事実、CERT_SYSTEM_STORE_CURRENT_USERで識別されるレジストリキーにてMYという名前の サブキーを確認してみると、そのサブキーは証明書を含んでいないことが分かります。 MY証明書ストアの実体は、次のファイルパスで識別されます。

%APPDATA%\Microsoft\SystemCertificates\My\Certificates

このフォルダに個人の証明書が格納されることになります。 作成されるファイル名は、証明書の拡張プロパティの拇印を基にしており、 ファイル形式は一般の証明書の拡張子である.cerではありません。 ちなみに、証明書を証明書ストアに追加する際にCertSetCertificateContextPropertyでCSPの関連付けをした場合、 Myフォルダ以下のKeyフォルダにファイルが作成されます。 これを発見した当初は、このファイルがCSPとの関連付けを示すものだと思いましたが、 実際にはそうではなく、どのような目的で存在しているのかは定かではありません。 少なくとも、鍵ペアの実体ではないので、削除しても問題はないと思われます。 ちなみに、鍵ペアの格納場所は次の通りです。

%APPDATA%\Microsoft\Crypto\RSA\User SID

一言にMY証明書ストアといっても、CERT_SYSTEM_STORE_LOCAL_MACHINEで識別されるレジストリキーもあり、 この場合、証明書はレジストリキーに格納されることになっています。 そのような証明書をオープンする場合は、CertOpenStoreを次のように呼び出すことになるでしょう。

hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, TEXT("MY"));

この場合、hStoreに対する証明書の追加は、CERT_SYSTEM_STORE_LOCAL_MACHINEで識別されるレジストリキーに反映されます。 また、このような場合は、証明書に含まれる公開鍵と対になる秘密鍵をマシンコンテナとして作成しておくことになるでしょう。



戻る