介紹本文是關(guān)于編寫基于Java智能卡的應(yīng)用程序。本教程將幫助初學(xué)者理解Java智能卡和主機(jī)應(yīng)用程序之間的概念和通信。我已經(jīng)看到Java智能卡技術(shù)的初學(xué)者提出了一些簡(jiǎn)單的問題,所以我決定為他們提供一個(gè)完整的例子來幫助他們?nèi)腴T。 在這篇文章/教程中,我將解釋一個(gè)示例應(yīng)用程序,一個(gè)計(jì)算器,它將執(zhí)行四個(gè)基本的計(jì)算操作,即,,+,-,*和/。
背景為了理解本教程,你必須知道J2SE,并且應(yīng)該對(duì)(Java)智能卡有基本的了解。要了解什么是Java卡,請(qǐng)?jiān)L問Oracle官方網(wǎng)站。此外,您可能需要對(duì)以下標(biāo)準(zhǔn)有基本的了解。 -ISO7816-3 -ISO7816-4 -全球平臺(tái)2.1/2.2 假設(shè)我假設(shè)你有智能卡和智能卡閱讀器,并且你能夠加載和安裝本教程/文章提供的.cap文件。工具使用 -網(wǎng)豆。 -Java智能卡 -戴爾鍵盤閱讀器 定義什么是智能卡小程序? 駐留在智能卡上的應(yīng)用程序叫做SmartCardApplet,它是寫在電腦上,然后安裝在智能卡上。它是在電腦上編寫,然后安裝在智能卡上。 什么是主機(jī)應(yīng)用? 它是駐留在計(jì)算機(jī)上或通過APDU與智能卡交互的應(yīng)用程序。這個(gè)應(yīng)用程序可以用任何編程語(yǔ)言編寫。 什么是APDU? APDU是ApplicationProgrammingDataUnit的縮寫,是小程序與主機(jī)應(yīng)用程序之間的通信媒介。它是applet和主機(jī)應(yīng)用程序之間的通信媒介。所有的通信都是通過APDUs在主機(jī)應(yīng)用程序和applet之間完成的。 APDU有兩種類型,一種是命令A(yù)PDU,由主機(jī)應(yīng)用程序向小程序發(fā)送,第二種是響應(yīng)APDU,作為命令A(yù)PDU的響應(yīng)發(fā)送回主機(jī)應(yīng)用程序。 什么是APDU結(jié)構(gòu)? 一個(gè)APDU由以下字段組成。 -CLA:是APDU的類別。 -INS:指示主機(jī)應(yīng)用程序要做什么 -P1和P2:切換參數(shù) -LC:數(shù)據(jù)的長(zhǎng)度 -數(shù)據(jù)。實(shí)際數(shù)據(jù)被發(fā)送到卡上 -LE:從卡上得到的數(shù)據(jù)長(zhǎng)度 上述字段的順序應(yīng)該是。 CLAINSP1P2LCDataLE。 使用代碼Java卡應(yīng)用程序是一種ClientServer應(yīng)用程序,其中智能卡始終保持空閑狀態(tài),并對(duì)主機(jī)應(yīng)用程序向其發(fā)出的命令作出響應(yīng)。主機(jī)應(yīng)用程序向它發(fā)送的命令??偸怯幸粋€(gè)響應(yīng)APDU的命令A(yù)PDU。 在任何智能卡應(yīng)用程序中,我們需要檢測(cè)與計(jì)算機(jī)相連的讀卡器,然后必須與該讀卡器建立連接,并與讀卡器內(nèi)的卡連接。 在計(jì)算器應(yīng)用中,我使用了一個(gè)組合框來顯示所有可用的讀卡器和一個(gè)名為"刷新"的按鈕,當(dāng)點(diǎn)擊該按鈕時(shí),組合框中的終端/讀卡器就會(huì)彈出。然后,你必須選擇一個(gè)終端,并點(diǎn)擊'連接'按鈕來與智能卡連接。 我使用的是JDK1.6以上版本的SmartCardIOAPI,這意味著你不需要下載它,只要導(dǎo)入它就可以使用。在計(jì)算器應(yīng)用中,使用了以下SmartCardIO類。 import javax.smartcardio.Card;import javax.smartcardio.CardChannel;import javax.smartcardio.CardException;import javax.smartcardio.CardTerminal;import javax.smartcardio.CommandAPDU;import javax.smartcardio.ResponseAPDU;import javax.smartcardio.TerminalFactory; 為了開始與卡的通信,我們需要先得到讀卡器/終端。為此,Java提供了一個(gè)類TerminalFactory,這個(gè)類用來獲取所有與計(jì)算機(jī)相連的終端。 public List<CardTerminal> getTerminals() throws Exception{
factory = TerminalFactory.getDefault();
terminals = factory.terminals().list(); return terminals;
}以上函數(shù)返回一個(gè)列表,我們可以在組合框中顯示。 從組合框中選擇一個(gè)終端后,用戶必須點(diǎn)擊連接按鈕。如果有一個(gè)卡存在,并且它的ATR工作正常,那么在框架的右下方將顯示一個(gè)文本Connected,否則將顯示相應(yīng)的錯(cuò)誤信息。
為了與智能卡連接,我們使用CardTerminal類的Connect()函數(shù)。要通過T=0進(jìn)行連接,你需要使用Connect("T=0"),而對(duì)于T=1,你必須使用Connect("T=1")。但是如果你不確定,你可以使用*,SmartCardIO會(huì)自動(dòng)檢測(cè)通信協(xié)議。 以下是執(zhí)行連接操作的方法。 protected void connectToCard(CardTerminal terninalSource) throws CardException {
terminal = terninalSource;
card = terminal.connect("*");
}與卡連接成功后,你需要在輸入文件中輸入數(shù)字,然后按計(jì)算操作按鈕。 在這里我將解釋(+)的操作,其余都是一樣的。 private void add_buttonActionPerformed(java.awt.event.ActionEvent evt) {
String command = "00A404000E63616C63756C61746F722E61707000"; byte[] apdu = JavaSmartcard.hexStringToByteArray(command); if (!selectApplet(apdu))
{ return;
}
byte[] data_LC; try
{
data_LC = getLCData(this.digit1_TextField.getText(), this.digit2_TextField.getText());
} catch (Exception ex)
{
JOptionPane.showMessageDialog(this, "Only digits are allowed to input in the fields\n"+
ex.getMessage(), "Type Error", JOptionPane.ERROR_MESSAGE); return;
}
command = "A000000002";
String LC_Hex = JavaSmartcard.byteArrayToHexString(data_LC);
command = command.concat(LC_Hex);
apdu = JavaSmartcard.hexStringToByteArray(command);
System.out.println(""+ JavaSmartcard.htos(apdu)); try
{
javaCard.sendApdu(apdu); byte[] data = javaCard.getData();
this.status_Label.setText(""+Integer.toHexString(javaCard.getStatusWords()).toUpperCase()); this.result_Label.setText(new BigInteger(data)+"");
}
catch (CardException | IllegalArgumentException ex)
{
JOptionPane.showMessageDialog(this, "Error while tried to send command APDU\n"+
ex.getMessage()+"", "APDU sending fail", JOptionPane.ERROR_MESSAGE);
}
}在上面的功能中,我首先選擇小程序,選擇成功后,我準(zhǔn)備APDU,它將指示小程序做什么,它有什么數(shù)據(jù)。 command = "00A404000E63616C63756C61746F722E61707000"; 上面是選擇小程序的APDU,因?yàn)槲覀冃枰冗x擇我們的計(jì)算器小程序來進(jìn)行計(jì)算,否則默認(rèn)的小程序可能不接受你后續(xù)的命令A(yù)PDU。 byte[] apdu = JavaSmartcard.hexStringToByteArray(command); 在準(zhǔn)備好APDU后,我要把它轉(zhuǎn)換成字節(jié)數(shù)組來傳輸?shù)娇ㄉ?,hexStringToByteArray是一個(gè)實(shí)用的函數(shù),用來把一個(gè)十六進(jìn)制字符串轉(zhuǎn)換成字節(jié)數(shù)組。 command = "A000000002"; 上面的APDUA000000002是APDU,它將告訴小程序,你必須添加給定的兩個(gè)數(shù)字。我將在下面解釋它的字段。 -CLA:AO -INS:00 -P1和P2:00,00 -LC:02 數(shù)據(jù)部分的計(jì)算方法如下。 private byte[] getLCData(String byte1Str, String byte2Str) throws Exception
{ byte[] data_LC = new byte[2]; byte byte1 = Byte.parseByte(byte1Str ); byte byte2 = Byte.parseByte(byte2Str);
data_LC[0] = byte1;
data_LC[1] = byte2;
return data_LC;
}我正在將兩個(gè)TextFields的輸入轉(zhuǎn)換為字節(jié),然后將這些字節(jié)復(fù)制到一個(gè)字節(jié)數(shù)組中傳送到卡上。 最終的APDU將如下所示。讓用戶輸入5和5并輸入。 A0 00 00 00 02 05 05 當(dāng)智能卡收到APDU時(shí),它將對(duì)其進(jìn)行解釋,并找到INS字段,以了解Host應(yīng)用程序想要做什么,然后它將從數(shù)據(jù)部分獲取數(shù)據(jù)(數(shù)字),并將其添加并以響應(yīng)APDU的形式返回給Host應(yīng)用程序。 當(dāng)收到響應(yīng)APDU時(shí),我們可以確定STATUSWORD來了解計(jì)算過程中發(fā)生了什么。如果卡返回的STATUSWORD為0x9000,則表示一切正常,否則可能會(huì)出現(xiàn)錯(cuò)誤,或者執(zhí)行進(jìn)一步的操作以獲得實(shí)際的響應(yīng)APDU。 public int getStatusWords() { return rAPDU.getSW();
}上面的函數(shù)是用來獲取卡片返回的STATUSWROD,通過使用下面的函數(shù),我得到的是Data部分。 public byte[] getData() { if (rAPDU!=null) { return rAPDU.getData();
} else { return null;
}
}
|
|
|