「ソフテックだより」では、ソフトウェア開発に関する情報や開発現場における社員の取り組みなどを定期的にお知らせしています。
さまざまなテーマを取り上げていますので、他のソフテックだよりも、ぜひご覧下さい。
ソフテックだより(発行日順)のページへ
ソフテックだより 技術レポート(技術分野別)のページへ
ソフテックだより 現場の声(シーン別)のページへ
私は入社2年目の社員で、主にWindowsアプリケーション開発を担当しています。昨年の9月頃、FeliCaによりID認証を行うシステム開発に携わった際に、PC/SCという技術を扱うことがありました。ICカードを利用したソフトを開発する場合、一般的には、リーダー/ライター専用のSDK(Software Development Kit)を購入して利用します。SDKにはカードの読み取り/書き込みなどのAPIが一通り用意されており、比較的容易な開発が可能となりますが、開発コストが高く、また他のリーダー/ライターでは動作しないソフトとなってしまいます。
今回のソフテックだよりでは、SDKを使用せずにPC/SCという標準インターフェース規格を利用して、FeliCaのIDmを取得する方法についてご紹介させていただきます。
FeliCa(フェリカ)は、ソニーが開発した非接触型ICカードの無線通信技術、およびその技術方式を採用した製品の総称です。FeliCaは日本全国で普及しており、身近なところでその利用を確認することが出来ます。例えば、JR東日本のプリペイドICカード「SuiCa」や、楽天Edyの電子マネー「Edy」、また、セブン&アイグループの「nanaco」、イオングループの「WAON」などもFeliCaを導入しています。特徴としては、「高い通信セキュリティ」「高速なデータ処理」「大容量の記憶容量」などがあり、セキュリティ面が重要視されるプリペイドカード・クレジットカードや、高速処理が要求される交通機関のシステムで採用されています。
FeliCaは、「IDm」と呼ばれる固有のID番号を持っています。IDmはICチップに記録された8バイト(16桁)の数字で、1枚のカード毎にそれぞれ固有の番号を有します。書き換えは不可能で、比較的簡単に読み取ることができることから応用範囲が広く、IDmを利用した様々なアプリケーションが開発され普及しています。
PC/SCとは、Windows環境でICカードを利用するための標準インターフェース規格のことをいいます。この規格は、メーカ各社のICカードやリーダー/ライターを相互利用できるようにするため、Microsoft社を中心としたPC/SC WorkGroupによって定められました。PC/SCを利用すれば、ICカードやリーダー/ライターなど各デバイスとの組み合わせを意識せずに開発することが可能となります。図1において、各デバイスで扱う数値データは、以下となります。
PC/SC規格ではWinSCard API(WinSCard.dll:Microsoft社)を使用してカードへアクセスします。このAPIを通じてカード内のファイルを読み取ったり、またファイルを作成することが可能となります。基本的な使い方は下記の通りです。
図1.WinSCard APIの使い方
図のように、『SCardTransmit』によりカードと通信を行って、データのやり取りを行います。その他のAPIについては、カードと通信を行うための準備・後始末を行うためのものですので、今回は詳細説明を省略いたします。
SCardTransmitでカードに送信するコマンドは、APDU(Application Protocol Data Unit)と呼ばれるバイナリデータです。APDUを送信すると、引数としてカードから応答データを受け取ることが出来ます。
例えばカードのIDmを取得するためのGetDataコマンドは、下記の通りに指定します。
CLA | INS | P1 | P2 | Le |
---|---|---|---|---|
0xFF | 0xCA | 0x00 | 0x00 | 0x00 |
その際に受け取るデータは下記の通り、Data部と2つのステータスワード(SW1, SW2)で構成されます。ステータスワードには、コマンド送信が成功したか、または失敗の要因を示す値が入ります。
取得データ |
---|
Data + SW1 + SW2 |
ここで、実際にFeliCaのIDmを取得する方法について説明します。リーダー/ライターには、ソニーのPaSoRi:RC-S380を使用します。
図2. ソニー製リーダー/ライター PaSoRi(RC-S380)
下記に、リーダー/ライターにセットされたカードのIDmを取得するサンプルプログラムを示します。カードとの通信は、「3‐1 カードアクセスAPI」で説明した通りの手順で行います。各APIでエラーが発生した場合、目的に応じてエラー処理を行う必要がありますが、今回の例では、カードとの通信を切断しリソースマネージャを解放する、という最低限の手順のみ行っています。
{
SCARDCONTEXT hContext;
SCARDHANDLE hCard;
LPTSTR lpszReaderName;
DWORD dwActiveProtocol;
DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;
LONG lResult;
// (1)SCardEstablishContext
// ⇒リソースマネージャのハンドルを取得
lResult = ::SCardEstablishContext( SCARD_SCOPE_USER, NULL, NULL, &hContext );
if( lResult != SCARD_S_SUCCESS )
{
goto END_FUNC;
}
// (2)SCardListReaders
// ⇒リーダー/ライターの名称を取得
lResult = ::SCardListReaders( hContext, NULL, (LPTSTR)&lpszReaderName, &dwAutoAllocate );
if( lResult != SCARD_S_SUCCESS )
{
goto END_FUNC;
}
// (3)SCardConnect
// ⇒カードに接続
lResult = SCardConnect( hContext, lpszReaderName, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol );
if( lResult != SCARD_S_SUCCESS )
{
goto END_FUNC;
}
// (4)SCardStatus
// ⇒ATR(Answer To Reset)を取得しカード種別を判定
DWORD dwReaderLen = sizeof( lpszReaderName );
DWORD dwState;
DWORD dwAtrLen;
BYTE byAtr[64]; //ATR
lResult = ::SCardStatus( hCard, NULL, &dwReaderLen, &dwState, &dwActiveProtocol,
NULL, &dwAtrLen );
if( lResult != SCARD_S_SUCCESS )
{
goto END_FUNC;
}
lResult = ::SCardStatus( hCard, lpszReaderName, &dwReaderLen, &dwState, &dwActiveProtocol,
byAtr, &dwAtrLen );
if( lResult != SCARD_S_SUCCESS )
{
goto END_FUNC;
}
// FeliCaかどうか?
if( byAtr[13] != 0x00 || byAtr[14] != 0x3b )
{
goto END_FUNC;
}
// (5)SCardTransmit
// ⇒カードにコマンド送信・データを受信
BYTE bySendCommand[] = {0xFF, 0xCA, 0x00, 0x00, 0x00}; // 送信コマンド
BYTE byRecvBuffer[256 + 2/* SW1+SW2 */]; // 受信バッファ
DWORD dwResponseSize = sizeof(byRecvBuffer);
lResult = ::SCardTransmit( hCard, SCARD_PCI_T1, bySendCommand, sizeof(bySendCommand), NULL,
byRecvBuffer, &dwResponseSize );
if( lResult != SCARD_S_SUCCESS )
{
goto END_FUNC;
}
END_FUNC:
// (6)SCardDisconnect
// ⇒カードとの通信を切断
::SCardDisconnect( hCard, SCARD_LEAVE_CARD );
// (7)SCardReleaseContext
// ⇒リソースマネージャのハンドルを解放
::SCardReleaseContext( hContext );
return lResult;
}
上記プログラムで実際にIDmが取得できるかどうか、サンプルアプリを作成して確認してみます。
図3.IDm読み取りサンプルアプリ
上図は、サンプルプログラムを読み取りボタンのイベントハンドラにコピーして、「(5)SCardTransmit」で取得したデータを、受信データと書かれたテキストボックスに表示するようにした簡単なアプリです。
PCに接続したPaSoRiにFeliCaカード(今回はSuiCaを使用しました)をセットし、読み取りボタンを押下すると、下記データが得られました。
図4.IDm読み取りサンプルアプリ 読み取り結果
正常に取得できているかどうかを、フリーで配布されているIDm取得ツールなどで確認してみます。今回はPaSoRi対応の「FeliCa IDm表示ツール」というソフトを利用します。リーダー/ライターにセットされたカードのIDmを読み取って表示する単純なツールです。
図5.FeliCa IDm 表示ツール
PaSoRiにカードをセットし、読み取りボタンを押下すると下記画面が表示されます。PC/SCにより取得したIDmと一致していますので、正常に取得できていることが判りました。
図6.FeliCa IDm 表示ツール 読み取り結果
APDUには、カードやリーダー/ライターなどのデバイスに依存する独自のコマンドが存在します。デバイス依存のAPDUを使用すると他のカード・リーダー/ライターでは動作しない可能性があるため、PC/SCにより代用できるコマンドがあれば、極力そちらを使用するようにするなど、対策を講じる必要があります。例えば、今回ご紹介したIDmの読み取りコマンドにおいても、PaSoRi専用のコマンドが存在し、そちらでもIDmを読み取ることが出来ます。使用したいコマンドがデバイス依存のものかどうかを事前に調査した上で、PC/SCで代用できる部分を共通化すると、汎用性の高いプログラムを作成することが出来ます。
今回は、PC/SCを用いたFeliCaのIDm読み取り方法の一例を紹介させていただきました。FeliCaを利用したIDmによる認証システムは、勤怠管理や受付管理、PCロック・ログインのシステムなど、現在様々な用途で活用されています。これからPC/SCを用いて開発をする方々のご参考になれば幸いです。
(D.S.)
関連ページへのリンク
関連するソフテックだより