分享
 
 
 

Java Card 技术(三)

王朝学院·作者佚名  2009-11-10
窄屏简体版  字體: |||超大  

Java Card 应用程序的元素

请记住,Java Card 应用程序并不是独立的,而是端到端应用程序的一部分:

图 1. Java Card 应用程序的典型组件

Java Card 应用程序通常由以下部分组成:

提供后台服务(例如保存在数据库中的安全或者电子支付信息)访问的 后台应用程序。如何开发后台应用程序超出了本文的范围。

主机应用程序 位于卡片外部的卡片终端,它可以使用许多接口(如 Java Card RMI、OpenCard Framework API 或安全性和信任服务应用编程接口 [SATSA])访问智能卡上的 applet。

卡片读取器、卡片终端 或者 卡片接入设备 ,提供了主机应用程序和卡片内部 applet 之间的物理接口。

卡片内部的物理接口是 Java Card applet 和 Java Card 框架。请注意,在访问 applet 之前,主机应用程序必须提供证书并进行自我身份验证。

编写主机应用程序 —— 访问 Applet

位于客户端的主机应用程序处理用户、Java Card applet 和提供器的后端应用程序之间的通信。主机程序访问由 applet 所提供的服务。它存储在终端或卡片接入设备上,例如工作站、销售终端点(POS)、手机或者机顶盒。回想一下,主机和 applet 使用 ISO-7816 APDU 命令通过卡片读取器或终端进行交互。

通常,读取器端应用程序使用 C语言编写,但是主机程序用Java 编程语言或其他语言编写,只要该语言准备了与applet交换的有效ISO-7816 APDU 命令就没有问题。

目前,大多数经过部署的手机集成了智能卡读取器,以便访问与该读取器捆绑的 SIM 卡片。使用即将引入的 JSR 177, 用于 J2ME 的安全性和信任服务应用编程接口(SATSA)、J2ME 设备的广泛采用,我们能够预计各种主机应用程序将使用移动设备上的Java 技术编写。SATSA 的意图就是启用 Java Card 主机应用程序来运行基于 J2ME的设备。目前,JSR 177 处于 JCP 团体审查阶段。

当编写客户端应用程序时,有三个主要的API可以使用:OpenCard Framework、Java Card RMI Client API 和用于 J2ME 的安全性和信任服务应用编程接口(SATSA)。我们将逐一研究每一个应用程序编程接口。

OpenCard Framework 简介

智能卡供应商通常不仅提供开发工具箱,还提供 API 以便支持读取器端应用程序以及 Java Card applet。供应商提供的许多产品都支持 OpenCard Framework (OCF)和基于 Java的 API集合,该集合隐藏了与不同供应商提供的卡片读取器交互的一些细节。

OpenCard 联盟 是一群推动定义和采用 OpenCard Framework(当前版本为1.2)的公司。OCF 的目标是向主机端应用程序的开发者提供跨不同的卡片读取器供应商工作的 API。

为了实现厂商无关性,OCF 定义了两个软件层:

CardTerminal 层提供了卡片读取器抽象,例如 CardTerminal (表示物理卡片读取器) APDU、CommandADPU和 ResponseAPDU。

OCF 为您定义了许多标准卡片服务。其中两个是 FileAccessCardService 和 SignatureCardService。一个特殊的类型是 ApplicationManagerCardService,提供生命周期管理方法以便安装、注册和删除卡片内部的 applet。

当编写主机端基于 OCF应用程序时,您基本上要将该应用程序分为两个部分:

主要的应用程序对象,它与终端或读取器进行交互(初始化 OCF、等待卡片插入、中断 OCF),并公开高级卡片访问方法,例如 getBalance()。

Applet 代理,它实现实际的低级通道管理和 APDU I/O。当把来自应用程序的 APDU 细节隐藏起来的时候,该代理设计模式允许公开面向对象的接口。

图 2. OCF应用程序的结构

总而言之,OCF 应用程序有一个或多个 main 对象,它们在主机上或者在执行的线程上创建。这些 main 应用程序对象公开特定于应用程序的高级调用,最终将它们委托给 applet 代理。这些对象利用OCF应用程序入口点的 SmartCard 对象,启用应用程序初始化并中断 OCF,并等待卡片被插入。main 对象可以实现即将看到的 CTListener,这个侦听器提供卡片插入和拔出等类似事件的异步通知。

您可以使用同步或异步模型编写应用程序。

在同步模型中,主机应用程序初始化 OCF,然后等待卡片被插入。然后它执行主机应用程序逻辑,当操作完成时,中断 OCF:

...

try {

// Initialize OCF

SmartCard.start();

// Wait for a smart card

CardRequest cr = new CardRequest(CardRequest.NEWCARD, null,

OCFCardAccessor.class);

SmartCard myCard = SmartCard.waitForCard(cr);

// Main client work is done here...

...

} catch (Exception e){

// Handle exception

} finally {

try {

// Shut down OCF

SmartCard.shutdown();

} catch (Exception e) {

e.printStackTrace();

}

}

...

清单 1. 同步 OCF 应用程序的典型结构

如果您喜欢使用异步方法,您选定的类必须实现CTListener 接口,且在初始化阶段,进行自我注册来通知类似插入和拔出这样的卡片终端事件通知。下列应用程序主干通过初始化OCF 并注册侦听器开始,然后为重要事件定义回调方法。

public class MyHostSideApp implements CTListener

...

public MyHostSideApp() {

try {

// Initialize the framework

SmartCard.start ();

// Register this as a Card Terminal Event Listener

CardTerminalRegistry.getRegistry().addCTListener(this);

} catch (Exception e) {

// handle error...

}

}

public void cardInserted(CardTerminalEvent ctEvent) {

...

}

public void cardRemoved(CardTerminalEvent ctEvent) {

...

}

...

}

清单 3. 基于侦听器的放大 OCF 应用程序。

接下来是一个来自applet 代理的引用。OCF 应用程序将服务调用委托给实现(复杂) APDU管理片断的 applet 代理:

public class MyCardProxy extends AppletProxy {

// My 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;

protected final static int OK = 0x9000;

final static short SW_PINVERIFY_FAILED = (short)0x6900;

/**

* Reusable command APDU for getting an information

* entry field.

*/

private CommandAPDU getBalanceAPDU = new CommandAPDU(14);

...

/** Application identifier of the BusinessCard applet */

private static final ApplicationID MY_CARD_AID =

new ApplicationID(new byte[] { (byte)0xD4,

(byte)0x55,

(byte)0x00,

(byte)0x00,

(byte)0x22,

(byte)0x00,

(byte)0x00,

(byte)0x00,

(byte)0xFF});

/**

* Create a MyCardProxy instance.

*

* @param scheduler The Scheduler from which channels

* have to be obtained.

* @param card The SmartCard object to which this

* service belongs.

* @param blocking Currently not used.

*

* @throws opencard.core.service.CardServiceException

* Thrown when instantiation fails.

*/

protected void initialize(CardServiceScheduler scheduler,

SmartCard card, boolean blocking)

throws CardServiceException {

super.initialize(MY_CARD_AID, scheduler, card, blocking);

try {

// Allocate the card channel. This gives us

// exclusive access to the card until we release the

// channel.

allocateCardChannel();

// Get the Card State.

...

} finally {

releaseCardChannel();

}

}

/**

* Gets the balance.

* @return The balance.

*/

public String getBalance()

throws CardServiceInvalidCredentialException,

CardServiceOperationFailedException,

CardServiceInvalidParameterException,

CardServiceUnexpectedResponseException,

CardServiceException,

CardTerminalException {

try {

allocateCardChannel();

// Set up the command APDU and send it to the card.

getBalanceAPDU.setLength(0);

getBalanceAPDU.append(MyAPPLET_CLA); // Class

getBalanceAPDU.append(GET_BALANCE_INS); // Instr'n

getBalanceAPDU.append((byte) 0x00); // P1

getBalanceAPDU.append((byte) 0x00); // P2

getBalanceAPDU.append((byte) 0x00); // Lc

getBalanceAPDU.append((byte) 0x00); // Le

// Send command APDU and check the response.

ResponseAPDU response =

sendCommandAPDU(getCardChannel(), MY_CARD_AID,

getBalanceAPDU);

switch (response.sw() & 0xFFFF) {

case OK :

return new String(response.data());

default :

throw new

CardServiceUnexpectedResponseException

("RC=" + response.sw());

}

} finally {

releaseCardChannel();

}

}

...

}

清单 4.Applet 代理示例

在 OpenCard Framework 1.2 编程人员指南中您会找到关于 OCF使用的更多信息。还可以参考 OpenCard Framework 参考实现中的示例文件。

本文中未涉及,但是值得一提的是主机端 API,称作 Global Platform。Global Platform Card Committee 提供了补充 Java Card API的卡片 API 集合,除其他内容以外,还提供了卡片管理功能。这些规范都在2.1.1版本中。

Java Card RMI 客户机 API

前面你已经学到了如何编写一个基于 JCRMI 的 applet。如果您的 Java Card applet 基于 JCRMI,您可以使用 Java Card RMI Client API编写一个主机应用程序,访问智能卡中保存的 apple 对象。

对于智能卡管理和访问,JCRMI Client API 需要一个卡片终端和诸如刚刚描述的 OpenCard Framework 这样的服务 API。

当我们把这两个 API 放在一起时,我们得到一个非常简单、非常完整的面向对象编程模型,有以下几个优点:

不必知道智能卡和卡片读取器的细节

不必知道低级的 APDU 通信

便于设计和维护的代码,从而缩短开发时间

JCRMI Client API 在下面的软件包中定义:

com.sun.javacard.javax.smartcard.rmiclient 包含核心 JCRMI Client API。它定义了以下内容:

JCRMI 桩模块用来访问智能卡的 CardAccessor 接口

CardObjectFactory 类,用于 JCRMI 桩模块生成实现的基类。这个类的实例与一个 Java Card applet 选择会话有关。

客户机应用程序使用的 JavaCardRMIConnect 类,用于初始化一个 JCRMI 会话并且获得初始的远程引用。

许多 Java Card 的异常子类,例如 APDUExceptionSubclass、CardExceptionSubclass、CardRuntimeExceptionSubclass、CryptoExceptionSubclass、ISOExceptionSubclass、PINExceptionSubclass、PINException、ServiceExceptionSubclass、SystemExceptionSubclass、TransactionExceptionSubclass 和 UserExceptionSubclass。

javacard.framework 定义了许多客户机上的许多可以被再次抛出的 Java Card 异常:APDUException、CardException、CardRuntimeException、ISOException、PINException、SystemException、TransactionException 和 UserException。

javacard.framework.service 定义了 ServiceException,表示与服务框架有关的异常。

javacard.security 定义了 CryptoException,用来表示一个有关加密的异常。

生成 RMI 客户机桩模块

您选择使用标准的 Java RMI 编译程序(rmic)生成客户机桩模块。您必须使用下面格式的命令运行 rmic,用于你的 applet 中的每个远程类:

rmic -v1.2 -classpath path -d output_dir class_name

......其中:

-v1.2 是一个 Java Card RMI 客户机框架所需要的标记。

-classpath path 确定到远程类的路径。

output_dir 是存放结果桩模块的目录

class_name 是远程类的名称。

然而,推荐生成 RMI 客户机桩模块的方法使用 J2SE SDK 1.3 中的 动态代理 生成机制。当您选择 JCRMI applet 的时候,如果使用 CardObjectFactory 子类型 JCCardProxyFactory 的话,JavaCard RMI Client API 的 2.2 版本将为你自动生成桩模块,你不必再生成任何桩模块!这个方法在清单 5 中加以说明。

用法限制

因为 Java Card 是一个有限制的运行时环境,我们可以发现关于 JCRMI 实际支持的限制。Java Card 不支持序列化和 JCRMI 参数,并且返回值也有限制:

每个到远程方法的参数必须是 Java Card 支持的类型之一,不包括 char, double, float, long 或者多维数组。对 int 的支持为可选的。

任何远程方法的返回值必须是受支持的类型之一,或者 void,或者一个远程接口类型。

JCRMI 客户机应用程序

因为 JCRMI Client API 依靠 OCF 用于卡片管理和通信,所以 JCRMI 客户机应用程序类似您前面看到的 OCF 主机应用程序。

下面的代码片断首先初始化 OCF,并且等待智能卡被插入。然后它创建一个 OCFCardAccessor 实现,用于把我们的 JCRMI 连接到卡片上,如有必要,客户机桩模块将动态生成,applet 被选中,我们取得了远程引用,最后我们实现到 getBalance() 的远程调用:

...

try {

// Initialize OCF

SmartCard.start();

// Wait for a smart card

CardRequest cr = new CardRequest(CardRequest.NEWCARD, null,

OCFCardAccessor.class);

SmartCard myCard = SmartCard.waitForCard(cr);

// Get an OCFCardAccessor for Java Card RMI

CardAccessor ca = (CardAccessor)

myCard.getCardService(OCFCardAccessor.class, true);

// Create a Java Card RMI instance

JavaCardRMIConnect jcRMI = new JavaCardRMIConnect(ca);

// Create a Java Card Proxy Factory that is used for dynamic

// proxy generation.

CardObjectFactory factory = new JCCardProxyFactory(ca);

// select the Java Card applet

jcRMI.selectApplet(MY_APPLET_AID, factory);

// Get the initial reference

MyRemoteInterface myRemoteInterface =

(MyRemoteInterface) jcRMI.getInitialReference();

if(myRemoteInterface == null) {

throw new

Exception("Received null instead of the initial ref");

}

// Invoke the remote getBalance() method

short balance = myRemoteInterface.getBalance();

}

catch(UserException e) {

// Handle exception

...

}

catch (Exception e){

// Handle exception

...

} finally {

// Clean up

try{

SmartCard.shutdown();

}catch (Exception e){

System.out.println(e);

}

}

清单 5. 示例 JCRMI 客户机

如您所见,您必须要做的是减少代码并大幅度简化代码。

用于 J2ME 的安全性和信任服务应用编程接口

SATSA 是一套用于 J2ME 的新的可选软件包,定义一个客户端 API 来访问 安全元素:例如智能卡这样的设备。在本节中,我将仅仅介绍 SATSA 的通信部分;本文中将不介绍 SATSA PKI 和加密 API。

SATSA 通信 API 被分解成下面几部分:

SATSA-APDU 定义一个 API,用于和遵循 ISO-7816-4 的智能卡进行通信。这个可选软件包由单独的 javax.microedition.io.APDUConnection 软件包组成。

SATSA-JCRMI 定义了 Java Card RMI 客户机 API。这个可选软件包由下面的 Java 软件包组成:

javax.microedition.io.JavaCardRMIConnection

javax.microedition.jcrmi.RemoteRef

javax.microedition.jcrmi.RemoteStub

java.rmi.Remote

java.rmi.RemoteException

javacard.framework.service.ServiceException

javacard.framework.CardRuntimeException

javacard.framework.ISOException

javacard.framework.APDUException

javacard.framework.CardException

javacard.framework.PINException

javacard.framework.SystemException

javacard.framework.TransactionException

javacard.framework.UserException

javacard.security.CryptoException

SATSA 将 J2ME 和 Java Card 平台紧密地结合在一起。SATSA 使用 CLDC 1.0 Generic Connection Framework(GCF)用于基于 J2ME 的设备和智能卡之间的通信,如下所示:

图 3. 通用连接框架和 SATSA 连接

因为 SATSA 基于 GCF,开发使用手机上的智能卡的 MIDlet 相对不容易,但是对于 J2ME 开发人员来说很熟悉。

SATSA 考虑到用于 Java Card 应用程序的两个总体编程模型:APDU-消息传递模型和 Java Card RMI 面向对象分布式模型。SATSA 为了每一个模型定义了一个新的 GCF 连接类型:

APDUConnection 允许一个 J2ME 应用程序使用 ISO-7816 APDU 协议以便与智能卡应用程序交换 APDU。

JavaCardRMIConnection 允许一个 J2ME 应用程序使用 Java Card RMI 来调用智能卡上的远程方法。

指定 SATSA 的连接类型

所有的 GCF 连接都使用 Connector.open() 方法创建。Connector.open() 的一个参数是指示要创建的连接类型的 URL。CLDC GCF 使用下面的格式定义这个 URL 为一个字符串:

scheme:[target][params]

......其中:

scheme 是要创建的连接类型(和要使用的协议)。

target 一般是某种网络地址。

params 是可选参数,表达形式是名称=值,通过分号隔开。

对于 SATSA,URL 的格式是:

protocol:[slotID]; AID

......其中:

protocol 要么是 apdu,用于基于 APDU 的连接,要么是 jcrmi,用于基于 JCRMI 的连接。

slotID 是指示卡片插入的插槽。slotID 字段是可选的;默认值为 0。

AID 是用于智能卡应用程序的应用程序标识符。AID 是一个由句号分隔开的 5 到 16 个十六进制字节值的字符串;例如,“ A0.0.0.67.4.7.1F.3.2C.3”。

使用 APDUConnection

APDUConnection 定义允许我们使用 GCF 与遵循 ISO-7816 的卡片进行通信的各种方法。它定义了三个方法是:

enterPIN() 提示用户输入一个个人识别号码。

exchangeAPDU() 与智能卡应用程序交换 APDU。这个调用直到一个响应从智能卡返回的时候(或者处理被中断)才会阻断。

getATR() 返回智能卡发送的 Answer To Reset(ATR) 消息,作为重置操作的响应。

下面的代码片断显示如何打开一个 APDUConnection,如何关闭它,以及如何交换一个命令 APDU 并且接收一个 APDU 响应:

...

try {

// Create an APDUConnection

String url = "apdu:0;AID=A1.0.0.67.4.7.1F.3.2C.5";

APDUConnection ac = (APDUConnection) Connector.open(url);

// Send a command APDU and receive a response APDU

byte[] responseAPDU = ac.exchangeAPDU(commandAPDU);

...

// Close connection.

ac.close();

} catch(IOException e){

...

}

...

清单6. 使用 SATSA-APDU

SATSA 使 APDU 通信简单化。请注意,这个 APDU 命令和响应的格式和您在本系列文章的第 2 部分的“编写客户端 JavaCard Applet”一节中看到的相同,告诉您如何编写一个基于 APDU 消息传递的 Java Card Applet。

使用 JavaCardRMIConnection

JavaCardRMIConnection 定义允许我们使用 Java Card RMI 程序设计模型的方法。JavaCardRMIConnection 定义了方法 getInitialReference(),为初始远程引用返回桩模块对象。

...

try {

// Create a JavaCardRMIConnection

String url = "jcrmi:0;AID=A0.0.0.67.4.7.1F.3.2C.3";

JavaCardRMIConnection jc = (JavaCardRMIConnection)

Connector.open(url);

MyRemoteObject robj = (MyRemoteObject)

jc.getInitialReference();

...

short balance = robj.getBalance();

...

// Close connection

jc.close();

} catch (Exception e) {

...

}

...

清单7. 使用 SATSA-JCRMI

采用上面所说的 SATSA-JCRMI 应用编程接口,允许我们调用本系列文章的第 2 部分中所定义的 getBalance() 方法,向你说明如何编写一个基于 RMI 的 JavaCard applet。

清单 2. 异步 OCF 应用程序的典型结构

当卡片被插入时,运行时调用 cardInserted() 方法,当卡片被拔出时,运行时调用 cardRemoved() 方法。在更为充实的下列清单中,插入卡片将初始化 applet 代理的创建,拔出卡片将触发 applet 代理的清除。清单还显示了用于信用卡余额的请求被委托给代理。

import opencard.core.event.CTListener;

import opencard.core.event.CardTerminalEvent;

import opencard.core.service.SmartCard;

import opencard.core.service.CardService;

...

public class MyHostSideApp implements CTListener

{

public void MyHostSideApp() {

try {

// Initialize the framework

SmartCard.start ();

// Register this as a Card Terminal Event Listener

CardTerminalRegistry.getRegistry().addCTListener(this);

} catch (Exception e) {

// Handle error.

...

}

}

/**

* Card insertion event. Get new card and card service

* @param ctEvent The card insertion event.

*/

public void cardInserted(CardTerminalEvent ctEvent) {

try {

// Get a SmartCard object

card = SmartCard.getSmartCard(ctEvent);

// Get the card proxy instance.

myCardProxy = (MyCardProxy)

card.getCardService(MyCardProxy.class, true);

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* Card removal event. Invalidate card and card service.

* @param ctEvent The card removal event.

*/

public synchronized void cardRemoved(CardTerminalEvent ctEvent) {

card = null;

myCardProxy = null;

// Initialize the framework

SmartCard.shutdown();

}

/**

* Get balance from the smart card.

*/

public int getBalance() {

try {

// Get mutex to prevent other Card Services from modifying

// data. Delegate the call to the applet proxy.

card.beginMutex();

return Integer.parseInt(myCardProxy.getBalance());

} catch (Throwable e) {

return 0;

} finally {

// End mutual exclusion

card.endMutex();

}

}

...

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有