ICカードの読み書きを試してみようと思って、ACR122というNFC リーダーを買ってみました。
http://www.nfc-reader.com/jp/acr122.php
無地のMifareカード10枚付きで1万円だったので、まぁまぁ安いんじゃないでしょうか。このリーダーはAPDUという方式で通信を行うため「SDKが不要」というのが魅力でした。標準のAPIドキュメントには載っていませんが、J2SE6にはAPDUをサポートするAPIが既に入っているんですよね。
http://java.sun.com/javase/ja/6/docs/ja/jre/api/security/smartcardio/spec/javax/smartcardio/package-summary.html
カードリーダーの技術資料には「FF CA 00 00 04」というバイト列を送ればカードにユニークなIDが取得できると説明されていたので、↑のページにあるサンプルコードを参考に、次のようなコードを書いてみました。
バイト列を16進数で表示するのって結構メンドクサイですね。上の例では、IntegerクラスのtoHexStringメソッドで16進数に変換していますが、これだと十進数で128から上がマイナスになってしまうので0xffでアンドを取って、更にtoUpperCaseで大文字に揃えて、それぞれのバイトが表す16進数が1桁の場合は頭に0を挿入するなんてことをやっています。
購入したリーダーとカードのセットでは、1Kバイトほどのデータ領域があって、値を自由に書いたり読んだりすることもできます。データの読み書きには認証キーを指定する必要があり、これを知らない人(プログラム)が勝手にデータを書いたり読んだりできないようになっています。
↑のプログラムに読み書き用のコードを追加してtestとかbakaとかtakegataとか書いたり読んだりして遊んでいるだけでも結構楽しいのですが、ふと、その辺にあったSuicaを何気に置いてみたらIDらしきデータが読めるじゃないですか。ちょっとびっくり。じゃぁEdyカードはどうかと思ってリーダーに乗せてみると、やはりIDらしきデータが読み取れます(認証キーが分からないので、データ領域は読めません)。
カードリーダーの仕様を見ると「Felicaカードに支援」と、微妙な日本語で対応を表明しています。調べてみたら、これはIDmと呼ばれるユニークなIDらしく、林優さんという方のウェブで紹介されているプログラムを使ってPaSoRiで確認したら同じデータが読み取れました。
http://yuuhayashi.blogspot.com/2009/01/pasorifelicaidm_30.html
ただ残念なことに、私が書いたプログラムではPaSoRiにアクセスできませんでした。J2SE6の標準APIでは読めないようです。林さんのページで紹介されているプログラムでは、felicalibというライブラリを使っていました。Javaからfelicalib経由でPaSoRiにアクセスする方法は亜細亜大学の先生が↓のページで紹介しています。
http://itasan.mydns.jp/wiki.cgi/ASIA?page=Java%A4%CE%B1%FE%CD%D1%A1%A7Felica%A5%AB%A1%BC%A5%C9%A5%EA%A1%BC%A5%C0#p0
Felica SDKにも興味はありますが、興味だけで買ってみるにはちょっと高い感じですね。
http://www.nfc-reader.com/jp/acr122.php
無地のMifareカード10枚付きで1万円だったので、まぁまぁ安いんじゃないでしょうか。このリーダーはAPDUという方式で通信を行うため「SDKが不要」というのが魅力でした。標準のAPIドキュメントには載っていませんが、J2SE6にはAPDUをサポートするAPIが既に入っているんですよね。
http://java.sun.com/javase/ja/6/docs/ja/jre/api/security/smartcardio/spec/javax/smartcardio/package-summary.html
カードリーダーの技術資料には「FF CA 00 00 04」というバイト列を送ればカードにユニークなIDが取得できると説明されていたので、↑のページにあるサンプルコードを参考に、次のようなコードを書いてみました。
import java.util.List;
import javax.smartcardio.*;
public class SmartCardReadId{
public static void main(String[] args){
try{
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
// get the first terminal
CardTerminal terminal = terminals.get(0);
// establish a connection with the card
Card card = terminal.connect("T=1");
CardChannel channel = card.getBasicChannel();
byte[] c1 = {(byte)0xFF,(byte)0xCA,(byte)0x00,(byte)0x00,(byte)0x04};
ResponseAPDU r = channel.transmit(new CommandAPDU(c1));
StringBuilder sb = new StringBuilder();
for(byte a:r.getData()){
String hexValue=Integer.toHexString(a & 0xff).toUpperCase();
if(hexValue.length()==1){
hexValue="0"+hexValue;
}
sb.append(hexValue);
}
System.out.println("ID:"+ sb.toString());
// disconnect
card.disconnect(false);
}catch(CardException e){
e.printStackTrace();
}
}
}
CommandAPDUクラスのコンストラクタにコマンドを渡してオブジェクトを作り、channelオブジェクトのtransmitメソッドで送るとResponseAPDUオブジェクトが帰ってきて、getDataメソッドでIDのバイト列が取得できるというわけです。import javax.smartcardio.*;
public class SmartCardReadId{
public static void main(String[] args){
try{
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
// get the first terminal
CardTerminal terminal = terminals.get(0);
// establish a connection with the card
Card card = terminal.connect("T=1");
CardChannel channel = card.getBasicChannel();
byte[] c1 = {(byte)0xFF,(byte)0xCA,(byte)0x00,(byte)0x00,(byte)0x04};
ResponseAPDU r = channel.transmit(new CommandAPDU(c1));
StringBuilder sb = new StringBuilder();
for(byte a:r.getData()){
String hexValue=Integer.toHexString(a & 0xff).toUpperCase();
if(hexValue.length()==1){
hexValue="0"+hexValue;
}
sb.append(hexValue);
}
System.out.println("ID:"+ sb.toString());
// disconnect
card.disconnect(false);
}catch(CardException e){
e.printStackTrace();
}
}
}
バイト列を16進数で表示するのって結構メンドクサイですね。上の例では、IntegerクラスのtoHexStringメソッドで16進数に変換していますが、これだと十進数で128から上がマイナスになってしまうので0xffでアンドを取って、更にtoUpperCaseで大文字に揃えて、それぞれのバイトが表す16進数が1桁の場合は頭に0を挿入するなんてことをやっています。
購入したリーダーとカードのセットでは、1Kバイトほどのデータ領域があって、値を自由に書いたり読んだりすることもできます。データの読み書きには認証キーを指定する必要があり、これを知らない人(プログラム)が勝手にデータを書いたり読んだりできないようになっています。
↑のプログラムに読み書き用のコードを追加してtestとかbakaとかtakegataとか書いたり読んだりして遊んでいるだけでも結構楽しいのですが、ふと、その辺にあったSuicaを何気に置いてみたらIDらしきデータが読めるじゃないですか。ちょっとびっくり。じゃぁEdyカードはどうかと思ってリーダーに乗せてみると、やはりIDらしきデータが読み取れます(認証キーが分からないので、データ領域は読めません)。
カードリーダーの仕様を見ると「Felicaカードに支援」と、微妙な日本語で対応を表明しています。調べてみたら、これはIDmと呼ばれるユニークなIDらしく、林優さんという方のウェブで紹介されているプログラムを使ってPaSoRiで確認したら同じデータが読み取れました。
http://yuuhayashi.blogspot.com/2009/01/pasorifelicaidm_30.html
ただ残念なことに、私が書いたプログラムではPaSoRiにアクセスできませんでした。J2SE6の標準APIでは読めないようです。林さんのページで紹介されているプログラムでは、felicalibというライブラリを使っていました。Javaからfelicalib経由でPaSoRiにアクセスする方法は亜細亜大学の先生が↓のページで紹介しています。
http://itasan.mydns.jp/wiki.cgi/ASIA?page=Java%A4%CE%B1%FE%CD%D1%A1%A7Felica%A5%AB%A1%BC%A5%C9%A5%EA%A1%BC%A5%C0#p0
Felica SDKにも興味はありますが、興味だけで買ってみるにはちょっと高い感じですね。
投稿:竹形 誠司[takegata]/2009年 02月 26日 05時 00分
/更新:2011年 05月 25日 10時 15分