Mais conteúdo relacionado Semelhante a HCEでなんちゃってType4のNDEFタグをつくる (20) HCEでなんちゃってType4のNDEFタグをつくる4. 簡単な紹介
• HCE (Host-based Card Emulation) は、Android
4.4 KitKat で導入された新機能
• 今まで FeilCa チップや UIM といった SE
(Secure Element) 上で行っていたカード動作を
Host (Android OS) 上で行う
• これにより、端末がカードのように振る舞える
– 具体的な例としては、チケットとか、会員証とか
• プロトコルとしては ISO-DEP(ISO 14443-4) 上
に APDU コマンド(ISO7816-4) を流す形
– Type 4 タグ的な感じ
• Host 上で動く(擬似)カードは
HostApduService を継承したサービスとして誰
でも実装できる
4
5. SE はいらない!の図
今、一般的なカードエミュレー
ション
HCE を使ったカードエミュレー
ション
Android device
Android device
Host CPU
NFC
Controller
NFC Reader
Host CPU
Secure
Element
NFC
Controller
Secure
Element
NFC Reader
※Secure Element を使わないので、Secure Element 非対応端末でもカードエミュレーションが出来
る
※一般的に管理が厳格な Secure Element と異なり、誰でもカードアプリを作成できる
5
8. サービスの作り方
• 簡単 3ステップ!
– HostApduService を継承したサービスを実装する
– サービスの Manifest に Permission や Intentfilter を定義する
– 反応する AID を定義した xml ファイルを作る
8
9. 準備
• Eclipse で Project を作る!
– API は Level19 (4.4) 以上にする
– Activity は不要なのでチェックはずす
新規AndroidProject
Activity はいらない
API Level は 19 (4.4)
9
11. サービスを実装する
public class HceNdefTagSample extends HostApduService {
static enum CardStatus { INIT, SELECTED }
final static byte[] SUCCESS = {(byte) 0x90, (byte) 0x00 };
CardStatus mCardStatus = CardStatus.INIT;
@Override
public void onDeactivated(int arg0) {
// Reader から離れると、この API が呼び出される
mCardStatus = CardStatus.INIT;
}
@Override
public byte[] processCommandApdu(byte[] cmd, Bundle arg1) {
// Reader からコマンドが送信されると、この API が呼び出される
if(mCardStatus == CardStatus.INIT){
// 最初のコマンド == SELECT AID には 9000 を返す
mCardStatus = CardStatus.SELECTED;
return SUCCESS;
}
// 来たコマンドを 9000 をつけてオウム返しする
ByteBuffer buf = ByteBuffer.allocate(cmd.length+SUCCESS.length);
buf.put(cmd);
buf.put(SUCCESS);
return buf.array();
}
}
11
12. Manifest に定義する
• 作成したサービスの AndroidManifest.xml に下
記を行う:
– android.permission.NFC の Permission を追加
– android.permission.BIND_NFC_SERVICE の
Permission を追加
– android.nfc.cardemulation.action.HOST_APDU_SERV
ICE の action で起動するように intent-filter を
追加
– 反応する AID を定義した xml ファイルを metadata として指定
12
13. Manifest に定義する
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hcendeftagsample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.NFC"/>
NFC を使う
<application
android:allowBackup="true"
作ったサービスを登
android:icon="@drawable/ic_launcher"
録
android:label="@string/app_name"
android:theme="@style/AppTheme" >
Reader にかざされた
<service android:name=".HceNdefTagSample"
Permission を追加
時に起動するように
android:exported="true"
intent-filter 追加
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/apduservice"/>
</service>
反応する AID を定義した
</application>
外部 xml ファイルの指定
</manifest>
13
14. AID を定義した xml を作る
• /res/xml/ の下に AID を定義した xml を作る
– ファイル名は、さっき AndroidManifest.xml の
meta-data で指定したもの
xml フォルダを作って
新しく xml ファイルを
作成
14
15. AID を定義した xml を作る
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_name"
android:requireDeviceUnlock="false">
<aid-group android:description="@string/aid_description"
android:category="other">
<aid-filter android:name=" F0010203040506 "/>
</aid-group>
反応する AID を定義
</host-apdu-service>
複数の AID をまとめて aid-group を定義
#なぜかAID 単品では定義できない…(´・ω・
`)
##Description は文字列を直接指定できない
##ので String.xml に文字列定義を追加するこ
と
15
16. Install/実行
• 前述の作業を完了したら、通常通り apk を
insatll すれば OK
– adb install ***.apk
• あとは、登録した AID 向けの SELECT コマンド
を NFC Reader/Writer から受信すれば、自動的
にサービスが起動して動作します
• 実際に実行した時のコマンド・レスポンスは下
記の通り
処理(例)
データの流
れ
対向→端末
00 A4 04 00 07 F0 01 02 03 04 05 06
対向←端末
90 00
対向→端末
00 01 02 03 04 01 02 03 04
対向←端末
00 01 02 03 04 01 02 03 04 90 00
16
18. Type4 の NDEF タグ
• HCE を使い、Type 4 の NDEF タグっぽい動きを
するサービスを実装してみる
• やらないといけないことは下記:
– NDEF Tag Application の AID で反応するようにす
る
– NDEF Tag アプリケーションっぽく振る舞う Service
を実装する
18
19. NDEF Tag Application に反応
• Type 4 の NDEF タグは固定名の AID のアプリ
ケーション (NDEF Tag Application) を持って
いる
• NDEF Tag Application は
AID: D2760000850101
• さっき作った apduservice.xml の AID をこの
ID に変更すれば、NDEF Tag の読み出しに反応
できるようになる
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_name"
android:requireDeviceUnlock="false">
<aid-group android:description="@string/aid_description"
android:category="other">
<aid-filter android:name=" D2760000850101 "/>
</aid-group>
NDEF Tag Application に反応するように指定
</host-apdu-service>
19
20. NDEF Tag App. の中身を実装
• onTagDiscovered() が NDEF Tag Application
として振る舞うように動作するように実装する
• 具体的には:
– NDEF Tag Application が選択されたら動く
– 中には2つのファイルがある:
• Capability Container FILE … 読み書きの最大データサイ
ズや NDEF FILE の ID などが格納されているファイル
• NDEF FILE … NDEF 本体が格納されているファイル
– なので、onTagDiscovered() ではそれぞれのファイ
ルが選択、Read された際に適切にデータを返せるよ
うにする
20
21. NDEF Tag App. の中身を実装
• NDEF タグ読み出し処理の流れは下記:
データの流
れ
処理
対向→端末
対向 R/W から、NDEF Tag Application が SELECT される
対向←端末
90 00 を返却する
対向→端末
対向 R/W から CCFILE が SELECT される
対向←端末
90 00 を返却する
対向→端末
対向 R/W から READ BINARY(offset:0/size:16バイト)される
対向←端末
CCFILE の情報(アクセス制御情報、NDEF FILE識別子等)を返却する
対向→端末
対向 R/W NDEFFILE が SELECT される
対向←端末
90 00 を返却する
対向→端末
対向 R/W から READ BINARY(offset:0/size:2バイト)される
対向←端末
NDEF データのサイズを返却する
対向→端末
対向 R/W から READ BINARY(offset:2/size:NDEFサイズバイト) される
対向←端末
NDEF データを返却する
21
22. NDEF Tag App. の中身を実装
@Override
public byte[] processCommandApdu(byte[] apdu, Bundle arg1) {
switch(mCardStatus){
case INIT: // 初期状態
if(checkCmd(apdu, CMD_SELECT_BY_DF) && checkData(apdu, strToHex(TAGAPP_AID))){
mCardStatus = CardStatus.TAGAPP_SELECTED;
return RES_SUCCESS;
}
return RES_NOTFOUND;
case TAGAPP_SELECTED: // NDEF TAG App. が選択された状態
if(apdu[INS] == INS_SELECT){
return selectFile(apdu);
}
case CCFILE_SELECTED: // CCFILE が選択された状態、16バイト、通信の最大サイズとかが書かれてる
if(apdu[INS] == INS_SELECT){
return selectFile(apdu);
}
if(apdu[INS] == INS_READ_BINARY){
int offset = apdu[P1]*256 + apdu[P2];
int length = apdu[LC];
return Arrays.copyOfRange(strToHex(CCFILE_DATA), offset, offset+length);
}
return RES_NOTFOUND;
// 続く
22
23. NDEF Tag App. の中身を実装
case NDEFFILE_SELECTED: // NDEFFILE が選択された状態、先頭2バイトはサイズ、その後ろは NDEF そのもの
if(apdu[INS] == INS_SELECT){
return selectFile(apdu);
}
if(apdu[INS] == INS_READ_BINARY){
int offset = apdu[P1]*256 + apdu[P2];
int length = apdu[LC];
return Arrays.copyOfRange(mNdefMessageData, offset, offset+length);
}
return RES_NOTFOUND;
default:
break;
}
return RES_NOTFOUND;
}
private byte[] selectFile(byte[] apdu) {
if(checkCmd(apdu, CMD_SELECT_EF)){
if(checkData(apdu, strToHex(CCFILE_EF))){
mCardStatus = CardStatus.CCFILE_SELECTED;
}
if(checkData(apdu, strToHex(NDEFFILE_EF))){
mCardStatus = CardStatus.NDEFFILE_SELECTED;
}
return RES_SUCCESS;
}
return RES_NOTFOUND;
}
// その他、各種定義とか省略
23
24. 実行
• 実際に実行した時のコマンド・レスポンスは下
記の通り
データの流
れ
対向→端末
00 A4 04 00 07 D2 76 00 00 85 01 01 00 (NDEF タグアプリ読むよ)
対向←端末
90 00 (おっけー)
対向→端末
00 A4 00 0C 02 E1 03 (CCFILE 開くよ)
対向←端末
90 00 (おっけー)
対向→端末
00 B0 00 00 0F (00~0F のデータ頂戴)
対向←端末
0F 20 00 40 00 40 E1 04 04 00 00 FF 00 00 00 (はいこれ~)
対向→端末
00 A4 00 0C 02 E1 04 (NDEF は E104 なのね?じゃあそれ開いて)
対向←端末
90 00 (おっけー)
対向→端末
00 B0 00 00 02 (じゃあ最初の2バイト=NDEFサイズ教えて)
対向←端末
00 0F (15バイトだよ)
対向→端末
00 B0 00 02 12 (じゃあ2バイト目から15バイトの NDEF 読むね)
対向←端末
D1 01 0B 55 01 67 6F 6F 67 6C 65 2E 63 6F 6D 00 00 00 (はい、これが NDEF)
24
26. やってみた感想
• とりあえず Reader/Writer に反応するようにす
るのはとても簡単
• セキュリティ的にうるさく無いシステムなら、
今 Type A カードでやってるようなサービス
(入出門管理とか)は簡単にモバイル対応でき
る気がする
• セキュリティが大切なやつはクラウド側でやれ
ばいいか?
• 作ったなんちゃって Type 4 タグを Android 端
末で読もうとしたら「タップしてビーム」とか
言われてがっかり
– enableReaderMode できる 4.4 端末が欲しい(´・
26