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;
    }
}
(0)

相关推荐