Android HCE 調査メモ（その３）


# 目的
C#アプリでもHCEと通信できることを確認します。


# 環境
今回は以下の機種で動作確認しました。
#### Android側
- ビルド環境
 - Android Studio 3.5.3

- 端末
  - Sony SO-3L
  - Android 9.0 (API level 28)

#### PC側
- NFCリーダ
 - ACR1251CL-NTTCom
   (PC/SCモードで使用)
- OS
  - Windows 10 64bit
- ビルド環境
 - Visual Studio 2019
- 言語
  - C#


# Android側アプリ
  
  過去の記事を参照ください。
  [Android HCE 調査メモ（その１）](https://qiita.com/tkhr68000/items/66de411d5eb007fea3ca)
    AIDは「F222222222」となっています。

# PC側アプリ
とりあえず、.NET Core コンソールアプリで作成することにします。
（GUI等は後々…）

## プロジェクト開始
Visual Studioで新規プロジェクトを作成しておきます。

## NuGetパッケージ追加
UWPには標準でライブラリがあるようですが、.NET Core 用の標準ライブラリは無いようです。
そのため、NuGetパッケージの追加を行います。
`ツール → NuGetパッケージマネージャー → ソリューションのNuGetパッケージの管理`
「PCSC」「PCSC.Iso7816」を追加します。

## ソースの作成
エラーチェック等行っていない、あまりキレイではないソースですが、とりあえず動作するソースを以下に書きます。


``` C#:Program.cs
using System;

namespace HCE_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var model = new HceModel();
            model.mainProcess();
            Console.ReadKey();
        }
    }
}
```

``` C#:HceModel.cs
using PCSC;
using PCSC.Iso7816;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HCE_Test
{
    class HceModel
    {
        internal ISCardContext SCardContext { get; private set; }
        internal string readerName { get; private set; }

        internal void mainProcess()
        {
            // 初期化
            Initialize();

            // NFCRW接続
            if (!ConnectNFCRW())
            {
                return;
            }
            // GET UID
            getUid();
            // SELECT AID
            var aid = new byte[] { 0xF2, 0x22, 0x22, 0x22, 0x22 };
            selectAID(aid);
        }

        internal void Initialize()
        {
            var contextFactory = ContextFactory.Instance;
            this.SCardContext = contextFactory.Establish(SCardScope.System);
        }

        internal bool ConnectNFCRW()
        {
            string txt = string.Empty;
            bool result = false;
            // Console.WriteLine("カードを置いてください。");
            // Console.ReadKey();
            try
            {
                // 最初に見つけたNFCRWに接続する.
                var readerNames = SCardContext.GetReaders();
                if (IsEmpty(readerNames))
                {
                    return false;
                }
                readerName = readerNames[0];
                using (var rfidReader = SCardContext.ConnectReader(readerName, SCardShareMode.Shared, SCardProtocol.Any))
                {
                    var status = rfidReader.GetStatus();
                    txt += String.Format("Reader {0}\r\n connected with protocol {1}\r\n in state {2}",
                        status.GetReaderNames().FirstOrDefault(),
                        status.Protocol,
                        status.State);
                    var atr = status.GetAtr();
                    if (atr != null || atr.Length > 0)
                    {
                        txt += String.Format("\r\n Card ATR: {0}", BitConverter.ToString(atr));
                    }
                }
                result = true;

            }
            catch (Exception ex)
            {
                txt = ex.Message;
                result = false;
            }
            Console.WriteLine(txt);
            return result;
        }
        internal void getUid()
        {
            string txt = string.Empty;
            try
            {
                using (var rfidReader = SCardContext.ConnectReader(readerName, SCardShareMode.Shared, SCardProtocol.Any))
                {

                    var apdu = new CommandApdu(IsoCase.Case2Short, rfidReader.Protocol)
                    {
                        CLA = 0xFF,
                        Instruction = InstructionCode.GetData,
                        P1 = 0x00,
                        P2 = 0x00,
                        Le = 0 // We don't know the ID tag size
                    };
                    using (rfidReader.Transaction(SCardReaderDisposition.Leave))
                    {
                        // Retrieving the UID .... 

                        var sendPci = SCardPCI.GetPci(rfidReader.Protocol);
                        var receivePci = new SCardPCI(); // IO returned protocol control information.

                        var receiveBuffer = new byte[256];
                        var command = apdu.ToArray();

                        int bytesReceived = rfidReader.Transmit(
                            sendPci, // Protocol Control Information (T0, T1 or Raw)
                            command, // command APDU
                            command.Length,
                            receivePci, // returning Protocol Control Information
                            receiveBuffer,
                            receiveBuffer.Length); // data buffer

                        var responseApdu =
                            new ResponseApdu(receiveBuffer, bytesReceived, IsoCase.Case2Short, rfidReader.Protocol);
                        if ((responseApdu.SW1 == 0x90) && (responseApdu.SW2 == 0x00))
                        {
                            txt = "UID : ";
                            txt += responseApdu.HasData ? BitConverter.ToString(responseApdu.GetData()) : "No uid received";
                        }
                        else
                        {
                            txt = string.Format("SW1: {0:X2}, SW2: {1:X2}\r\n00",
                                responseApdu.SW1,
                                responseApdu.SW2);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                txt = ex.Message;
            }
            Console.WriteLine(txt);
        }

        internal void selectAID(byte[] aid)
        {
            string txt = string.Empty;
            try
            {
                using (var rfidReader = SCardContext.ConnectReader(readerName, SCardShareMode.Shared, SCardProtocol.Any))
                {
                    var apdu = new CommandApdu(IsoCase.Case3Short, rfidReader.Protocol)
                    {
                        CLA = 0x00,
                        Instruction = InstructionCode.SelectFile,
                        P1 = 0x04,
                        P2 = 0x00,
                        Data = aid //AID
                    };
                    var command = apdu.ToArray();
                    txt += string.Format("APDU: {0}\r\n", BitConverter.ToString(command));
                    using (rfidReader.Transaction(SCardReaderDisposition.Leave))
                    {
                        // Retrieving the AID .... 

                        var sendPci = SCardPCI.GetPci(rfidReader.Protocol);
                        var receivePci = new SCardPCI(); // IO returned protocol control information.

                        var receiveBuffer = new byte[256];
                        int bytesReceived = rfidReader.Transmit(
                            sendPci, // Protocol Control Information (T0, T1 or Raw)
                            command, // command APDU
                            command.Length,
                            receivePci, // returning Protocol Control Information
                            receiveBuffer,
                            receiveBuffer.Length); // data buffer

                        var responseApdu =
                            new ResponseApdu(receiveBuffer, bytesReceived, IsoCase.Case2Short, rfidReader.Protocol);
                        txt += string.Format("SW1: {0:X2}, SW2: {1:X2}\r\n",
                            responseApdu.SW1,
                            responseApdu.SW2);
                        if ((responseApdu.SW1 == 0x90) && (responseApdu.SW2 == 0x00))
                        {
                            txt += "received:";
                            txt += responseApdu.HasData ? BitConverter.ToString(responseApdu.GetData()) : "No data received";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                txt = ex.Message;
            }
            Console.WriteLine(txt);
        }

        private static bool IsEmpty(ICollection<string> readerNames) => readerNames == null || readerNames.Count < 1;
    }
}
```

## 実行結果
リーダーにHCEアプリを起動したAndroidを乗せ、アプリを実行してみます。

```
Reader ACS ACR1251 CL Reader PICC 0
 connected with protocol T1
 in state Specific
 Card ATR: 3B-88-80-01-00-00-00-00-80-81-71-00-79
UID : B8-65-F7-C4
APDU: 00-A4-04-00-05-F2-22-22-22-22
SW1: 90, SW2: 00
received:30-30-30-30-30-30-30-30
```

無事受信できました。