健保卡讀取程式 (console app sample)


Posted by kennyshu on 2024-09-17

最近有讀取健保卡的需求故參考了網路上的一些範例寫了個console測試一下:

第一版
BackgroundService持續偵測讀卡機是否存在,成功則接著嘗試讀取是否有健保卡存在

public class CardReader : BackgroundService
{
    private readonly ILogger<CardReader> _logger;
    public CardReader(ILogger<CardReader> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // 循環偵測/讀取讀卡機
            await ReadICCardAsync();
            await Task.Delay(1000, stoppingToken);
        }
    }

    private async Task ReadICCardAsync()
    {
        using (var readerCtx = PCSC.ContextFactory.Instance.Establish(PCSC.SCardScope.System))
        {
            // 找到讀卡機
            if (readerCtx.GetReaders().Any())
            {
                var readerName = readerCtx.GetReaders().FirstOrDefault();
                Console.WriteLine($"找到讀卡機:[{readerName}]");

                try
                {
                    using (var reader = new PCSC.Iso7816.IsoReader(
                       context: readerCtx,
                       readerName: readerName,
                       mode: PCSC.SCardShareMode.Shared,
                       protocol: PCSC.SCardProtocol.Any))
                    {
                        var apduInit = new PCSC.Iso7816.CommandApdu(PCSC.Iso7816.IsoCase.Case4Short, reader.ActiveProtocol)
                        {
                            CLA = 0x00,
                            INS = 0xA4,
                            P1 = 0x04,
                            P2 = 0x00,
                            Data = new byte[]
                                { 0xD1, 0x58, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11 }
                        };

                        var apduInitResponse = reader.Transmit(apduInit);
                        if (!(apduInitResponse.SW1.Equals(144) && apduInitResponse.SW2.Equals(0)))
                        {
                            Console.WriteLine("晶片卡非健保卡");
                            return;
                        }

                        var apduProfile = new PCSC.Iso7816.CommandApdu(PCSC.Iso7816.IsoCase.Case4Short, reader.ActiveProtocol)
                        {
                            CLA = 0x00,
                            INS = 0xCA,
                            P1 = 0x11,
                            P2 = 0x00,
                            Data = new byte[] { 0x00, 0x00 }
                        };
                        var apduProfileResponse = reader.Transmit(apduProfile);
                        // 有插卡才有資料
                        if (apduProfileResponse.HasData)
                        {
                            byte[] aryData = apduProfileResponse.GetData();
                            //文字編碼解碼器
                            var oUTF8 = Encoding.UTF8;
                            var oBIG5 = Encoding.GetEncoding("big5");

                            var patientName = oBIG5.GetString(aryData.Skip(12).Take(20).ToArray()).Trim().TrimEnd('\0');
                            var patientId = oUTF8.GetString(aryData.Skip(32).Take(10).ToArray()).Trim();
                            var patientBirth = oUTF8.GetString(aryData.Skip(42).Take(7).ToArray()).Trim();
                            var patientGender = oUTF8.GetString(aryData.Skip(49).Take(1).ToArray()).Trim();

                            Console.WriteLine($"讀取健保卡:[{patientName}]");
                            return;
                        }
                    }
                }
                catch (PCSC.Exceptions.RemovedCardException)
                {
                    // 有時候沒插卡會噴這個exception,可略過
                    return;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"讀卡機名稱:{readerName}");
                    return;
                }
            }
        }
    }
}

之後會有第二版改用事件(event)的方式改寫,有偵測到插入卡片的行為才觸發讀卡的動作
PS:ReadICCardAsync()的async原本有對應await的更新資料的地方,不過這邊我拔掉了

Reference:


#C# #健保卡







Related Posts

linkedin Java 檢定題庫 static

linkedin Java 檢定題庫 static

Day 1 - MongoDB 基礎概念教學

Day 1 - MongoDB 基礎概念教學

Airflow 動手玩:(六)Airflow with Kuberentes Executor

Airflow 動手玩:(六)Airflow with Kuberentes Executor


Comments