本文是關(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è)你有智能卡和智能卡閱讀器,并且你能夠加載和安裝本教程/文章提供的.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; } }
聯(lián)系客服