2021最新整理Java教程:Java 智能卡迷你计算器
介绍
本文是关于编写基于Java智能卡的应用程序。本教程将帮助初学者理解Java智能卡和主机应用程序之间的概念和通信。我已经看到Java智能卡技术的初学者提出了一些简单的问题,所以我决定为他们提供一个完整的例子来帮助他们入门。
在这篇文章/教程中,我将解释一个示例应用程序,一个计算器,它将执行四个基本的计算操作,即,,+,-,*和/。
背景
为了理解本教程,你必须知道J2SE,并且应该对(Java)智能卡有基本的了解。要了解什么是Java卡,请访问Oracle官方网站。此外,您可能需要对以下标准有基本的了解。
-ISO7816-3
-ISO7816-4
-全球平台2.1/2.2
假设
我假设你有智能卡和智能卡阅读器,并且你能够加载和安装本教程/文章提供的.cap文件。工具使用
-网豆。
-Java智能卡
-戴尔键盘阅读器
定义
什么是智能卡小程序?
驻留在智能卡上的应用程序叫做SmartCardApplet,它是写在电脑上,然后安装在智能卡上。它是在电脑上编写,然后安装在智能卡上。
什么是主机应用?
它是驻留在计算机上或通过APDU与智能卡交互的应用程序。这个应用程序可以用任何编程语言编写。
什么是APDU?
APDU是ApplicationProgrammingDataUnit的缩写,是小程序与主机应用程序之间的通信媒介。它是applet和主机应用程序之间的通信媒介。所有的通信都是通过APDUs在主机应用程序和applet之间完成的。
APDU有两种类型,一种是命令APDU,由主机应用程序向小程序发送,第二种是响应APDU,作为命令APDU的响应发送回主机应用程序。
什么是APDU结构?
一个APDU由以下字段组成。
-CLA:是APDU的类别。
-INS:指示主机应用程序要做什么
-P1和P2:切换参数
-LC:数据的长度
-数据。实际数据被发送到卡上
-LE:从卡上得到的数据长度
上述字段的顺序应该是。
CLAINSP1P2LCDataLE。
使用代码
Java卡应用程序是一种ClientServer应用程序,其中智能卡始终保持空闲状态,并对主机应用程序向其发出的命令作出响应。主机应用程序向它发送的命令。总是有一个响应APDU的命令APDU。
在任何智能卡应用程序中,我们需要检测与计算机相连的读卡器,然后必须与该读卡器建立连接,并与读卡器内的卡连接。
在计算器应用中,我使用了一个组合框来显示所有可用的读卡器和一个名为"刷新"的按钮,当点击该按钮时,组合框中的终端/读卡器就会弹出。然后,你必须选择一个终端,并点击'连接'按钮来与智能卡连接。
我使用的是JDK1.6以上版本的SmartCardIOAPI,这意味着你不需要下载它,只要导入它就可以使用。在计算器应用中,使用了以下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提供了一个类TerminalFactory,这个类用来获取所有与计算机相连的终端。
public List<CardTerminal> getTerminals() throws Exception{ factory = TerminalFactory.getDefault(); terminals = factory.terminals().list(); return terminals; }
以上函数返回一个列表,我们可以在组合框中显示。
从组合框中选择一个终端后,用户必须点击连接按钮。如果有一个卡存在,并且它的ATR工作正常,那么在框架的右下方将显示一个文本Connected,否则将显示相应的错误信息。
为了与智能卡连接,我们使用CardTerminal类的Connect()函数。要通过T=0进行连接,你需要使用Connect("T=0"),而对于T=1,你必须使用Connect("T=1")。但是如果你不确定,你可以使用*,SmartCardIO会自动检测通信协议。
以下是执行连接操作的方法。
protected void connectToCard(CardTerminal terninalSource) throws CardException { terminal = terninalSource; card = terminal.connect("*"); }
与卡连接成功后,你需要在输入文件中输入数字,然后按计算操作按钮。
在这里我将解释(+)的操作,其余都是一样的。
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); } }
在上面的功能中,我首先选择小程序,选择成功后,我准备APDU,它将指示小程序做什么,它有什么数据。
command = "00A404000E63616C63756C61746F722E61707000";
上面是选择小程序的APDU,因为我们需要先选择我们的计算器小程序来进行计算,否则默认的小程序可能不接受你后续的命令APDU。
byte[] apdu = JavaSmartcard.hexStringToByteArray(command);
在准备好APDU后,我要把它转换成字节数组来传输到卡上,hexStringToByteArray是一个实用的函数,用来把一个十六进制字符串转换成字节数组。
command = "A000000002";
上面的APDUA000000002是APDU,它将告诉小程序,你必须添加给定的两个数字。我将在下面解释它的字段。
-CLA:AO
-INS:00
-P1和P2:00,00
-LC:02
数据部分的计算方法如下。
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; }
我正在将两个TextFields的输入转换为字节,然后将这些字节复制到一个字节数组中传送到卡上。
最终的APDU将如下所示。让用户输入5和5并输入。
A0 00 00 00 02 05 05
当智能卡收到APDU时,它将对其进行解释,并找到INS字段,以了解Host应用程序想要做什么,然后它将从数据部分获取数据(数字),并将其添加并以响应APDU的形式返回给Host应用程序。
当收到响应APDU时,我们可以确定STATUSWORD来了解计算过程中发生了什么。如果卡返回的STATUSWORD为0x9000,则表示一切正常,否则可能会出现错误,或者执行进一步的操作以获得实际的响应APDU。
public int getStatusWords() { return rAPDU.getSW(); }
上面的函数是用来获取卡片返回的STATUSWROD,通过使用下面的函数,我得到的是Data部分。
public byte[] getData() { if (rAPDU!=null) { return rAPDU.getData(); } else { return null; } }