EternalWindows
スマートカード / スマートカードデータベース

通常、スマートカードを販売するベンダーは、インストーラーを付属させています。 インストーラーによってインストールされるものは、CryptoAPIのCSPやPKCS #11の関数を実装したDLL、 ドライバなどであり、スマートカードデータベースへの書き込みもこのインストール作業に入ります。 スマートカードデータベースには、カードリーダやカードの名前が列挙されており、 次のレジストリキーで表されます。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais

スマートカードデータベースを参照するWinSCard APIはいくつもあります。 たとえば、SCardListReadersの第1引数にリソースマネージャのハンドルを指定した場合、 この関数はリーダからデバイス名を取得し、このデバイス名をエントリとして持つサブキーを 上記キーから検索します。 そして、該当したサブキーの名前をリーダ名として返します。 また、SCardLocateCardsという関数は、リーダにセットされたカードからATRを取得し、 この値を持つエントリのサブキーを上記キーから検索します。 そして、該当したサブキーの名前をカード名として返します。 こうした設計により、アプリケーションは複雑やデバイス名やATRから隠蔽され、 分りやすいリーダ名やカード名などで表現することができるようになります。 カードベンダーはリーダ名の書き込みをSCardIntroduceReaderで行い、 カード名の書き込みをSCardIntroduceCardTypeで行います。

スマートカードデータベースに書き込まれたリーダ名は、 前節で取り上げたSCardListReadersの第1引数に0を指定することで取得可能です。 また、カード名の列挙はSCardListCardsで行うことができます。

LONG SCardListCards(
  SCARDCONTEXT hContext,
  LPCBYTE pbAtr,
  LPCGUID rgguidInterfaces,
  DWORD cguidInterfaceCount,
  LPTSTR mszCards,
  LPDWORD pcchCards
);

hContextは、リソースマネージャのハンドルを指定しますが、0でも問題ありません。 pbAtrは、マッチングを行うためのATRを指定します。 これは、このATRを持つカードのみを取得する場合に利用します。 全てのカードを列挙したい場合は、NULLを指定します。 rgguidInterfacesは、マッチングを行うためのGUIDを指定します。 通常、GUIDによるマッチングは行わないため、NULLを指定します。 cguidInterfaceCountは、rgguidInterfacesに指定したGUID配列の要素数を指定します。 rgguidInterfacesにNULLを指定した場合は、0を指定します。 mszCardsは、列挙されたカード名を受け取るポインタまたはバッファを指定します。 pcchCardsは、SCARD_AUTOALLOCATEという定数、 またはバッファのサイズを格納した変数のアドレスを指定します

今回のプログラムは、スマートカードデータベースからリーダ名とカード名を列挙します。 これはレジストリへのアクセスとなるため、システムにカードリーダが接続されている必要はありません。

#include <windows.h>
#include <winscard.h>

#pragma comment (lib, "winscard.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	SCARDCONTEXT hContext;
	LPTSTR       lpszNames;
	LPTSTR       lp;
	LONG         lResult;
	DWORD        dwAutoAllocate = SCARD_AUTOALLOCATE;
	BOOL         bEnumReaders = TRUE;

	lResult = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
	if (lResult != SCARD_S_SUCCESS) {
		if (lResult == SCARD_E_NO_SERVICE)
			MessageBox(NULL, TEXT("Smart Cardサービスが起動されていません。"), NULL, MB_ICONWARNING);
		return 0;
	}

	if (bEnumReaders) {
		lResult = SCardListReaders(0, SCARD_ALL_READERS, (LPTSTR)&lpszNames, &dwAutoAllocate);
		if (lResult != SCARD_S_SUCCESS) {
			MessageBox(NULL, TEXT("カードリーダのリストを取得することができませんでした。"), NULL, MB_ICONWARNING);
			SCardReleaseContext(hContext);
			return 0;
		}
	}
	else {
		lResult = SCardListCards(0, NULL, NULL, 0, (LPTSTR)&lpszNames, &dwAutoAllocate);
		if (lResult != SCARD_S_SUCCESS) {
			MessageBox(NULL, TEXT("カードのリストを取得することができませんでした。"), NULL, MB_ICONWARNING);
			SCardReleaseContext(hContext);
			return 0;
		}
	}

	lp = lpszNames;
	while (*lp != '\0') {
		MessageBox(NULL, lp, TEXT("OK"), MB_OK);
		lp += lstrlen(lp) + 1;
	}

	SCardFreeMemory(hContext, lpszNames);
	SCardReleaseContext(hContext);

	return 0;
}

bEnumReadersがTRUEの場合はリーダ名を列挙し、FALSEの場合はカード名を列挙します。 SCARD_AUTOALLOCATEを指定することで、一連の名前を含んだメモリが内部で確保され、 そのアドレスがlpszNamesに返ることになります。 各名前は\0文字で区切られており、\0文字が2回続けて存在する場合は、 これ以上名前が存在しないことを意味します。


戻る