CSPとしてcsp.dllをロードし一連のCryptoSPIの各関数の呼び出しのテストを行い、結果を
表示する。
Calling CryptAcquireContext - CryptAcquireConext returned error 80090019 FAILED
などと表示された場合は、csp.dllのセットアップが正しく行われていない。
以上で、動
作確認は終了である。次に開発した CSP をテストするために署名とセッ トアップについて解説する。
(2)
CSPの署名とセットアップWindows 2000以前のOSでは、前述の例のように、署名データはレジストリにバイナリの値
“Signature”として格納していた。
Windows 2000からは署名データをdllファイルにリソースとして保持することが可能となっ
ている。dllファイルにリソースIDが0x29A(666)のリソースがあった場合、署名データとし て扱われる。リソースとして署名データを持つ場合、レジストリには“Signature”のかわりに
DWORD値の”SigInFile”で値のデータを“0”とする。署名データの操作は同梱されている
cspSign.exeによって行う。
cspSign.exeの使用方法を次に示す。
(a) cspSign.exe によるテスト用署名の生成
CSPの署名データは、表 5-2のようにして生成する。
cspSign <オプション> <CSPファイル> [<署名ファイル>]
表 5-2 テスト用署名ツール cspSign のオプション
オプション 説明
C <CSPファイル>を指定して実行する。署名データをリソースに格納する。
S <CSP ファイル>と[<署名ファイル>]を指定して実行する。署名データがファイ
ルに格納される。
D <CSP ファイル>を指定して実行する。リソースの署名データから署名検証を行
う。
V <CSP ファイル>と[<署名ファイル>]を指定して実行する。署名ファイルの署名
データから署名検証を行う。
リソースタイプの署名を使うためには、開発する CSP のリソースファイルは図 5-1 のようにする。
ここで、署名データの大きさ 144 バイトに相当するダミーのファイル( csp.sig ) を用意する。次のようにコマンドを実行することによって、この 144 バイトのリソ ースが適切な署名データに置き換わる
。> cspSign -c <CSPファイル>
2 TEXTINCLUDE DISCARDABLE BEGIN
"#include ""cspdk.h""¥0"
END
CRYPT_SIG_RESOURCE_NUMBER RCDATA DISCARDABLE "csp.sig"
図 5-1 CSP のリソースファイル
(b)
セットアップ開発したCSPをレジストリに登録するために、CSPには、CryptoSPIの関数以外に、図 5-2 のようにDllRegisterServerとDllUnregisterServerの2つの関数を記述しエクスポートする。
DllRegisterServerでは、レジストリにProvider Nameをキーとして、ImagePath、Provider Type、SignatureまたはSigInFileがセットする。DllUnregisterServerではレジストリから自
身のProvider Nameのキーを削除する。これにより、以下のコマンドを実行するとそれぞれの
関数が呼び出され、レジストリへの操作が行われる。
(c)
レジストリへの登録コマンド> regsvr32 CSPファイル
(d)
レジストリからの削除コマンド regsvr32 /u CSPファイルvoid DllRegisterServer (void) {
レジストリに値を登録 }
void DllUnregisterServer (void) {
レジストリの値を削除 }
図 5-2 レジストリへの登録/削除の関数
これとは別に、cspinstl.exeのように、セットアッププログラムが必要なキーと値を直接レジ ストリに書く込むことも可能である。
その場合、Windows NT4などの署名データをリソースとしてではなく、レジストリに登録す る必要があるOSを考慮して、cspinstl.exeでは、署名データのファイルを読み込んで、レジス トリに登録することもあわせて行っている。
1.1.26
単純な換字暗号を実装したCSPのサンプルプログラムの解説付録にCSPのサンプルプログラムcpkicsps.cを添付した。サンプルのなかから、暗号化部分 について図 5-3に示す。
// アルゴリズムのパラメータを保持する構造体 typedef struct tagMyAlgorithmParam { BYTE *m_pData;
DWORD m_nDataLen;
} MyAlgorithmParam;
// 処理中のデータを保持する構造体 typedef struct tagMyKeyParam {
BYTE m_nBits; // 暗号鍵 void *m_pOption; // 未使用 } MyKeyParam;
MyAlgorithmParam *g_pParam = NULL;
MyKeyParam *g_pKey = NULL;
// CrypotSPI 初期化
BOOL WINAPI CPAcquireContext(
OUT HCRYPTPROV *phProv, IN LPCSTR szContainer, IN DWORD dwFlags,
IN PVTableProvStruc pVTable) {
g_pParam = (MyAlgorithmParam *)malloc(sizeof(MyAlgorithmParam));
*phProv = (HCRYPTPROV)g_pParam;
g_pParam->m_pData = NULL;
g_pParam->m_nDataLen = 0;
return TRUE;
}
// CrypotSPI 開放
BOOL WINAPI CPReleaseContext(
IN HCRYPTPROV hProv, IN DWORD dwFlags) {
free((MyAlgorithmParam *)hProv);
return TRUE;
}
// CrypotSPI 鍵生成 BOOL WINAPI CPGenKey(
IN HCRYPTPROV hProv,
IN ALG_ID Algid, IN DWORD dwFlags, OUT HCRYPTKEY *phKey) {
g_pKey = (MyKeyParam *)malloc(sizeof(MyKeyParam));
g_pKey->m_nBits = (BYTE)(BASE_ADDITION + (dwFlags & 0xff));
g_pKey->m_pOption = NULL;
*phKey = (HCRYPTKEY)g_pKey; // Replace NULL with your own structure.
return TRUE;
}
// CrypotSPI 暗号化 BOOL WINAPI CPEncrypt(
IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN HCRYPTHASH hHash, IN BOOL fFinal, IN DWORD dwFlags, IN OUT LPBYTE pbData, IN OUT LPDWORD pcbDataLen, IN DWORD cbBufLen)
{
BYTE *pBuf = NULL;
DWORD dwPrevLen = 0;
DWORD dwLen = 0;
DWORD n;
BYTE bAddition;
// 初期化済みか確認
if (hProv == NULL)
return FALSE;
if (pbData == NULL) {
// 暗号化した結果のデータの長さを通知する。
*pcbDataLen = ((MyAlgorithmParam *)hProv)->m_nDataLen;
return TRUE;
}
dwPrevLen = ((MyAlgorithmParam *)hProv)->m_nDataLen;
dwLen = dwPrevLen + cbBufLen;
pBuf = realloc(((MyAlgorithmParam *)hProv)->m_pData, dwLen);
memcpy(pBuf+dwPrevLen, pbData, cbBufLen);
((MyAlgorithmParam *)hProv)->m_pData = pBuf;
((MyAlgorithmParam *)hProv)->m_nDataLen = dwLen;
// 最終データでなければ戻る。
if (!fFinal)
return TRUE;
bAddition = ((MyKeyParam *)hKey)->m_nBits;
// 最終データなので、暗号化して戻る。
for (n = 0; n < dwLen; ++n) { pBuf[n] += bAddition;
}
*pcbDataLen = *pcbDataLen >= dwLen ? *pcbDataLen : dwLen;
memcpy(pbData, pBuf, *pcbDataLen);
return TRUE;
}
図 5-3 CSP の暗号化のサンプルプログラム
(2) Revocation Providerの実装
CryptoAPIでは証明書の検証のための機能をプロバイダとして、追加や置換することが可能で
ある。この証明書の検証のためのプログラムをRevocation Providerと呼ぶ。
1.1.27
関連する関数の解説Revocation Providerは図 5-4に示す関数を実装してエクスポートする。
BOOL WINAPI CertDllVerifyRevocation(
DWORD dwEncodingType, DWORD dwRevType, DWORD cContext,
PVOID rgpvContext[ ], DWORD dwFlags,
PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus )
戻り値:TRUE:有効な証明書。FALSE:無効な証明書
引数:
dwEncodingType
[入力]引き渡したデータのフォーマットを示す。以下の定義値をセットされている。
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING dwRevType
[入力]rgpvContextにて引き渡すデータの種別を示す。以下の定義値がセットされている。
CERT_CONTEXT_REVOCATION_TYPE cContext
[入力]rgpvContext配列の数。
rgpvContext
[入力]検証対象の証明書をCERT_CONTEXT構造体の配列として渡す。
dwFlags
[入力]検証処理の動作フラグとして以下の定義位置がセットされている。
CERT_VERIFY_REV_CHAIN_FLAG : 0番目の証明書から順に発行元からと仮定してよい。
CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION:キャッシュのみを使う。
CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG:dwUrlRetrievalTimeout タイム アウト値を参照する。
pRevPara
[入力]CERT_REVOCATION_PARA構造体を使い、パス構築に必要なCTL、証明書、CRLを格納し た証明書ストアのハンドルを渡すために使う。rgCertStore に証明書ストアハンドルの配列を指 定し、cCertStoreには配列の数を指定する。
typedef struct _CERT_REVOCATION_PARA {
DWORD cbSize; 本構造体のサイズをセット PCCERT_CONTEXT pIssuerCert; 発行元証明書
DWORD cCertStore; 参照すべき証明書ストアの数 HCERTSTORE *rgCertStore; 参照すべき証明書ストア HCERTSTORE hCrlStore; 参照すべきCRLストア LPFILETIME pftTimeToUse; 検証時刻として使う。
DWORD dwUrlRetrievalTimeout; URLへのタイムアウト値 BOOL fCheckFreshnessTime; dwFreshnessTimeを使う DWORD dwFreshnessTime;
LPFILETIME pftCurrentTime; 検証時刻として使う。
PCERT_REVOCATION_CRL_INFO pCrlInfo;ベースCRLとデルダCRL } CERT_REVOCATION_PARA, *PCERT_REVOCATION_PARA;
dwFreshnessTime の使い方:
ThisUpdate >= (CurrentTime - dwFreshnessTime) の場合にCRLを探す。
pRevStatus
[入力/出力]以下の構造体のポインタが渡され、終了時に処理結果をセットする。
typedef struct _CERT_REVOCATION_STATUS {
DWORD cbSize; 本構造体のサイズをセット DWORD dwIndex;
DWORD dwError;
DWORD dwReason;
BOOL fHasFreshnessTime;
DWORD dwFreshnessTime;
} CERT_REVOCATION_STATUS;
dwIndex:検証した証明書のrgpvContextでの順番 dwError:以下の定義値をセットする。
ERROR_SUCCESS 有効な証明書である。
CRYPT_E_NO_REVOCATION_CHECK:検証完了しなかった
CRYPT_E_REVOKED:無効な証明書である。
dwReason:証明書が失効していた場合、以下のような失効理由をセットする。
CRL_REASON_UNSPECIFIED 理由なし CRL_REASON_KEY_COMPROMISE 鍵の危殆化 CRL_REASON_CA_COMPROMISE CAの危殆化 CRL_REASON_AFFILIATION_CHANGED 属性変更 CRL_REASON_SUPERSEDED 更新されている CRL_REASON_CESSATION_OF_OPERATION 使われなくなった CRL_REASON_CERTIFICATE_HOLD 保留中
図 5-4 Revocation Provider でエクスポートする関数
Revocation Providerでは関数CertDllVerifyRevocation 以外に、CSPと同様にセットアップ 用の関数として、DllRegisterServerとDllUnregisterServerの2つの関数を記述しエクスポー トする。これにより、CSPと同様、regsvr32コマンドを使い、図 5-6に示すようなレジストリへ の登録/削除の処理が行われる。
OSの標準では cryptnet.dll、mscrlrev.dll などがあらかじめ登録されている。
1.1.28
証明書の有効/無効を通知するRevocation Providerサンプルプログラムの解説 付録に証明書の有効/無効をUIにより指定し、通知するサンプルプログラム(cpkicrp.dll)のソ ースを添付する。サンプルの証明書検証プログラム(cpkicval.exe)を次のようにコマンドプロンプトで実行す ると、図 5-5のような画面が表示されマニュアルで失効情報を指定することが可能である。
> cpkicval. -r 証明書ファイル
図 5-5 サンプルの Revocation Provider が呼ばれたところ
(3) その他の拡張可能な関数
Revocation Provider以外にもCryptoAPIの関数を置換することが可能である。表 5-3に想定 されている関数を示す。これらも、Revocation Providerと同様にレジストリに登録すると、プ ログラムでは標準の関数を呼び出すとCrypt32.dllがレジストリを参照し適切に処理する。
表 5-3 CryptoAPI の拡張可能な関数 CryptoAPI の関数 プロバイダでエクスポートす
る関数
説明
CryptEncodeObject CryptDllEncodeObject X.509やPKCSなどで規定されてい るデータ型のエンコードを行う。未 サポートのデータ型を使う場合など に置換する。
CryptEncodeObjectEx CryptDllEncodeObjectEx CryptEncodeObjectの関数ないでの メ モ リ ア ロ ケ ー シ ョ ン 機 能 付 版
(Windows 2000、XP)
CryptDecodeObject CryptDllDEcodeObject エンコードされた規定のデータを解 析し構造体に格納する。
CryptDecodeObjectEx CryptDllDEcodeObjectEx CryptDecodeObjectの関数ないでの メ モ リ ア ロ ケ ー シ ョ ン 機 能 付 版
(Windows 2000、XP)
CertOpenStore CertDllOpenStoreProv 証明書ストアをオープンする。置換
することで、未サポートの証明書ス トア形式を利用可能となる。
CertVerifyCTLUsage CertDllVerifyCTLUsage CTLの用途に関して検証する。検証
手順を拡張する場合に置換する。
これらの拡張関数のレジストリでの登録状態を図 5-6に示す。ロードされるdllファイルと関 数によっては呼び出す関数も指定できることが分かる。例えば、「RFC 2634 Enhanced Security
Services for S/MIME」で定義されている署名つき受領通知はオブジェクトIDが
1.2.840.113549.1.9.16.1.1であるが、inetcomm.dllでエクスポートされている関数 EssReceiptEncodeExを呼び出すことがわかる。
HKEY̲LOCAL̲MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 1 CertDllVerifyCTLUsage
DEFAULT CertDllVerifyRevocation
DEFAULT
Dll = cryptnet.dll
CryptDllDecodeObject CryptDllDecodeObjectEx
CryptDllEncodeObject CryptDllEncodeObjectEx
1.2.840.113549.1.9.16.1.1
Dll = cryptnet.dll
・・・・・・・
CryptDllFormatObject
Dll = inetcomm.dll
FuncName = EssReceiptEncodeEx
図 5-6 レジストリの拡張関数に関する情報
6. まとめ
「4. (3) 証明書の検証」で述べたように、CryptoAPIの標準の機能では、証明書パス検証に関
してRFC 3280で規定されているパス検証の初期パラメータ(表 4-8)を指定した検証はできな
い。
この問題に対処するためには、「5. (2) Revocation Providerの実装」で示すようなプラグイン 可能な独自の失効検証ルーチンにパス構築/パス検証の機能を組み込みことが考えられる。ただ し、Revocation Providerは名の示すとおり、本来は証明書の失効を確認することを主目的とし て用意されているインターフェイスであるため、受け渡し可能な引数にはRFC 3280の初期パラ メータは想定されていない。ファイルやレジストリなどを使った独自の手段を用いて渡す工夫が 必要となるであろう。