EternalWindows
スマートカード / IC製造者識別子ファイル

JICSAP仕様のスマートカードには、0x2f11という識別子を持ったIC製造者識別子ファイルが存在することがあります。 カード識別子がカード製造者の情報を格納するのに対し、 IC製造者識別子ファイルはIC製造者の情報を格納することになります。 このファイルの形式はDO-WEFであり、次のようなデータを格納しています。

T(1バイト) L(1バイト) データ(Lの値)
0x455埋込者/IC組立業者識別子
0x461IC製造者識別子
2製造者ICタイプ識別子

DO-WEFは、単一または複数のデータオブジェクトを格納するファイル形式です。 データオブジェクトはデータを維持し、タグによって識別されます。 たとえば、0x45で識別できるデータオブジェクトは、 埋込者/IC組立業者識別子と呼ばれるデータを維持していることを意味しています。 DO-WEFの構造を理解するために、前節で述べたレコード構造のWEFと比較しながら見ていきます。

まずは、DO-WEFとレコード形式のWEFの違いを確認しておきましょう。 レコード形式のWEFには各レコードに番号があり、 その番号を指定してTLV構造のデータを取得します。 また、このTLV構造のデータフィールドから実際のデータを取り出す必要があります。 一方、DO-WEFではデータを維持するがレコードではなくデータオブジェクトであり、 これはタグによって識別されます。 また、データオブジェクトからのデータの取得は、実際のデータのみが返るようになっています。 当然ながら、1つのDO-WEFに同じタグを持ったデータオブジェクトが存在することはありません。

データオブジェクトの存在意義は、アプリケーションから目的のデータの検索を容易にし、 DO-WEFの存在を抽象化させることです。 より具体的には、データオブジェクトのタグとそれが存在するDFさえ知っていれば、 データを取得できるような環境を作り出すことです。 こうした事情を踏まえた場合、ある2つの仕様が自然と確定することになります。 1つ目は、DO-WEF内のデータオブジェクトのタグが、 DF内で一意にならなければならないという点です。 つまり、同じDF内に複数のDO-WEFが存在する場合は、 それぞれのデータオブジェクトが異なったタグを持っている必要があります。 そして2つ目は、取得対象のデータオブジェクトを格納するDO-WEFを予め選択する必要がないという点です。 DFを選択していれば、その直下に存在する個々のDO-WEFのデータオブジェクトは検索対象となりますから、 目的のデータオブジェクトを格納するDO-WEFのファイル識別子を知っておく必要はありません。

データオブジェクトからデータを取得するには、GET DATAコマンドを利用します。 このコマンドのINSは0xcaであり、P1とP2の値は次のようになります。

P1とP2 説明
0x0040〜0x00fe P1が0x00固定で、P2に0x40から0xfeまでのタグを指定する。 BER-TLV構造のデータを取得する場合に利用する。
0x0100〜0x00ff P1が0x01固定で、P2に0x00から0xfeまでのタグを指定する。 アプリケーションデータを取得する場合に利用する。 なお、JICSAP2.0の仕様書にこの記述はない(ISO 7816-4を参照)。
0x0200〜0x02fe P1が0x02固定で、P2に0x00から0xfeまでのタグを指定する SIMPLE-TLV構造のデータを取得する場合に利用する。
0x4001〜0xfffe 2バイトのタグの上位バイトをP1に指定し、下位バイトをP2に指定する。 BER-TLV構造のデータを取得する場合に利用する。

SIMPLE-TLV構造というのは、通常のTLV構造のことを指します。 一方、BER-TLV構造は、タグの1ビット毎に特定の意味が含まれており、 値フィールドがBER形式でエンコードされています。 基本的にP1には0x01または0x02を指定することになりますが、 この明確な区別についてはよく分かりません。 冒頭で示したIC製造者識別子ファイルは、0x45と046のタグを持つDO-WEFでしたが、 0x02を指定すると取得できるようです。

今回のプログラムは、GET DATAコマンドでIC製造者識別子ファイルのデータオブジェクトを取得します。

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

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

BOOL SendCommand(SCARDHANDLE hCard);
BOOL SelectMf(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;
	}

	if (SelectMf(hCard))
		SendCommand(hCard);
		
	SCardDisconnect(hCard, SCARD_LEAVE_CARD);

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

	return 0;
}

BOOL SendCommand(SCARDHANDLE hCard)
{
	TCHAR  szBuf[256];
	DWORD  dwResponseSize;
	LPBYTE lp;
	BYTE   response[5 + 2];
	BYTE   commandGet1[] = {0x00, 0xca, 0x02, 0x45, 0};
	BYTE   commandGet2[] = {0x00, 0xca, 0x02, 0x46, 0};
	
	lp = response;
	dwResponseSize = sizeof(response);
	SCardTransmit(hCard, SCARD_PCI_T1, commandGet1, sizeof(commandGet1), NULL, response, &dwResponseSize);
	wsprintf(szBuf, TEXT("埋込者/IC組立業者識別子 : %#x %#x %#x %#x %#x"), lp[0], lp[1], lp[2], lp[3], lp[4]);
	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);

	dwResponseSize = sizeof(response);
	SCardTransmit(hCard, SCARD_PCI_T1, commandGet2, sizeof(commandGet2), NULL, response, &dwResponseSize);
	wsprintf(szBuf, TEXT("IC製造者識別子 : %#x"), lp[0]);
	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
	wsprintf(szBuf, TEXT("製造者ICタイプ識別子 : %#04x"), MAKEWORD(lp[2], lp[1]));
	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
	
	return TRUE;
}

BOOL SelectMf(SCARDHANDLE hCard)
{
	DWORD dwResponseSize;
	LONG  lResult;
	BYTE  response[2];
	BYTE  command[] = {0x00, 0xa4, 0x00, 0x00};

	dwResponseSize = sizeof(response);
	lResult = SCardTransmit(hCard, SCARD_PCI_T1, command, sizeof(command), NULL, response, &dwResponseSize);
	if (lResult != SCARD_S_SUCCESS)
		return FALSE;

	return response[dwResponseSize - 2] == 0x90 && response[dwResponseSize - 1] == 0x00;
}

自作関数のSendCommandでは、0x45のデータオブジェクトと0x46のデータオブジェクトを取得するコマンドが宣言されています。 GET DATAコマンドを送るためINSは0xcaであり、Leには0を指定します。 Leは取得するデータのサイズを指定するフィールドですが、 GET DATAコマンドの場合はここに0を指定することで、 必要なデータを全て取得できるようになっています。 もちろん、そのためにはレスポンスのサイズが十分でなければならないため、 埋込者/IC組立業者識別子のサイズである5を加算しています。


戻る