NFC#09 NFC処理実装[前編] – カードアクセス
今回は開発用のブランクカードを使い、ICカード内のデータに対するデータ読み書きを実装します。
|
【注意事項】 管理者の許可なくデータの書き込みや改変を行う行為は、不正アクセス禁止法や刑法等の法令に違反する可能性があります。 |
・スマートフォンやICカードとの連携機能を実装したいエンジニア
・IoT機器や非接触決済の技術に関心のある開発者
開発環境
- OS: macOS Sonoma 14.5
- Xcode: Xcode 15.4 (15F31d)
使用するICカード

今回使用するカードは、何もデータが書き込まれていないブランクカードを使用します。
なお、開発で使用するカードには目印としてシールを貼っております。
また、このカードは ISO/IEC 14443-4 Type A に準拠、及び NFC フォーラム タイプ4タグ に対応しております。
|
注) |
代表的な通信手順
カードと通信するための代表的なシーケンス、及びAPDUコマンドの対応は下表の通りです。
| 手順 | 操作 | 対応コマンド | CLA | INS | P1 | P2 |
| 1 | Transparent Session 開始 | Manage Session | FFh | C2h | 00h | 00h |
| 2 | プロトコル切り替え | Switch Protocol | FFh | C2h | 00h | 02h |
| 3 | フラグ設定(カードコマンドに依存) | Transparent Exchange | FFh | C2h | 00h | 01h |
| 4 | RF 出力 ON | Manage Session | FFh | C2h | 00h | 00h |
| 5 | カードコマンド送受信 | Transparent Exchange | FFh | C2h | 00h | 01h |
| 6 | RF 出力 OFF | Manage Session | FFh | C2h | 00h | 00h |
| 7 | Transparent Session 終了 | Manage Session | FFh | C2h | 00h | 00h |
ここで、異なる操作で同一のコマンドが使用されますが、コマンドデータの内容に応じて各操作が実現されます。
また、コマンドデータは前回の記事で紹介したTLV形式で設定されます。
各操作を実現するためのコマンドパケット
それでは実装を進めていきます。
実装と言っても、データが変わるだけで送信・受信処理はほとんど変わらないため、本記事ではコマンドパケットのみに注目して解説していきます。
なお、今回はプログラムが複雑にならないよう、 Start メソッド内(前回記事のバージョン取得の直後)に実装していきます。
|
Start メソッドはドライバ起動時の処理を実装するため、本来であれば Start メソッドにカードアクセス処理を実行するのは好ましくありません。(製品の仕様によります) |
1. Transparent Session 開始
uint8_t sendStartTransparentBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0x11, 0xef, // データ長、チェックサム
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0x07, 0x00, 0x00, 0x00, // データ長
0x00, 0x01, // スロット番号、シーケンス番号
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x00, // CLA, INS, P1, P2
0x02, // Lc
// Data In
0x81, // Tag : Start Transparent Session
0x00, // Length
0x49, // データチェックサム
0x00 // ポストアンブル
};
カードと通信するためには、まず Transparent Session を開始して RC-S660/S を Transparent 状態にする必要があります。
以降の操作で使用する一部のコマンドは、 Transparent 状態ではない時は使用不可であり、実行するとエラーが返却されます。
Transparent Session 開始のためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 81h : Start Transparent Session |
| Length | 00h |
| Value | なし |
2. プロトコル切り替え
uint8_t sendSwitchProtocolBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0x13, 0xed, // データ長、チェックサム
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0x09, 0x00, 0x00, 0x00, // データ長
0x00, 0x02, // スロット番号、シーケンス番号
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x02, // CLA, INS, P1, P2
0x04, // Lc
// Data In
0x8f, // Tag : Switch Protocol Data Object
0x02, // Length
0x00, 0x04, // Value : Standard type, Layer
0x2e, // データチェックサム
0x00 // ポストアンブル
};
次に Transparent Session で使用する規格を指定してカードを捕捉します。
NFC は準拠する規格に応じて、 Type A や Type F というように区別されます。
ここで設定する規格と、タッチされたカードが準拠する規格が適合していれば、 RC-S660/S とカード間でNFC通信が可能となります。
プロトコル切り替えのためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 8Fh : Switch Protocol Data Object |
| Length | 02h |
| Value | 1バイト目:規格 2バイト目:レイヤー |
今回使用するカードは ISO/IEC 14443-4 Type A に準拠してるので、1バイト目の規格には Type A を表す 00h を設定します。
また、レイヤーはカードの捕捉まで実行する 04h を設定します。
|
カードが準拠する規格によって規格・レイヤーに設定する値が変わるため、カードが準拠している規格を確認の上、公式リファレンスに従って設定する必要があります。 |
|
カードの捕捉は、本コマンドを実行する瞬間にのみ実施されます。 |
3. フラグ設定(カードコマンドに依存)
uint8_t sendTransmissionReceptionFlagBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0x13, 0xed, // データ長、チェックサム
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0x09, 0x00, 0x00, 0x00, // データ長
0x00, 0x03, // スロット番号、シーケンス番号
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x01, // CLA, INS, P1, P2
0x04, // Lc
// Data In
0x90, // Tag : Transmission and Reception Flag
0x02, // Length
0x00, 0x00, // Value : 送受信処理フラグ
0x31, // データチェックサム
0x00 // ポストアンブル
};
カードコマンドに応じて送受信に関する挙動を設定します。
今回は送受信処理フラグ設定のみ実施します。
送受信処理フラグ設定のためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 90h : Transmission and Reception Flag |
| Length | 02h |
| Value | 送受信処理フラグ |
送受信処理フラグでは、 Protocol Prologue(ISO/IEC 14443-4 の PCB, CID, NAD) や CRC, パリティの自動処理の挙動を設定できます。
今回は全て自動処理する設定として 0000h を設定します。
4. RF 出力 ON
uint8_t sendRfOnBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0x11, 0xef, // データ長、チェックサム
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0x07, 0x00, 0x00, 0x00, // データ長
0x00, 0x04, // スロット番号、シーケンス番号
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x00, // CLA, INS, P1, P2
0x02, // Lc
// Data In
0x84, // Tag : Turn On RF Field
0x00, // Length
0x43, // データチェックサム
0x00 // ポストアンブル
};
送受信の準備ができたらRF出力をオンにします。
NFCは電波を利用して無線通信します。RF出力をオンにすることで通信のための電波を発生させます。
RF出力オンのためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 84h : Turn On RF Field |
| Length | 00h |
| Value | なし |
5. カードコマンド送受信
RF出力をオンにしたらカードと任意のデータを送受信することできるようになります。
カードが準拠する仕様に則ってデータを送信することで、 RC-S660/S からカード内のデータを操作することができます。
今回使用するカードは NFC フォーラム タイプ4タグ に準拠しています。
この仕様に則ると、複数回のデータ通信が必要となります。
そのため、本記事ではカードコマンドを除いた基本となるパケット構造のみとし、次回の記事で詳細なカードコマンドについて実装していきます。
// 基本となる送受信コマンドパケットデータ
uint8_t sendTxRxBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0xXX, 0xXX, // データ長、チェックサム(それぞれカードコマンドによる)
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0xXX, 0xXX, 0xXX, 0xXX, // データ長(カードコマンドによる)
0x00, 0xXX, // スロット番号、シーケンス番号(コマンド送信ごとに増加)
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x01, // CLA, INS, P1, P2
0xXX, // Lc(カードコマンドによる)
// Data In 1
0x5f, 0x46, // Tag : Timer
0x04, // Length
0x60, 0xea, 0x00, 0x00, // Value : 受信待ちタイムアウト時間(マイクロ秒単位)
// Data In 2
0x95, // Tag : Transceive (Transmit and Receive)
0xXX, // Length(カードコマンドによる)
0xXX, 0xXX, 0xXX, ..., // Value : カードコマンドデータ(カードコマンドによる)
0xXX, // データチェックサム(カードコマンドによる)
0x00 // ポストアンブル
};
ここで、APDUコマンドのコマンドデータ(Data In)に、2つのTLV形式データを設定しています。
このようにコマンドデータ(Data In)には、必要に応じて複数のTLV形式データを設定することができます。
今回は、データ送受信前に、受信待ちタイムアウト時間を設定しています。
受信待ちタイムアウト時間設定のためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 5f46h : Timer |
| Length | 04h |
| Value | タイムアウト時間(マイクロ秒単位、リトルエンディアン) |
また、データ送受信のためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 95h : Transceive (Transmit and Receive) |
| Length | 送信データサイズ |
| Value | 送信データバイト列 |
6. RF 出力 OFF
uint8_t sendRfOffBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0x11, 0xef, // データ長、チェックサム
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0x07, 0x00, 0x00, 0x00, // データ長
0x00, 0x0c, // スロット番号、シーケンス番号
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x00, // CLA, INS, P1, P2
0x02, // Lc
// Data In
0x83, // Tag : Turn Off RF Field
0x00, // Length
0x3c, // データチェックサム
0x00 // ポストアンブル
};
カードコマンドの送受信完了後、RF出力をオフにします。
RF出力オフのためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 83h : Turn Off RF Field |
| Length | 00h |
| Value | なし |
7. Transparent Session 終了
uint8_t sendEndTransparentBuffer[] = {
0x00, 0x00, 0xff, // プリアンブル、スタートコード
0x00, 0x11, 0xef, // データ長、チェックサム
// パケットデータ : CCIDコマンド
0x6b, // メッセージタイプ : PC_to_RDR_Escape
0x07, 0x00, 0x00, 0x00, // データ長
0x00, 0x0d, // スロット番号、シーケンス番号
0x00, 0x00, 0x00, // 固有ヘッダー
// 固有データ : APDUコマンド
0xff, 0xc2, 0x00, 0x00, // CLA, INS, P1, P2
0x02, // Lc
// Data In
0x82, // Tag : End Transparent Session
0x00, // Length
0x3c, // データチェックサム
0x00 // ポストアンブル
};
最後に Transparent Session を終了して通常状態に戻し、NFC通信の一連の処理が完了となります。
Transparent Session 終了のためのコマンドデータは以下となります。
| TLV | 設定データ |
| Tag | 82h : End Transparent Session |
| Length | 00h |
| Value | なし |
おわりに
今回はNFCカードアクセスのための大まかな流れについて実装しました。
次回は本題である、NFCカード読み書きのためのデータ送受信について実装していきます。

