健保卡內到底存了什麼
在健保卡上所嵌的IC晶片內規劃有「個人基本資料」、「健保資料」、「醫療專區」及「衛生行政專區」等四種不同類別資料存放區段,各區段預定存放之內容說明如下:
基本資料
|
健保內容
|
醫療專區
|
衛生行政區
|
卡片號碼
|
保險人代碼
|
門診處方箋
|
預防接種資料
|
姓名
|
保險對像身份註記
|
長期處方箋
|
同意器官捐贈註記
|
身份證字號
|
卡片有效期限
|
重要處方項目
|
同意安寧緩和醫療註記
|
出生年月日
|
重大傷病註記
|
過敏藥物
|
|
性別
|
就醫可用次數
|
|
|
發卡日期
|
最近一次就醫序號
|
|
|
照片
|
新生兒依附註記
|
|
|
卡片註銷註記
|
就醫類別
|
|
|
|
新生兒就醫註記
|
|
|
|
就診日期時間
|
|
|
|
補卡註記
|
|
|
|
就醫序號
|
|
|
|
保險醫事服務機構代碼
|
|
|
|
主、次診斷碼
|
|
|
|
就醫醫療費用紀錄
|
|
|
|
就醫累計資料
|
|
|
|
醫療費用總累計
|
|
|
|
個人保險費
|
|
|
|
保健服務紀錄
|
|
|
|
緊急聯絡電話
|
|
|
|
孕婦產前檢查
|
|
|
|
其他就醫需要之註記
|
|
|
資料來源
http://www.nhi.gov.tw/webdata/webdata.aspx?menu=23&menu_id=817&WD_ID=196&webdata_id=918
在上述表格中,橘色部份是可以用一般晶片讀卡機讀出來的部份。其他的部份必須由健保專用讀卡機以搭配醫事人員卡以及醫療專用網路認證讀卡機及醫事人員卡後才可讀取。
今天要寫的是用一般市售的晶片讀卡機把基本資料讀取出來的方法。
目前常見用途為診間報到。
晶片讀卡機的部份,windows大多都可以支援,但Mac上網友推的是EZ-100PU ,或是選購符合CCID規格的晶片讀卡機。
CCID(USB Chip/Smart Card Interface Devices-USB芯片智能卡接口設備)標準是由幾大國際級IT企業共同製定的一個標準,它提供了一種智能卡讀寫設備與主機或其它嵌入式主機實現相互通訊的可能。
資料來源 http://baike.baidu.com/view/2808369.htm
背景知識
IC卡內的積體電路可包含微處理器(MCU)與記憶體,
只有記憶體的稱為記憶卡(Memory Card),只能儲存資料,而
具微處理器則擁有運算與資料處理能力,被稱為智慧卡(Smart Card)。
接觸式 IC卡的標準是 ISO 7816(規定了規格/電氣特性/通訊協議/部件等各方面);
非接觸式 IC卡的標準是 ISO 14443。
由Microsoft、Gemplus等業者提出了 PC/SC(Personal Computer/Smart Card)的驅動程式規範,遵循此規範才能讓智慧卡(Smart Card)透過讀卡機(Card Reader)跟 PC相連運作。
PC/SC規模支援 ISO 7816-4的基本指令集(APDU指令),界定了 IC卡、讀卡機及作業系統的責任與分工,各家讀卡機廠商只要遵循 PC/SC所定義之介面與方法開發驅動程式,應用程式只要透過單一標準介面與作業系統溝通,就可輕易操控各種讀卡機讀寫 IC卡。
PC/SC實做在 Microsoft Windows 200x/XP及以上版本,但 Microsoft Windows NT/9x也可以執行,而 Open-source實作的成品叫作 PC/SC Lite,支援 Linux及 Mac OS X。
資料來源
http://blog.xuite.net/sugopili/computerblog/31011868-%E8%AE%80%E5%8F%96+IC%E5%8D%A1(%E6%99%BA%E6%85%A7%E5%8D%A1)
APDU
APDU Command Structure
APDU Command Details
Type
|
Name
|
Length
|
Details
|
CLA
|
Class
|
1 Byte
|
Class of the command
(e.g:if a command uses secure messageing or not)
|
INS
|
Instruction
|
1 Byte
|
Command instruction
|
P1
|
Parameter 1
|
1 Byte
|
First parameter of the instruction
|
P2
|
Parameter 2
|
1 Byte
|
Second parameter of the instruction
|
Lc
|
Length Command
|
0-3 Byte
|
Length of the command data
|
Data
|
Data
|
Lc Byte
|
Command data(apdu request)
|
Le
|
Length Expeted
|
0-3 Byte
|
Length of the response data (apdu response)
|
APDU Respone Structure
APDU Response Details
Type
|
Name
|
Length
|
Details
|
Data
|
Body
|
0-3 Bytes
|
Data of the response (Le) Can be Null
|
SW1
|
Status Word 1
|
1 Byte
|
Status Word 1
|
SW2
|
Status Word 2
|
1 Byte
|
Status Word 2
|
至於要怎麼下APDU Command ,請自行GOOGLE ISO 7816-4 . 或參考這篇使用的方式,小弟在此只概略說明程式碼中會用到的ADPU Command以及Response。網路上常見己的有讀取健保卡、自然人憑證、金融卡等範例程式。
以下是程式中的APDU Command 以及欄位資料
CLA
|
INS
|
P1
|
P2
|
Lc
|
Data
|
Le
|
00
|
CA
|
11
|
00
|
02
|
00 00
|
|
Type
|
Value
|
Value 意思
|
CLA
|
00
|
No SM or no SM (Secure messaging) indication
|
INS
|
CA
|
GET DATA
|
P1
|
11
|
只可以輸入 11 或 00 ,當輸入00 時只會讀出卡號 (自行嘗試)
|
P2
|
00
|
若P1 為00 時只能輸入 00 ,若P1 為11 則任意 0x00~0x15 (自行嘗試)
|
Lc
|
02
|
Length of the command data ,如果輸入 03 則 Data 為 00 00 00 以此類推,輸入08 則Data 為 00 00 00 00 00 00 00 00
|
Data
|
00 00
|
Command data(apdu request)
|
Le
|
|
Length of the response data (apdu response)
|
至於回傳的純資料欄位共有57個Byte(Debug mode 得知 ,SW1 及SW2不包含在此)
程式碼如下,由網路上爬到的,配合自己的理解,修改了一點點 。
package readhealthcardbasicdata;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import javax.smartcardio.Card;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardChannel;
public class ReadData {
//Define APDU
public static byte[] SelectAPDU = new byte[]{(byte) 0x00,
(byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x10,
(byte) 0xD1, (byte) 0x58, (byte) 0x00, (byte) 0x00,
(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x11, (byte) 0x00};
public static byte[] ReadProfileAPDU =
new byte[]{(byte) 0x00, (byte) 0xca, (byte) 0x11,
(byte) 0x00,(byte) 0x02, (byte) 0x00, (byte) 0x00};
public static void main(String args[]) {
TerminalFactory terminalFactory
= TerminalFactory.getDefault();
try {
//Get CardTermial List
for (CardTerminal terminal :
terminalFactory.terminals().list()) {
try {
//連線讀卡機 * 表示不限制協定,
//這裡也可以輸入T=1,T=0 (健保卡T=1)
Card card = terminal.connect("T=1");
CardChannel channel =
card.getBasicChannel();
//APDU Command
CommandAPDU command =
new CommandAPDU(SelectAPDU);
//APDU Response
ResponseAPDU response =
channel.transmit(command);
//APDU Command
command =
new CommandAPDU(ReadProfileAPDU);
//APDU Response
response = channel.transmit(command);
byte[] a = response.getData();
//Display Data
System.out.println("卡號:"
+ new String(Arrays.copyOfRange
(response.getData(), 0, 12)));// 卡號
System.out.println("姓名:"
+ new String(Arrays.copyOfRange
(response.getData(),12, 32),
"Big5").trim());// 姓名
System.out.println("身份證字號:"
+ new String(Arrays.copyOfRange
(response.getData(),32, 42))); // 身分證號
System.out.println("出生年月日:"
+ new String(Arrays.copyOfRange
(response.getData(),42, 49))); // 出生年月日
System.out.println("性別:"
+ new String(Arrays.copyOfRange
(response.getData(),49, 50))); // 性別
System.out.println("發卡年月日:"
+ new String(Arrays.copyOfRange
(response.getData(),50, 57))); // 發卡年月日
}
catch (javax.smartcardio.CardNotPresentException e)
{
continue;
} catch (CardException e) {
continue;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
} catch (NumberFormatException e2) {
e2.printStackTrace();
} catch (CardException e3) {
e3.printStackTrace();
}
}
}
本篇文章,小弟對APDU了解尚淺,若有不足的地方,請不吝指教。