EternalWindows
スマートカード / スマートカードへの接続

カードリーダにセットされたカードにアクセスするためには、 SCardConnectでカードに接続する必要があります。 この関数は、次のように定義されています。

LONG SCardConnect(
  SCARDCONTEXT hContext,
  LPCTSTR szReader,
  DWORD dwShareMode,
  DWORD dwPreferredProtocols,
  LPSCARDHANDLE phCard,
  LPDWORD pdwActiveProtocol
);

hContextは、リソースマネージャのハンドルを指定します。 szReaderは、カードをセットしているリーダの名前を指定します。 dwShareModeは、共有モードを表す定数を指定します。 通常は、SCARD_SHARE_SHAREDを指定します。 dwPreferredProtocolsは、想定するカードのプロトコルを表す定数を指定します。 phCardは、カードと接続したハンドルを受け取る変数のアドレスを指定します。 pdwActiveProtocolは、実際のカードのプロトコルを受け取る変数のアドレスを指定します。 不要な場合は、NULLを指定することができます。

カードのプロトコルには、T=0とT=1があります。 T=0はキャラクタベースのプロトコルであり、SCARD_PROTOCOL_T0で表されます。 一方、T=1はブロックベースのプロトコルであり、SCARD_PROTOCOL_T1で表されます。 アプリケーションがT=1のプロトコルを持ったカードのみを扱いたいような場合、 dwShareModeにSCARD_PROTOCOL_T1を単一で指定することによって、 T=0のカードがセットされている場合は関数を失敗させることができます。 逆にどちらのプロトコルのカードでも対応したい場合は、 SCARD_PROTOCOL_T0とSCARD_PROTOCOL_T1の両方を指定することになります。 この場合、実際にカードのプロトコルを特定するには、pdwActiveProtocolを参照します。 これが、たとえばSCARD_PROTOCOL_T0であるならば、 セットされているカードのプロトコルはT=0ということになります。 基本的に、カードベンダーは、カードの仕様説明の箇所にプロトコルを記述しています。

カードのハンドルを取得すれば、カードがセットされているリーダ名やカードの現在の状態、 さらにカードのプロトコルやATRを取得できるようになります。 これには、SCardStatusを呼び出します。

LONG SCardStatus(
  SCARDHANDLE hCard,
  LPTSTR szReaderName,
  LPDWORD pcchReaderLen,
  LPDWORD pdwState,
  LPDWORD pdwProtocol,
  LPBYTE pbAtr,
  LPDWORD pcbAtrLen
);

hCardは、カードのハンドルを指定します。 szReaderNameは、カードがセットされているリーダ名を受け取るバッファを指定します。 pcchReaderLenは、szReaderNameに指定したバッファのサイズか、 SCARD_AUTOALLOCATEを指定します。 pdwStateは、カードの状態を受け取る変数のアドレスを指定します。 pdwProtocolは、カードのプロトコルを受け取る変数のアドレスを指定します。 pbAtrは、カードのATRを受け取るバッファを指定します。 pcbAtrLenは、ATRのサイズを受け取る変数のアドレスを指定します。 基本的に、hCard以外の引数については、不要な場合はNULLを指定することができます。

ATR(Answer To Reset)とは、カードが電気的に活性化した際にカードから送られる初期応答情報です。 これは最高32バイトであり、転送速度やカードを識別情報が含まれています。 各情報はキャラクタ単位で識別され、たとえば1バイト目は開始キャラクタ(TS)と呼ばれています。 ATRはカードが電気的に活性化した際に送られるものですから、 それを取得するのに必ずしもカードへの接続が必要になるわけではありません。 たとえば、SCardGetStatusChangeでカードがセットされたことを検出した 時点でATRは取得しています。

カードのハンドルが不要になった場合は、SCardDisconnectでカードの接続を終了します。

LONG SCardDisconnect(
  SCARDHANDLE hCard,
  DWORD dwDisposition
);

hCardは、カードのハンドルを指定します。 dwDispositionは、カードの接続を終了すると同時に行いたい操作を指定します。 基本的には、特に何もしないことを示すSCARD_LEAVE_CARDを指定します。

今回のプログラムは、SCardConnectでカードに接続します。 また、SCardStatusでカードのATRを取得し、そのATRがシステムで認識できるかどうかを調べます。

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

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

BOOL ShowCardName(SCARDCONTEXT hContext, SCARDHANDLE hCard);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	SCARDCONTEXT hContext;
	SCARDHANDLE  hCard;
	LPTSTR       lpszReaderName;
	LONG         lResult;
	DWORD        dwActiveProtocol;
	DWORD        dwAutoAllocate = SCARD_AUTOALLOCATE;

	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;
	}

	lResult = SCardListReaders(hContext, NULL, (LPTSTR)&lpszReaderName, &dwAutoAllocate);
	if (lResult != SCARD_S_SUCCESS) {
		if (lResult == SCARD_E_NO_READERS_AVAILABLE)
			MessageBox(NULL, TEXT("カードリーダが接続されていません。"), NULL, MB_ICONWARNING);
		SCardReleaseContext(hContext);
		return 0;
	}

	lResult = SCardConnect(hContext, lpszReaderName, SCARD_SHARE_SHARED,  SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
	if (lResult != SCARD_S_SUCCESS) {
		if (lResult == SCARD_W_REMOVED_CARD)
			MessageBox(NULL, TEXT("カードがセットされていません。"), NULL, MB_ICONWARNING);
		SCardFreeMemory(hContext, lpszReaderName);
		SCardReleaseContext(hContext);
		return 0;
	}
	
	ShowCardName(hContext, hCard);

	SCardDisconnect(hCard, SCARD_LEAVE_CARD);

	SCardFreeMemory(hContext, lpszReaderName);
	SCardReleaseContext(hContext);

	return 0;
}

BOOL ShowCardName(SCARDCONTEXT hContext, SCARDHANDLE hCard)
{
	BYTE   atr[MAXIMUM_ATTR_STRING_LENGTH];
	LPTSTR lpszCardName;
	LONG   lResult;
	DWORD  dwAtrSize;
	DWORD  dwAutoAllocate = SCARD_AUTOALLOCATE;
	
	dwAtrSize = sizeof(atr);
	lResult = SCardStatus(hCard, NULL, NULL, NULL, NULL, atr, &dwAtrSize);
	if (lResult != SCARD_S_SUCCESS) {
		MessageBox(NULL, TEXT("ステータスの取得に失敗しました。"), NULL, MB_ICONWARNING);
		return FALSE;
	}
	
	lResult = SCardListCards(hContext, atr, NULL, 0, (LPTSTR)&lpszCardName, &dwAutoAllocate);
	if (lResult != SCARD_S_SUCCESS) {
		MessageBox(NULL, TEXT("カード名の取得に失敗しました。"), NULL, MB_ICONWARNING);
		return FALSE;
	}
	
	MessageBox(NULL, lpszCardName, TEXT("OK"), MB_OK);
	
	SCardFreeMemory(hContext, lpszCardName);

	return TRUE;
}

ShowCardNameという関数は、SCardStatusでカードのATRを取得します。 これをSCardListCardsに指定した場合、ATRに関連するレジストリキーがスマートカードベースで検索され、 第5引数にカードの名前が格納されます。 返された文字列が空白である場合、指定したATRがスマートカードベースに書き込まれていないことを意味するため、 アプリケーションによっては予期しないカードと判断するかもしれません。 実際にカードに対してコマンドを送る方法については、後の節で説明します。

トランザクションの開始

SCardConnectの第3引数にSCARD_SHARE_SHAREDを指定した場合、 カードへのアクセスは基本的にどのアプリケーションでも可能となります。 ただし、あるアプリケーションがSCardBeginTransactionを呼び出してトランザクションを開始している場合、 他のアプリケーションは、そのアプリケーションがSCardEndTransactionを呼び出すまで カードにアクセスすることはできません。

SCardBeginTransaction(hCard);

// カードにアクセスする処理を行う

SCardEndTransaction(hCard, SCARD_LEAVE_CARD);

あるアプリケーションがこのようなコードを実行した場合、 他のアプリケーションによるSCardConnectやSCardTransmitの呼び出しは、 SCardEndTransactionが実行されるまで制御が返りません。 トランザクションは、カードに送信する一連のコマンドに整合性を持たせるための重要な機能ですが、 上記した点については常に意識しておくべきといえます。



戻る