「そうか!そうだったのか!」
男は突然つぶやいた。男はCAPI(CryptoAPI)を使ってWindows証明書ストアから選択した(秘密鍵が関連付いている)証明書にてRSA方式のデジタル署名を生成するプログラムをデバッグしていた。これまでは問題無く動作していたのだが、Adobe Acrobatから**「IDを追加」-「今すぐデジタルIDを新規作成」-「Windows証明書ストア」**を使って登録した秘密鍵(署名鍵)を使うとエラーになると言う問題で苦しんでいたのである。CAPIの実装は以下のようになっていた。
HCRYPTPROV hProv = NULL;
HCRYPTPROV hKeyProv = NULL;
HCERTSTORE hStore = NULL;
DWORD dwKeySpec;
BOOL fCallerFreeProv = FALSE;
HCRYPTHASH hHash = NULL;
// -- 証明書ストアから証明書の取得
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, NULL);
hStore = CertOpenSystemStore(hProv, L"MY");
PCCERT_CONTEXT pcCert = CertFindCertificateInStore(hStore, ...);
// -- 秘密鍵(署名鍵)の取得
int hasPrivKey = CryptAcquireCertificatePrivateKey(pcCert, 0, NULL,
&hKeyProv, &dwKeySpec, &fCallerFreeProv);
if(hasPrivKey != 1)
return ERR; // 秘密鍵が関連付いていない
// -- 署名対象ハッシュ値のセット(仮にSHA-256利用)
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
CryptSetHashParam(hHash, HP_HASHVAL, pbHash, 0);
// -- 署名結果のサイズを取得
CryptSignHash(hHash, dwKeySpec, NULL, 0, NULL, &cbSign);
// -- pbSignをcbSign分確保してデジタル署名実行
CryptSignHash(hHash, dwKeySpec, NULL, 0, pbSign, &cbSign);
// -- 署名終了(注:pbSignに格納された結果は反転している)
普通はこれでうまく動作するのだが、Acrobatから登録した証明書だとCryptAcquireCertificatePrivateKeyの結果が 0(秘密鍵無し)と返され GetLastError() の値が 0x8009001b(provider type does not match registered value)となってしまう。つまり秘密鍵の取得自体が出来ないように見える。困った。
色々試していてCryptAcquireCertificatePrivateKeyのdwFlagsにCRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAGを指定してみたところ、hasPrivKeyが 1(秘密鍵有り)で返ってきた。CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAGは**CNG(Cryptography API: Next Generation)**と呼ばれる新しい暗号API用の鍵も取得するフラグである。
と言うことはAcrobatから登録するとCNG形式で秘密鍵が登録されていることになる。これが冒頭の男のつぶやき「そうか!そうだったのか!」に繋がっていたのである。
取得した秘密鍵がCNGの場合にはdwKeySpecがCERT_NCRYPT_KEY_SPECと返されているので判別がつけられる。またCNG鍵ハンドルはhProvにNCRYPT_KEY_HANDLEとして返される。
さて次なる問題はCNG形式の秘密鍵と言うことはCAPIを使っては署名が出来ない。つまりCNGのAPIを使って署名する必要があると言うことになる。NCryptSignHashを使えば良いのだが、以下にCAPIと同じ結果を得る為のソースを示す。
// -- 秘密鍵(署名鍵)の取得
int hasPrivKey = CryptAcquireCertificatePrivateKey(pcCert,
CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL,
&hKeyProv, &dwKeySpec, &fCallerFreeProv);
if(hasPrivKey != 1)
return ERR; // 秘密鍵が関連付いていない
if(dwKeySpec == CERT_NCRYPT_KEY_SPEC)
{
// -- CNGによる署名値計算
NCRYPT_KEY_HANDLE hCngKey = (NCRYPT_KEY_HANDLE)hKeyProv;
// -- パディングはCAPIのCryptSignHashと同じPKCS#1とする
BCRYPT_PKCS1_PADDING_INFO padding_PKCS1 = BCRYPT_PKCS1_PADDING_INFO();
// -- ハッシュアルゴリズムのセット(仮にSHA-256利用)
padding_PKCS1.pszAlgId = BCRYPT_SHA256_ALGORITHM;
// -- 署名結果のサイズを取得
NCryptSignHash(hCngKey, &padding_PKCS1, pbHash, cbHash, NULL, 0, &cbSign, BCRYPT_PAD_PKCS1);
// -- pbSignをcbSign分確保してデジタル署名実行
NCryptSignHash(hCngKey, &padding_PKCS1, pbHash, cbHash, pbSign, cbSign, &cbSign, BCRYPT_PAD_PKCS1);
// -- 署名終了(注:pbSignに格納された結果は反転していない)
} else {
// CAPIによる署名値計算(略)
:
}
「ふむ。これで良さそうだ。新しい知識が増えたし今晩はビールくらい飲んでも良かろう。」
男は今度は満足げにつぶやいた。