Java卡

JavaCard应用程序开发三部曲 - 小应用程序

2020-12-05 10:56:42 明申科技 80

1、简介

当创建一个 Java Card 应用程序的时候的典型的步骤是:

1、编写 Java 源代码。

2、编译你的源代码。

3、把类文件改变为一个 Converted Applet(CAP)文件。

4、检验这个 CAP 是否有效;这个步骤是可选的。

5、安装这个 CAP 文件。

当用 Java 程序设计语言开发传统的程序的时候,头两个步骤是相同的:编写.java 文件并且把它们编译成.class 文件。可是,一旦你已经创建 Java Card 类文件,过程会变化的。

Java Card 虚拟机(JCVM)被分成卡外虚拟机和卡内虚拟机。这个分解移除了昂贵的卡外操作,并且考虑到了在卡本身上的小的内存空间,但是它导致在开发 Java Card 应用程序的时候的额外步骤。

在Java Card 类可以被导入一个Java Card 设备之前,他们必须被转化成标准的CAP 文件格式,然后选择性地检验:

  • 转化必然伴有把每个 Java 程序包变换到一个 CAP 文件中,在一个程序包中包含类和接口的 联合二进制表示法。转化是一个卡外操作。

  • 验证是一个可选择的过程,来确认 CAP 文件的结构、有效的字节码子集和程序包内依赖性。你可能想在你使用的第三方供应商程序包上进行验证,或者如果你的转换工具来自一个第三方供应 商。验证一般来说是一个卡外操作,但是一些卡片产品可能包括一个机载的检验器。

一旦检验,CAP 文件就即将安装在 Java Card 设备上了。

2、Sun JavaCard Development 工具箱

Sun JavaCard Development 工具箱

你可以使用 Sun JavaCard 开发工具箱编写 JavaCard 小应用程序,并且甚至可以不使用一个智能卡或者读卡器来测试它们。这个工具箱包括所有你开发和测试所需要的 Java Card 小应用程序的基本工具:

1、Java Card Workstation Development Environment ( JCWDE),一个便利的易于使用的JavaCard 模拟工具,允许开发者直接执行类文件,而不要转化和安装 CAP 文件。JCWDE 可以和调试程序和 IDE 整合。

从这个开发工具箱的 2.2.1 版本开始,JCWDE 支持 Java Card RMI ( JCRMI)。注意 JCWDE不是一个成熟的 Java Card 模拟器。它不支持许多 JCRE 特性,例如包安装、小应用程序实例创建、防火墙和事务。请参阅这个开发工具箱的用户指南获取更多信息。

2、C 语言 Java Card 运行时环境(C-JCRE),一个使用 C 语言编写的可执行参考实现。C-JCRE是一个 Java Card 应用程序编程接口、虚拟机和运行时环境完全兼容的实现。它能让一个开发者在一个工作站环境中精确地测试小应用程序的行为。

C-JCRE 有一些限制:它在一个卡片会话期间支持多达八个可以返回的引用,多达 16 个可以同时被导出的远程对象,8 个远程方法中的数组类型参数,32 个支持的 Java 程序包和 16 个 Java Card小应用程序。想要获得这些限制条件,请参阅 Java Card 开发工具箱用户指南。

3、 JavaCard 转化工具,用于生成 CAP 文件。

4、 JavaCard 检验,用于选择性地核对 CAP 和导出文件的有效性。

5、 一个发送和接收应用程序协议数据单元(Application Protocol Data Units,APDUs)的 APDU工具(apdutool)。这样你就可以在 Java Card 小应用程序测试期间发送 APDU。你可以 apdutool读取的脚本文件,发送 APDUs 到 C-JCRE 或者 JCWDE 中。

6、 一个 capdump 工具,用于转出 CAP 的内容,和一个打印 EXP 文件的 exp2text。

7、 一个 scriptgen 工具,转换 CAP 文件为 APDU 脚本文件。这个工具还被认为是卡外安装程序。

8、 支持库(用于 Java Card 应用编程接口的类文件和导出文件)文档和范例。

当 Sun JavaCard 开发工具箱允许你编写和测试 Java Card 小应用程序的时候,部署一个现实的端对端的智能卡应用程序需要开发工具箱中没有包含的工具,例如利用了终端应用程序编程接口,如 OpenCard 和 Global Platform 应用程序编程接口。它可能还需要利用例如 Subscriber Identification Module (用户识别模块,SIM)工具包这样的工具来帮助你管理 SIM。

图 1 显示了这个工具包的目录结构(Windows 版本),以及包含开发工具的 bin 目录的内容。


明申智能卡电子标签,IC卡厂家定制明申智能卡电子标签,IC卡厂家定制
Figure 1a. Development Kit Directory StructureFigure 1b. Contents of bin directory

现在让我们在看一次 Java Card 开发步骤,这次使用 Sun Java Card Development 工具箱:

1.使用你喜爱的编辑器或者 IDE 编写 Java 源程序。

2.使用你喜爱的编译程序或者 IDE 编译 Java 源程序。

3.选择性地,使用 JCWDE 模拟器测试你的 Java Card 小应用程序。重申一下,JCWDE 不是一个成熟的 Java Card 模拟器。

4.使用工具包的 bin 目录下的转换程序把类文件转化成一个 Converted Applet (转化过的小应用程序,CAP)文件。注意,除类文件之外,另一个输入到这个转换工具中的文件是导出文件,提供了关于你的应用程序导入的(引用)的程序包的信息。这些是还被装载到卡片中的程序包。导出文件还是转换工具的一个输出。

5.选择性地,检验 CAP 的有效性。这一步包括使用 verifycap 脚本来验证 CAP 文件的有效性,使用 verifyexp 来验证导出文件,并且使用 verifyrev 来检验程序包修正之间的二进制兼容性。工具verifycap、verifyexp 和 verifyrev 脚本全部都可在 bin 目录中得到。

6.安装 CAP 文件。使用 scriptgen 工具转换 CAP 文件为一个(安装)APDU 脚本文件。然后使用 apdutool 发送脚本文件(安装 APDU 命令和 CAP 文件)到 Java Card 设备上的 C-JCRE 或者一个 JCRE。JCRE 保存 CAP 文件到卡片的内存中。

下面的图总结了这些步骤。注意每个 Java Card 供应商提供它自己的工具,但是这些用于开发一个 Java Card 小应用程序的步骤在开发工具箱之间通常是相同的:

明申智能卡电子标签,IC卡厂家定制

Figure 2. Java Card Development Steps(click for larger image)

3、小应用程序结构

Sun 提供了两个模型用来设计 JavaCard 应用程序(javacard.framework.Applet):传统的 JavaCardAPI 和 JavaCard Remote Method Invocation(Java Card 远程方法调用,JCRMI)编程接口。我们可以使用其中任何一个来编写 Java Card 小应用程序,开发 Java Card 小应用程序是一个两步的过程:

1.定义负责主应用程序和小应用程序之间接口的命令和响应 APDU。

2.编写 Java Card 小应用程序本身

JavaCard 小应用程序结构

首先,让我们看一下 Java Card 小应用程序的结构。

列表 1 说明了一个典型的 JavaCard 小应用程序是如何构造的:

import javacard.framework.*

...

public class MyApplet extends Applet {

//Definitions of APDU-related instruction codes

...

MyApplet() {...} // Constructor

//Life-cycle methods

install() {...}

select() {...}

deselect() {...}

process() {...}

// Private methods

...

}

列表⒈一个 JavaCard 小应用程序的结构

一个 JavaCard 小应用程序通常定义它的 APDU 相关指令、它的构造器,然后是 Java Card 小应用程序的生命周期方法:install ()、select ()、deselect ()和 process ()。最后,它定义任何合适的私有方法。


4、定义 APDU 指令

定义 APDU 指令

不同的 Java Card 应用程序有不同的接口(APDU)需求。一个信用卡小应用程序可能支持验证 PIN 号码的方法,产生信用和借记事务,并且核对帐目余额。一个健康保险小应用程序可能提供访问健康保险信息、保险总额限制、医生、病人信息等等信息的权限。你定义的精确的 APDU 全依赖你的应用程序需求。

举例来说,让我们亲身感受一下如何开发经典的 Wallet 信用卡示例。你可以在 Sun Java CardDevelopment 工具箱的 samples 目录下得到这个及其他示例的完整的代码。

我们将开始定义一个 APDU 命令来查询保存在 Java Card 设备上的当前余额数。注意,在一个实际信用卡应用程序中,我们还将定义信用并且借记命令。我们将分配我们的 Get Balance APDU一个 0x80 指令类和一个 0x30 指令。Get Balance APDU 不需要任何指令参数或者数据区,并且预期的响应由包含余额的两个字节组成。下一个表格描述 Get Balance APDU 命令:

表 1 - Get Balance APDU 命令
NameCLAINSP1P2LcData FieldLe (size of response)
Get Balance0x800x3000N/AN/A2

虽然 Get Balance 命令未定义输入数据,但是有一些命令 APDU 将定义输入数据。举例来说,让我们定义验证从卡片读取器中传递来的 PIN 号码的 Verify PIN APDU 命令。下一个表格定义 Verify APDU:

表格 2- Verify APDU 命令
NameCLAINSP1P2LcData FieldLe (size of response)
Verify PIN0x800x2000PIN LenPIN ValueN/A

注意 Le 字段,响应的大小是 N/A。这是因为没有到 Verify PIN 的应用程序特定响应;成功或者失败通过响应 APDU 中的状态字标明。

为了简化 APDU 过程,javacard.framework.ISO7816 接口定义了许多常数,我们可以用来从process ()方法传送到小应用程序中的输入缓冲器中检索各个的 APDU 字段:

...

byte cla = buf[ISO7816.OFFSET_CLA];

byte ins = buf[ISO7816.OFFSET_INS];

byte p1 = buf[ISO7816.OFFSET_P1];

byte p2 = buf[ISO7816.OFFSET_P2];

byte lc = buf[ISO7816.OFFSET_LC];

...

//Get APDU data, by copying lc bytes from OFFSET_CDATA, into reusable buffer databuf.

Util.arrayCopy(buf, ISO7816.OFFSET_CDATA, databuf, 0, lc);

...

列表 2、使用 ISO-7816-4 常数

现在我们将定义用于 Get Balance 和 Verify 命令的类(CLA)和指令(INS),Get Balance 响应的大小,以及在如果 PIN 验证失败后的出错返回代码。

...

// MyApplet APDU definitions

final static byte MyAPPLET_CLA = (byte)0x80;

final static byte VERIFY_INS = (byte)0x20;

final static byte GET_BALANCE_INS = (byte) 0x30;

final static short GET_BALANCE_RESPONSE_SZ = 2;

// Exception (return code) if PIN verify fails.

final static short SW_PINVERIFY_FAILED = (short)0x6900;

...

列表 3、小应用程序的 APDU 定义

接下来,让我们定义小应用程序构造器和生命循环方法。

5、构造器

构造器

定义一个初始化这个对象的状态的私有构造器。这个构造器被从 install()方法调用;换句话说,构造器只在小应用程序的生命周期期间被调用:

/**

* Private Constructor.

*/

private MyApplet() {

super();

//... Allocate all objects needed during the applet's lifetime.

ownerPin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);

...

// Register this applet instance with the JCRE.

register();

}

列表 4、小应用程序构造器

在这个示例中,我们使用一个 javacard.framework.OwnerPIN,一个描述个人识别号码的对象;这个对象将存在于 Java Card 小应用程序的一生。回忆一下本文第一部分中的"管理内存和对象",在一个 Java Card 环境中,数组和基本类型将在对象声明中被声明,而且你应该最小化对象实例,以利于对象重用。在小应用程序生命周期期间,以创建对象一次。做到这点的一个简易的方法是在构造器中创建对象,并且从 install()方法中调用这个构造器--install()本身在小应用程序生命周期中只被调用一次。为了利于再使用,对象应该保持在范围中或者适当的引用中,用于小应用程序的生命周期,并且它们的成员的值在再使用之前适当的重置。因为一个垃圾收集程序并不总是可用,一个应用程序可能从不回收被分配给对象的存储空间。

install()方法

JCRE 在安装过程期间调用 install()。你必须覆盖这个从 javacard.framework.Applet 类继承来的方法,并且你的 install ()方法必须实例化这个小应用程序,如下:

/**

*Installs the Applet. Creates an instance of MyApplet. The JCRE calls this static method during applet installation.

*@param bArray install parameter array.

*@param bOffset where install data begins.

*@param bLength install parameter data length.

*@throw ISOException if the install method fails.

*/

public static void install(byte[] bArray, short bOffset, byte bLength)

throws ISOException {

// Instantiate MyApplet

new MyApplet();

...

}

列表 5、install ()小应用程序生命周期方法

install ()方法必须直接或者间接地调用 register ()方法来完成安装;如果这步失败将导致安装失败。在我们的范例中,构造器调用 register()。

select()方法

JCRE 调用 select()来通知已经被选作 APDU 过程的小应用程序。你不必实现这个方法,除非你想提供会话初始化或者个性化。select()方法必须返回 true 来指明它即将处理进入的 APDU,或者返回 false 来拒绝选择。javacard.framework.Applet 类的默认实现返回 true。

/**

*Called by the JCRE to inform this applet that it has been selected. 

*Perform any initialization that may be required toprocess APDU commands. 

*This method returns a boolean to indicate whether it is ready to accept incoming APDU commands

*via its process() method.

*@return If this method returns false, it indicates to the JCRE

*that this Applet declines to be selected.

*/

public boolean select() {

// Perform any applet-specific session initialization. return true;

}

列表 6、select()小应用程序生命周期方法

deselect()方法

JCRE 调用 deselect()来通知小应用程序,它已经被取消选定了。你不必实现这个方法,除非你想提供会话清除。javacard.framework.Applet 类的默认实现什么都不做。

/**

*Called by the JCRE to inform this currently selected applet

*it is being deselected on this logical channel. Performs the session cleanup.

*/

public void deselect() {

// Perform appropriate cleanup. ownerPin.reset();

}

列表 7、deselect()小应用程序生命周期方法

在我们的示例中,我们重置了 PIN(个人识别号码)。

process()方法--感受 APDU 的全过程

一旦一个小应用程序已经被选择,它将准备接收命令 APDUs,如在本文第一部分中"Java Card小应用程序的生命周期"描写的。

回想一下被从主机端(客户端)应用程序发送到卡片的 APDU 命令,如下面的说明:

明申智能卡电子标签,IC卡厂家定制

Figure 3. APDU 指令和响应流程

每次 JCRE 接收一个 APDU 命令(通过卡片读取器从主应用程序,或者如果使用 Sun Java Card Development 工具箱就通过 apdutool),它调用小应用程序的 process()方法,把输入命令当作一个参数传送给它(APDU 命令输入缓冲中的参数)。process()方法然后:

1.摘录 APDU CLA 和 INS 字段

2.检索应用程序特定的 P1、P2 和数据字段

3.处理 APDU 数据

4.生成并发送一个响应

5.优雅地返回,或者抛出相应的 ISO 异常

在此时,JCRE 发送合适的状态字回到主应用程序,通过读卡器

列表 8 显示一个样本 process()方法。

/**

*Called by the JCRE to process an incoming APDU command. An applet is expected to

* perform the action requested and return response data if any to the terminal.

*

*Upon normal return from this method the JCRE sends the ISO-7816-4-defined success 

*status (90 00) in the APDU response. If this method throws an ISOException the JCRE 

* sends the associated reason code as the response status instead.

*@param apdu is the incoming APDU.

*@throw ISOException if the process method fails.

*/

public void process(APDU apdu) throws ISOException {

//Get the incoming APDU buffer.

byte[] buffer = apdu.getBuffer();

//Get the CLA; mask out the logical-channel info.

buffer[ISO7816.OFFSET_CLA] = (byte)(buffer[ISO7816.OFFSET_CLA] &(byte)0xFC);

//If INS is Select, return - no need to process select here.

if ((buffer[ISO7816.OFFSET_CLA] == 0) && (buffer[ISO7816.OFFSET_INS] == (byte)(0xA4)) )

return;

// If unrecognized class, return "unsupported class."

if (buffer[ISO7816.OFFSET_CLA] != MyAPPLET_CLA)

ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);

//Process (application-specific) APDU commands aimed at MyApplet.

switch (buffer[ISO7816.OFFSET_INS]) {

case VERIFY_INS: 

verify(apdu); 

break;

case GET_BALANCE_INS:getBalance(apdu);

break;

default:ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 

break;

}

}

列表 8、process()小应用程序生命周期方法

我们的 process()方法调用 getBalance()和 verify()方法。列表 9 显示 getBalance ()方法,处理 get balance APDU 并且返回保存在卡片中的余额。

/**

*Retrieves and returns the balance stored in this card.

*@param apdu is the incoming APDU.

*/

private void getBalance(APDU apdu) {

//Get the incoming APDU buffer. 

byte[] buffer = apdu.getBuffer();

//Set the data transfer direction to outbound and obtain

//the expected length of response (Le).

short le = apdu.setOutgoing();

//If the expected size is incorrect, send a wrong-length status word.

if (le != GET_BALANCE_RESPONSE_SZ)

ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);

//Set the actual number of bytes in the response data field.

apdu.setOutgoingLength((byte)GET_BALANCE_RESPONSE_SZ);

//Set the response data field; split the balance into 2

//separate bytes.

buffer[0] = (byte)(balance >> 8); 

buffer[1] = (byte)(balance & 0xFF);

//Send the 2-byte balance starting at the offset in the APDU

//buffer.

apdu.sendBytes((short)0, (short)GET_BALANCE_RESPONSE_SZ);

}

列表 9、处理 Get Balance APDU

getBalance()方法通过调用APDU.getBuffer()方法取得一个引用到APDU 缓冲。在返回响应(当前余额)之前,小应用程序必须设置 JCRE 模式通过调用 APDU.setOutgoing()方法来发送,方便地返回期望的响应大小。我们还必须设置响应数据字段中的字节的实际数字,通过调用APDU.setOutgoingLenth()。APDU 缓冲中的响应事实上通过调用 APDU.sendBytes ()发送。

小应用程序不直接发送返回码(状态字);一旦小应用程序调用 APDU.setOutgoing()并且提供任何请求的信息,JCRE 注意这个状态字。状态字的值依靠 process()方法如何使返回到 JCRE 来变化。如果所有的已经正常运行,JCRE 将返回 9000,指明无错。你的小应用程序可以通过抛出一个定义在 ISO7816 接口中的异常返回一个错误代码。 在列表 9 中,如果期望响应的大小不正确,方法 getBalance()抛出一个 ISO7816.SW_WRONG_LENGTH 代码。对于有效的状态码值,请参阅ISO7816 接口的定义,或者回到本文第一部分的"响应 APDU"。

现在让我们看看在列表 10 中的 verify()方法。因为我们定义的验证 PIN APDU 命令包含数据,verify()方法必须调用APDU.setIncomingAndReceive ()方法,设置 JCRE 为接收模式,然后接收输入数据。

/**

*Validates (verifies) the Owner's PIN number.

*@param apdu is the incoming APDU.

*/

private void verify(APDU apdu) {

// Get the incoming APDU buffer. 

byte[] buffer = apdu.getBuffer();

// Get the PIN data.

byte bytesRead = (byte)apdu.setIncomingAndReceive();

//Check/verify the PIN number. Read bytesRead number of PIN

//bytes into the APDU buffer at the offset ISO7816.OFFSET_CDATA.

if (ownerPin.check(buffer, ISO7816.OFFSET_CDATA, byteRead) == false )

ISOException.throwIt(SW_PINVERIFY_FAILED);

}

列表 10、处理验证 APDU

这 个 方 法 通 过 调 用 APDU.getBuffer() 取 得 一 个 到 APDU  缓 冲 的 引 用 , 调 用APDU.setIncomingAndReceive()来接收命令数据,从输入的 APDU 缓冲中取得 PIN 数据,并且验证 PIN。一个验证失败导致状态码 6900 被发送回主应用程序。

有时输入的数据比填充到 APDU 缓冲中的数据要多,并且小应用程序必须大块的读取数据知道没有数据可以读取。在此情况下,我们必须首先调用 APDU.setIncomingAndReceive(),然后重复地调用 APDU.receiveBytes(),直到不再有数据可用。列表 11 显示如何读取大量输入数据。

...

byte[] buffer = apdu.getBuffer();

short bytes_left = (short) buffer[ISO.OFFSET_LC];

short readCount = apdu.setIncomingAndReceive(); 

while (bytes_left > 0) {

//Process received data in buffer; copy chunk to temp buf. 

Util.arrayCopy(buffer, ISO.OFFSET_CDATA, tbuf, 0, readCount);

bytes_left -= readCount;

//Get more data

readCount = apdu.receiveBytes(ISO.OFFSET_CDDATA);

}

...

列表 11、读取大量输入数据

由于每个大块被读取,小应用程序可以把它添加到另一个缓冲中,否则仅仅处理它。

6、JavaCard RMI 模型介绍

Java Card 小应用程序编程的第二个模型是 JavaCard RMI ( JCRMI),严格的说它是 J2SE RMI分布对象模型的缩小版。

这是一个以对象为中心的模型,根据这个模型你在上一节看到的 APDU 通信和句柄将被抽象化;取而代之的是你处理对象。这简化了编程和集成基于 Java Card 技术的设备。

在 RMI 模型中,一个服务器应用程序创建并生成可访问的远程对象,并且一个客户应用程序获得到服务器的远程对象的远程引用,然后调用它们的远程方法。在 JCRMI 中,Java Card 小应用程序是服务器,而主应用程序是客户端。

JavaCard RMI 简介

两个程序包提供了 JavaCard RMI 支持:

1、java.rmi 定义了 Java 2 标准版 java.rmi 程序包的一个子集。它定义了 Remote 接口和RemoteException 类。除此之外,没有包含其他传统的 java.rmi 类。

2、Javacard.framework.service 定义了 Java Card 小应用程序服务类,包括 RMI 服务类CardRemoteObject 和 RMIService。

3、Class CardRemoteObject 定义了两个方法启动和禁止卡片外的对象的远程访问。类RMIService 处理 RMI 请求(转化输入的命令 APDU 为远程方法调用)。

编写一个 JCRMI 应用程序类似于编写一个典型的基于 RMI 的应用程序:

1.定义远程类的行为为一个接口。

2.编写远程类的服务器实现,和支持类。

3.编写一个使用远程服务的客户机程序和支持类。

注意 JCRMI 没有改变小应用程序的基本结构或者生命周期,你不久就会看到。

7、远程接口

远程接口

创建一个远程服务中的第一步是定义它的可见行为。远程接口定义你的小应用程序提供的服务。和在标准的 J2SE RMI 中一样,所有的 Java Card RMI 远程接口必须扩展 java.rmi.Remote 接口。为了说明,这里有一个远程接口,揭示一个取得保存在卡片里的余额的方法:

import java.rmi.*;

import javacard.framework.*;

public interface MyRemoteInterface extends Remote {

...

public short getBalance() throws RemoteException;

...

//A complete credit card application would also define other

//methods such as credit() and debit() methods.

...

}

列表 1、远程接口

MyRemoteInterface 定义这个远程方法,在我们的示例中一个 getBalance ()方法,来取得保存在智能卡中的余额。注意,除了 Java Card 特定的导入之外,这个远程接口看起来完全象一个标准的 RMI 远程接口。

服务器实现

下一步是实现服务器的行为。服务器实现包含 Java Card 小应用程序,在所有的你已经定义的远程接口当中的实现,和任何与你的应用程序相关的特定类。

8、小应用程序

Java Card 小应用程序

Java Card 小应用程序是 JCRMI 服务器,并且是主机(客户端)应用程序可用的远程对象的所有者。一个典型的 Java Card RMI 小应用程序的结构在下面的图表中说明:

明申智能卡电子标签,IC卡厂家定制

Figure 4. 典型的 JavaCard RMI 应用程序结构

当和明确地处理 APDU 消息的小应用程序相比,基于 JCRMI 的小应用程序更像一个对象容器。如图 4 所示,基于 JCRMI 的小应用程序有一个或多个远程对象,一个 APDU Dispatcher 和一个接收 APDU 并且把它们转化为远程方法调用的 RMIService。Java Card 远程类可以扩展CardRemoteObject 类,自动导出对象,使之可用于远程使用。

JCRMI 小应用程序必须扩展 javacard.framework.Applet,遵循标准的小应用程序结构,并且定义适当的生命周期方法。它必须安装并且登记本身,并且分配 APDU。下面这一代码片断说明一个基于 JCRMI 的小应用程序的典型结构:

public class MyApplet extends javacard.framework.Applet {

private Dispatcher disp;

private RemoteService serv;

private Remote myRemoteInterface;

/**

*Construct the applet. Here instantiate the remote implementation(s), the APDU Dispatcher, 

*and the RMIService. Before returning, register the applet.

*/

public MyApplet () {

// Create the implementation for my applet. 

myRemoteInterface = new MyRemoteInterfaceImpl();

// Create a new Dispatcher that can hold a maximum of 1 service, the RMIService.

disp = new Dispatcher((short)1);

// Create the RMIService

serv = new RMIService(myRemoteInterface); 

disp.addService(serv, Dispatcher.PROCESS_COMMAND); 

// Complete the registration process

register();

}

...

小应用程序创建一个 Dispatcher 和一个处理输入的 JCRMI APDU 的 RMIService。

...

/**

* Installs the Applet. Creates an instance of MyApplet.

*The JCRE calls this static method during applet installation.

*@param bArray install parameter array.

*@param bOffset where install data begins.

*@param bLength install parameter data length.

*/

public static void install(byte[] aid, short s, byte b) { 

new MyApplet();

}

在JavaCard 环境中,JVM 的生命周期是物理卡片的生命周期,而不是提供垃圾收集器的所有 的 Java Card 实现的,所以你需要最小化内存分配。在安装时间创建对象,这样内存只被分配给它 们一次。

/**

*Called by the JCRE to process an incoming APDU command. An applet is expected to 

*perform the action requested and return response data, if any.

*This JCRMI version of the applet dispatches remote invocation APDUs by invoking the Dispatcher.

*

*Upon normal return from this method the JCRE sends the ISO-7816-4-defined success status (90 00) in the APDU response.

*If this method throws an ISOException, the JCRE sends the associated reason code as the response status instead.

*@param apdu is the incoming APDU.

*@throw ISOException if the install method fails.

*/

public void process(APDU apdu) throws ISOException {

// Dispatch the incoming command APDU to the RMIService. 

disp.process(apdu);

}

代码列表 13.Java Card RMI 小应用程序

小应用程序的 process()方法接收一个 APDU 命令并且把它发送到 RMIService,RMIService 通过把它转化为一个 RMI 调用和后续响应处理这条命令

9、实现远程对象

实现远程对象

实现一个 JCRMI 远程对象类似于实现标准的 J2SE RMI 远程对象。主要区别是在 JCRMI 中你的远程对象有扩展 CardRemoteObject 的选择(除了实现你的远程接口之外)。

CardRemoteObject 定义两个方法 export()和 unexport(),分别允许或者禁止从卡外到对象的访问。通过扩展 CardRemoteObject,你自动地导出你的远程对象所有的方法。如果你决定不扩展CardRemoteObject,你将要负责通过调用 CardRemoteObject.export()导出它们。

import java.rmi.RemoteException;

import javacard.framework.service.CardRemoteObject;

import javacard.framework.Util;

import javacard.framework.UserException; /**

* Provides the implementation for MyRemoteInterface. */

public class MyRemoteImpl extends CardRemoteObject implements MyRemoteInterface { 

/** The balance. */

private short balance = 0;

/**

*The Constructor invokes the superclass constructor, which exports this remote implementation.

*/

public MyRemoteImpl() {

super(); // make this remote object visible

}

/**

*This method returns the balance.

*@return the stored balance.

*@throws RemoteException if a JCRMI exception is encountered

*/

public short getBalance() throws RemoteException { 

return balance;

}

// Other methods

...

}

列表⒕远程对象实现

10、完整的 Java Card RMI 应用程序流程

一个完整的 Java Card RMI 应用程序的流程

让我们概述一个 JCRMI 应用程序的流程。客户端(主机)应用程序通过传递 RMI APDU 到卡上的 JCRE 来产生 RMI 调用,依次转送这些 APDU 到相应的 JCRMI 小应用程序。这个小应用程序分配接收的 APDU 到 RMIService,依次处理 APDU 并且转化它为一个 RMI 调用。一个 JCRMI 小应用程序的典型流程在下面说明:

明申智能卡电子标签,IC卡厂家定制

Figure 5. 基于 JavaCard RMI 模型的应用程序流程

简言之,JCRMI 提供一个基于 APDU 的消息传递模型的分布式对象模型机制。JCRMI 消息被封装到传送到 RMIService 的 APDU 消息中,负责解码 APDU 命令,并且转化这些命名到方法调用和响应。这允许服务器和客户端通信,来回传送方法信息、参数和返回值。

首页
产品
新闻
联系