简介
Java Card 技术适用于 Java 平台,可应用于环境高度专用化、内存和处理约束比 J2ME 设备更苛刻的智能卡和其他设备。
智能卡在个人安全领域发挥着举足轻重的作用。它们可以用于添加身份验证,并对安全级别很高的信息系统提供安全访问。存储在智能卡中的信息是可移植的。借助 Java Card 技术,您可以携带有价值且敏感的个人信息,例如病历、信用卡号或者存储在压缩但非常安全的介质中的电子现金余额。
什么是智能卡?
智能卡不是什么新鲜事物。早在 20 年前,欧洲就以(非智能形式)内存卡的形式引入了智能卡的概念,使用它保存重要的电话信息,其作用是减少盗打付费电话的可能。
智能卡技术由一项国际标准组织(ISO)和国际电工委员会 (IEC)组成的联合技术委员会(JTC1) 定义并管理的工业标准。1987年推出的ISO/IEC 7816国际标准系列在2003年推出了它的最新的升级版本,界定了智能卡的方方面面,包括物理特性、物理接触界面、电子信号和传输协议、命令、安全体系、应用程序标识符和公用数据元素等。
智能卡是一个含有嵌入式集成电路(IC)的塑料卡片。类似于一张信用卡。当用作 SIM 卡时,这个塑料卡片很小,但大小刚好能插入手机中。智能卡从设计上保证高度安全性,窜改一点点内容都会导致毁坏它所包含的信息。
在智能卡使用的某些领域中,它们仅仅提供受保护的非易失性存储。更高级的智能卡还有用于安全处理和存储的微处理器和内存,可以用于使用公钥或共享密钥算法的安全应用程序。智能卡上的非易失性存储是最宝贵的资源,可以用于保存安全密钥和数字证书。一些智能卡有单独的加密协处理器,支持象 RSA、AEC 和 (3)DES 这样的算法。
智能卡不含电池,只有在和智能卡读取器相连时才被激活。当被连接时,在执行完一段复位序列后,智能卡仍保持被动状态,等待接受从客户机(主机)应用程序发来的命令请求。
智能卡可以是 接触式的 或者 非接触式的。正如其名称所暗示的,接触式智能卡通过介于智能卡读取器与智能卡 8 触点之间的物理接触进行通信并工作;而非可接触式智能卡依靠在小于 2 英尺的一般距离之内的射频信号进行通信。非接触式智能卡的射频通信基于类似于用于保存反盗窃和记录清单的 无线射频识别(Radio Frequency ID,RFID)标记 技术。图 1 描述了接触式和非接触式智能卡:
图 1a.接触式智能卡
图 1b.非接触式智能卡
Java Card 技术也存在不同于智能卡的外形规格,例如智能按钮和 USB 令牌(如图 2 所示)。它们可以同智能卡一样验证用户或传送敏感信息。智能按钮包括一块电池而且是基于可接触模式,而 USB 令牌则可以直接插入到个人计算机的 USB 端口,而无需使用接触式或非接触式读取器。这两种类型的 Java Card 均提供与智能卡相同的编程功能,并具有防篡改特性。
图 2a.带有 Java 功能的智能按钮
图 2b.带有 Java 功能的 USB 令牌
Java Card 规范
多年以前,Sun Microsystem 就实现了智能卡和类似的资源约束设备的潜能,并为 Java 技术的子集定义一套规范,以便为 Java Card applet 创建应用程序。支持这些规范的设备简称 Java Card 平台。在 Java Card 平台上,来自不同供应商的多个应用程序可以安全地共存。
一台典型的 Java Card 设备有一个运行于 3.7MHz 的 8 位或 16 位 CPU ,带有 1K 的 RAM 和多于 16K 的非易失内存(EEPROM 或闪存)。高性能的智能卡带有单独的处理器、加密芯片和内存加密,某些智能卡还带有 32 位 CPU。
Java Card 技术规范的最新版本为 2.2,由三部分组成:
Java Card 虚拟机规范,定义了用于智能卡的 Java 程序编程语言的一个子集和虚拟机。
Java Card 运行时环境规范,详细定义了基于 Java 的智能卡的运行时行为。
Java Card API 规范,定义了用于智能卡应用程序的核心框架和扩展 Java 软件包和类。
Sun 还提供了 Java Card 开发工具(JCDK),其中包括 Java Card RE 和 Java Card VM 的参考实现以及其他帮助开发的 Java Card applet 。本文第二部分将介绍详细 JCDK。
Java Card 技术和 J2ME 平台
让我们比较一下 Java Card 和 J2ME 平台技术:
图 3 Java Card 技术和 J2ME 平台
CDC 和 CLDC 配置以及相关配置文件都是 J2ME 平台的组成部分,而 Java Card 是一个为智能卡环境而专门创建的单独平台专门。
Java Card 应用程序的元素
完整的 Java Card 应用程序由一个后端应用程序、系统、一个主机(卡片外部)应用程序、一个接口设备(卡片读取器)和卡片内部 applet、用户证书和支持软件组成。所有这些元素一起构成了一个安全的端到端应用程序:
图 4. Java Card 应用程序的架构
一个典型的 Java Card 应用程序并不是独立的,而是包含卡片端、读取器端和后端元素。下面详细介绍一下每个元素。
后端应用程序和系统
后端应用程序提供支持卡片内 Java applet 的服务。例如,一个后端应用程序能提供与安全系统和卡上证书的连接,并提供强大的安全性。在电子支付系统中,后端应用程序可以提供到信用卡和其他付款信息的访问。
读取器端主机应用程序
主机应用程序存在于一个像个人计算机这样的台式机或者终端、电子付款终端、手机或者一个安全子系统中。
主机应用程序处理用户、Java Card applet 和提供器的后端应用程序之间的通信。
传统的读取器端应用程序用 C 语言编写。近来 J2ME 技术的广泛普及有望使用 Java 实现主机应用程序;例如,它可以在一台支持 MIDP 和安全性和信任服务应用编程接口 (Security and Trust Services API)的手机上运行。
智能卡供应商通常不仅提供开发工具箱,还提供 API 以便支持读取器端应用程序以及 Java Card applet。这方面的示例包括基于 Java 的 API 集合的 OpenCard Framework,其中隐藏了与不同卡片供应商提供的卡片读取器进行交互的一些细节,还有两个将在本文后边讨论的 Java Card 远程方法调用(Java Card Remote Method Invocation,JCRMI)分布式对象模型和安全性和信任服务应用编程接口(SATSA)。
读取器端卡片接入设备
卡片接入设备(Card Acceptance Device,CAD)是一个位于主机应用程序和 Java Card 设备之间的接口设备。CAD 为卡片提供电源以及与之进行的电子的或 RF 通信。CAD 可能是使用串行端口与台式计算机相连的卡片读取器,也可能被集成为一个终端。例如饭店或者加油站内的电子付款终端。接口设备从主机应用程向卡片转发应用协议数据单元(Application Protocol Data Unit,APDU) 命令(稍后加以讨论),再将卡片发出的响应传递给主机应用程序。一些 CAD 有用于输入 PIN 入口点的键盘,有的可能还有显示屏。
卡片端 Applet 和环境
Java Card 平台是一个多应用程序环境。如图 4 所示,卡片上可能存在一个或多个 Java Card applet,与支持软件 —— 卡片操作系统和 Java Card 运行时环境(JCRE)一起存储在卡片上。JCRE 由 Java Card VM、Java Card Framework 和 API 以及一些扩展 API 组成。
所有的 Java Card applet 扩展 Applet 基类,且必须实现 install() 和 process() 方法;当 JCRE 安装 applet 时,调用 install(),每当为 applet 传入 APDU 时,JCRE 都会调用 process()。
当 Java Card applet 被加载时进行实例化,并且电源被切断时仍保持激活状态。卡片 applet 起到服务器的作用,是被动的。当卡片接上电源,每个 applet 一直处于未激活状态直到进行初始化时被选中。当向 applet 发送 APDU 时它才被激活。Applet 如何变为激活状态(被选中)的详细内容在 Java Card Applet 的生命周期 一节中进行了描述。
与 Java Card Applet 之间的通信(访问智能卡)
您可以使用两种模式中的任意一种完成主机应用程序和 Java Card applet 之间的通信。第一种模式是基本的消息传递模式,第二种模式基于 Java 远程方法调用(Java RMI)和 J2SE RMI 分布式对象模型的子集。此外,通过基于通用连接框架(Generic Connection Framework,GCF)API 的一个更抽象的 API,SATSA 使您能够使用消息传递或 JCRMI 访问智能卡。
消息传递模型
在图 5 中说明的消息传递模型为所有 Java Card 通信提供基础。其核心是与 CAD 和 Java Card Framework 之间交换的逻辑数据包 —— 应用协议数据单元(APDU)。Java Card Framework 接收任何由 CAD 发送的 APDU 并 转发给适当的 applet。Applet 处理 APDU 命令 并返回一个 APDU 响应。APDU 遵循国际标准 ISO/IEC 7816-3 和 7816-4。
图 5 使用消息传递模型进行通信
读取器和卡片之间的通信通常建立在两项链接协议基础上,面向字节的 T=0 或面向块的 T=1。可选协议简称 T=USB,且可能会采用 T=RF。JCRE APDU 类隐藏了来自应用程序的协议细节,但并非全部,因为 T=0 协议相当复杂。
1. APDU 命令
APDU 命令的结构由第一个字节的值控制,大多数情况下,类似于:
图 6. APDU 命令
APDU 命令有一个必须有的标题和可选的主体,其中包括:
CLA (1 字节):这个必需的字段识别指令的一个特定于应用程序类。有效的 CLA 值在 ISO 7816-4 规范中做了界定:
表 1. ISO 7816 CLA 值
CLA 值 指令类
0x0n,0x1n ISO 7816-4 卡片指令,例如用于文件存取和安全操作
20 to 0x7F 保留
0x8n 或者 0x9n 您可以使用特定于应用程序指令的 ISO/IEC 7816-4 格式,根据标准解释 'X'。
0xAn 特定于应用程序的指令或供应商指定的指令
从 B0 到 CF 您可以使用特定于应用程序指令的 ISO/IEC 7816-4 格式
从 D0 到 FE 特定于应用程序的指令或供应商指定的指令
FF 为协议类型选项所做的保留
从理论上讲,可以使用所有的 CLA 值 0x80 或更高值来用于特定于应用程序的指令,但是实际上在当前许多的 Java Card 实现中,只有以黑体显示的指令是实际认可的。
INS (1 字节):这个必需的字段说明在指令类中由 CLA 字段识别的特定指令。ISO 7816-4 标准指定了当卡片根据标准所定义的卡片内部文件系统接收到指令时,用于访问卡片内部数据的基本指令。在标准中的其它地方还指定了一些附加功能,其中包括安全功能。要了解 ISO 7816 部分指令的列表,参见表 2。只有当使用适当的 CLA 字节值时,根据标准规定才可以定义自己的应用程序指定的 INS 值。
表 2. 当 CLA = 0X 时的 ISO 7816-4 INS 值
INS 值 命令描述
0E 删除二进制
20 验证
70 管理通道
82 外部身份验证
84 质询
88 内部身份验证
A4 选择文件
B0 读取二进制
B2 读取记录
C0 获取响应
C2 信封
CA 获取数据
D0 编写二进制码
D2 编写记录
D6 更新二进制码
DA 输入数据
DC 更新记录
E2 附加记录
P1 (1 字节):这个必需的字段定义指令参数 1。可以使用该字段限定 INS 字段,或者用于输入数据。
P2 (1 字节):这个必需的字段定义指令参数 2。可以使用该字段限定 INS 字段,或者用于输入数据。
Lc (1 字节):这个可选字段是命令的数据字段中的字节数。
Data 字段(变量,字节的 Lc 数):这个可选字段保存命令数据。
Le (1 字节):这个可选字段指定在期望响应的数据字段中的字节最大数。
根据所出现的命令数据和是否需要响应,为 APDU 命令配置了四个变量。只有使用协议 T=0 的情况下才有必要关注这些变量。
图 7. APDU 命令的四种可能结构
典型的应用程序使用带有不同结构的各种 APDU 命令。
2. APDU 响应
APDU 响应的格式很简单的:
图 8 APDU 响应
响应 APDU 与命令 APDU 类似,都具有可供选择的必需字段:
Data 字段(变量长度,由 APDU 命令中的 Le 确定):该可选字段包含由 applet 所返回的数据。
SW1 (1 字节):这个必需的字段是状态字 1。
SW2 (1 字节):这个必需的字段是状态字 2。
在 ISO 7816-4 规范中定义了状态字的值:
图 9. 响应状态代码
在 Java Card Framework API 中的 ISO7816 Java 接口中定义了许多常量以便实施返回错误代码的规范。
3. 处理 APDU
每当为所选的 applet 提供传入的 APDU 时,JCRE 都会调用 applet 的 process() 方法,把传入的 APDU 作为参数来传递。Applet 必须解析 APDU 命令,处理数据,生成响应 APDU,然后把控制权返回给JCRE。
您会在本文的第二部分“ process() 方法 —— 与 APDU 协同工作”中找到关于 APDU 命令和响应的更多信息。
Java Card RMI(JCRMI)
第二种通信模型基于 J2SE RMI 分布式对象模型的子集。
在 RMI 模型中,服务器应用程序创建并访问远程对象,且客户机应用程序获得对远程对象的远程引用,然后为这些对象调用远程方法。在 JCRMI 中,Java Card applet 为服务器,主机应用程序为客户机。
在扩展软件包 javacardx.rmi 中由类 RMIService 提供 JCRMI。JCRMI 消息被封装到传入的 RMIService 方法的 APDU 对象中。换句话说,JCRMI 提供了一个 熟练运用基于 APDU 消息传递模型 的分布式对象模型的机制,借助该机制,服务器和客户机进行通信,往复传递方法信息、参数、并返回值。
安全性和信任服务应用编程接口(SATSA)
在 JSR 177 中所定义的 SATSA 指定了为 J2ME 提供安全性和信任服务 API 的可选软件包。该 客户机 API 提供了对各种服务的访问,这些服务由安全存储、敏感信息的检索以及加密和身份验证服务等 安全元素 (例如智能卡)提供。
SATSA 采用有限连接设备配置(Connected Limited Device Configuration,CLDC)的版本 1.0 中所定义的通用连接框架(Generic Connection Framework,GCF) 以便向消息传递和 JCRMI 通信模型提供更抽象的接口。为了支持消息传递,SATSA 定义了 apdu:URL 方案和 APDUConnection,为了支持 JCRMI,它还定义了 jcrmi:方案和 JavaCardRMIConnection。
SATSA 由下列软件包组成:
java.rmi 定义了 Java 2 标准版 java.rmi 软件包的子集,以及专门定义的 Remote 和 RemoteException。
javacard.framework 定义了远程方法可能引发的标准 Java Card API 异常:CardRuntimeException、ISOException、APDUException、CardException、PINException、SystemException、TransactionException 和 UserException。
javacard.framework.service 定义了远程方法可能引发的标准 Java Card API 服务 异常:ServiceException。
javacard.security —— 定义了远程方法会引发的标准 Java Card API 加密相关 异常。CryptoException。
javax.microedition.io 定义了两种连接接口,用于基于 APDU 协议而进行智能卡访问的 APDUConnection 和用于 Java Card RMI 协议的 JavaCardRMIConnection。
javax.microedition.jcrmi 定义了桩模块所使用的类和接口,该桩模块由 Java Card RMI 桩模块编译器生成。
javax.microedition.pki 定义了用于用户证书基础管理的类。
javax.microedition.securityservice 定义了生成应用程序级数字签名的类。
Java Card VM
Java Card 虚拟机 (JCVM)规范定义了用于智能卡的 Java 编程语言和 Java 可兼容的 VM,包括二进制数据表示和文件格式以及 JCVM 指令集。
用于 Java Card 平台的 VM 分两部分实现,一部分在卡片外部,另一部分在卡片上运行。卡片内部的 Java Card VM 解释字节码、管理类和对象等等。外部 Java VM 部分是一个开发工具,通常简称 Java Card 转换工具 ,加载、验证进而为卡片内部指令执行进一步准备卡片 applet 中的 Java 类。转换工具的输出是一个 Converted Applet(CAP)文件,保护所有可加载、可执行的二进制表示中的 Java 软件包。转换程序验证类是否遵循 Java Card 规范。
JCVM 只支持 Java 编程语言的有限子集,但是保留了许多熟悉的特性,包括对象、继承、软件包、动态对象创建、虚拟方法、接口和异常。JCVM 规范放弃支持使用过多智能卡有限内存的语言元素。
表 3. Java Card 语言限制汇总
语言特性 不支持动态类加载、安全管理器(java.lang.SecurityManager)、线程、对象克隆和软件包访问控制的某些方面。
关键词 不支持 native、synchronized、transient、volatile、strictfp。
类型 不支持 char, double, float 和 long 或多维数组。对 int 的支持是可选的。
类和接口 除 Object 和 Throwable 之外,不支持 Java 核心 API 类和接口(java.io, java.lang, java.util),绝大多数 Object 和 Throwable 方法不可用。
异常 一些 Exception 和 Error 子类被忽略,因为这些封装的异常和错误在 Java Card 平台中不出现。
还存在编程模型的限制。例如,一个被加载的库类再也不会在卡片中被扩展;它被隐式地 结束。
按照内存约束,JCVM 规范另外定义了许多程序属性的约束。表 4 总结 JCVM 资源约束。请注意许多约束通常对 Java Card 开发人员通常是透明的。
表 4. Java Card VM 约束的摘要信息
软件包 一个软件包可以引用多达 128 个其他的软件包。
完全合格的软件包名称限定为 255 字节。请注意字符大小取决于字符编码。
一个软件包能够具备多至 255 个类。
类 一个类可以直接或间接实现多达 15 个接口。
一个接口可以从多达 14 个接口继承。
如果一个软件包含有 applet(一个 applet 软件包)它可以有多达 256 个静态方法,如果包不含有 applet(库软件包),它可以有 255 个静态方法。
一个类可以实现 128 个公共的或受保护的实例方法,以及带有软件包可见性的 128 个公共的或受保护的实例方法。
同 J2SE VM 中的类文件一样,在 Java Card VM 中的 类文件 是核心,但是 JCVM 规范定义了深化平台独立性的两个其他文件格式 Converted Applet (CAP) 和 导出 格式,二者在本系列文章第二部分的“开发 Java Card 应用程序”一节中加以描述。
Java Card API
Java Card API 规范定义了传统 Java 编程语言 API 的小型子集 —— 甚至比 J2ME 的 CLDC 子集还要小。它不支持 Strings 或多线程。在这个子集中存在像 Boolean 和 Integer 这样的包装类,不存在 Class 或 System 类。
除了熟悉的 Java 核心类的小型子集外,Java Card Framework 定义了自己的核心类子集以便专门支持 Java Card 应用程序。这些子集被包含在下列软件包内:
java.io 定义了一个异常类,基 IOException 类,以便完成 RMI 异常层级结构。除此之外,不包含其它传统的 java.io 类。
java.lang 定义了 Object 和 Throwable 类,但是没有 J2SE 中那么多方法。它还定义了许多异常类:Exception 基类、各种运行时异常和 CardException。除此之外,不包含其它传统的 java.lang 类。
java.rmi 定义了 Remote 接口和 RemoteException 类。除此之外,不包含其它传统的 java.rmi 类。其中包含对远程方法调用(Remote Method Invocation,RMI)的支持,以便简化与使用 Java Card 技术的设备之间的迁移和与之的集成。
javacard.framework 定义了构成核心 Java Card Framework 的接口、类和异常。它还定义了像个人识别号码(Personal Identification Number,PIN)、应用程序数据单元(Application Protocol Data Unit,APDU)、Java Card applet(Applet)、Java Card System(JCSystem)和实用类这样的重要概念。它还定义了各种 ISO7816 常量和各种特定于 Java Card 的异常。表 5 总结了这个软件包的内容:
表 5. Java Card v2.2 javacard.framework
接口 ISO7816 定义了与 ISO 7816-3 和 ISO 7816-4 相关的常量。
MultiSelectable 识别可以支持并发选项的 applet。
PIN 表示用于安全目的(身份验证)的个人识别号码。
Shareable 识别共享对象。那些能通过 applet 防火墙的对象必须实现这个接口。
类 AID 定义了遵循 ISO7816-5 标准并且与应用程序提供器相关的应用程序标识符,还定义了 applet 的强制属性。
APDU 定义了遵循 ISO7816-4 标准的应用程序协议数据单元(APDU),它是 applet(卡片内部)和主机应用程序(卡片外部)之间使用的通信格式。
Applet 定义了一个 Java Card 应用程序。所有的 applet 必需扩展这个抽象类。
JCSystem 提供了控制 applet 生命周期、资源和事务管理以及 applet 内部对象共享和对象删除的方法。
OwnerPIN 是 PIN 接口的一个实现。
Util 提供了数组和短型变量操作的使用方法,包括 arrayCompare()、arrayCopy()、arrayCopyNonAtomic()、 arrayFillNonAtomic()、getShort()、makeShort() 和 setShort()。
异常 以下为所定义的 Java Card VM 异常类:APDUException、CardException、CardRuntimeException、ISOException、PINException、SystemException、TransactionException 和 UserException。
javacard.framework.service 定义了用于各种 服务 的接口、类和异常。服务处理以 APDU 的格式传入的命令。表 6 总结了框架服务 API:
表 6. javacard.framework.service
接口 Service 是基本的服务接口,定义 processCommand()、 processDataIn() 和 processDataOut() 等方法。
RemoteService 是一个通用 服务,给出对卡内部服务访问的远程进程。
SecurityService 扩展了 Service 基接口,并提供方法以便查询包括 isAuthenticated()、isChannelSecure() 和 isCommandSecure() 在内的当前安全状态。
类 BasicService 是一个 服务 的默认实现;它提供了辅助方法以便处理 APDU 和服务协作。
Dispatcher 维护服务的注册表。如果希望将 APDU 的处理委托给几个服务,请使用分发程序。分发程序可以使用 process() 方法完整处理 APDU,或者使用 dispatch() 方法通过几项服务将其分发进行处理。
异常 ServiceException 是一个与服务相关的异常。
javacard.security 定义用于 Java Card 安全框架的类和接口。Java Card 规范定义了一个健壮的安全 API,其中包括各种类型的私人密钥和公共密钥及其算法、计算循环冗余码校验(CRC)的各种方法、消息摘要和签名:
表 7. javacard.security
接口 通用基接口 Key、PrivateKey、PublicKey 和 SecretKey 以及表示各种类型的安全密钥和算法的子接口:AESKey、DESKey、DSAKey、DSAPrivateKey、DSAPublicKey、ECKey、ECPrivateKey、ECPublicKey、RSAPrivateCrtKey、RSAPrivateKey 和 RSAPublicKey。
类 Checksum:一个用于 CRC 算法的抽象基类
KeyAgreement:一个用于密钥协议算法的基类
KeyBuilder:密钥对象厂
KeyPair:一个存储密钥对(一个是私人密钥,一个是公共密钥)的容器
MessageDigest:用于散列算法的基类
RandomData:用于随机数生成器的基类
Signature:一个用于签名算法的基抽象类
异常 CryptoException:与加密相关的异常,例如不支持的算法或未初始化的密钥。
javacardx.crypto 是一个定义接口 KeyEncryption 和类 Cypher 的扩展软件包,其中接口和类在各自的软件包中,可以轻松实现导出控制。使用 KeyEncryption 来解密一个使用加密算法的输入密钥。Cypher 是所有加密器必须实现的基抽象类。
javacardx.rmi 是一个定义 Java Card RMI 类的扩展软件包。它定义了两个类,CardRemoteObject 和 RMIService。CardRemoteObject 定义了两种方法,export() 和 unexport(),用于启用和禁用对来自卡片外部对象的远程访问。RMIService 扩展 BasicService 并实现 RemoteService 以便处理 RMI 请求。
Java Card 运行时
JCRE 规范定义了 Java Card VM 的生命周期、applet 的生命周期、applet 被选择和与其它 applet 隔离的方式、事务、对象持久性和共享。JCRE 提供一个由卡片操作系统所提供服务的平台独立接口。该接口由 Java Card Virtual Machine、Java Card AP 和供应商指定的扩展组成:
图 10. Java Card 架构和运行时环境
Java Card VM 的生命周期
JCVM 的生命周期与卡片本身的生命周期一致:JCVM 的生命周期从卡片被制造并测试后且发行给卡片持有人前的某一时刻开始,在卡片被废弃或损坏之时结束。当接到卡片上的电源去掉时,JCVM 并不停止运行,因为其状态在卡的非易失内存中仍被保留下来。启动 JCVM 需要初始化 JCRE 并创建所有在 JCVM 整个生命周期中都处于激发状态的 JCRE 框架对象。在 JCVM 启动之后,所有与卡片的交互原则上受卡片中某个 applet 控制。当电源从卡片上移走时,所有包含在 RAM 中的数据全部丢失,但是存储在非易失内存中的状态仍然被保留下来。当电源重新供应时,VM 再一次被激发,此时 VM 的状态和对象的状态被存储,执行恢复,等待新的输入。
Java Card Applet 的生命周期
卡片上的 applet 由应用程序 ID (AID)惟一识别。AID 在 ISO 7816-5 中定义,是一个介于 5 个字节和 16 个字节的序列。所有 applet 必须扩展这个定义 JCRE 使用方法的 Applet 抽象基类,以便控制 applet 的生命周期,总结内容如图 10 所示:
图 11. Java Card Applet 的生命周期方法
Applet 的生命周期始于 applet 被下载到卡片上且 JCRE 调用 applet 的静态 Applet.install() 方法,以及 applet 通过使用 JCRE 调用 Applet.register() 进行自我注册之时。一旦 applet 被安装和注册,它便处于未被选中的状态,可以用于选择并进行 APDU 处理。图 11 Applet 方法操作的总结
图 12 使用 Java Card Applet 方法
当 applet 处于未被选中的状态时,它是无效的。当主机应用程序请求 JCRE 选择卡片中特定的 applet 时(通过向卡片读取器发出指令 SELECT APDU 或 MANAGE CHANNEL APDU 完成),applet 被选中用于 APDU 处理。要通知 applet 已经被主机应用程序选中,JCRE 会调用 select() 方法;applet 通常执行适当的初始化过程,准备用于 APDU 处理。
一旦完成选择 applet,JCRE 将传入的 APDU 命令传递给 applet,通过调用 process() 方法进行处理。JCRE 可以捕捉任何 applet 捕捉失败的异常。
当主机应用程序告诉 JCRE 选择另一个 applet 的时候,当前正在使用的 applet 被取消选中。通过调用通常执行清除逻辑并返回 applet 为无效、未被选中状态的 deselect() 方法,JCRE 通知处于激活状态的 applet 已经被取消选中。
Java Card 会话和逻辑通道
当卡片接通电源并与卡片读取器进行 APDU 交换期间,卡片进行会话。
Java Card 2.2 支持 逻辑通道 概念,允许卡片一次打开多至 16 个的应用程序会话,一个会话占用一个逻辑通道。由于卡片内的 APDU 处理过程不能被中断,每个 APDU 包含了对逻辑通道(以 CLA 字节 的形式)的引用,替换的 APDU 可以不同时访问卡片中的许多 applet。您还可以设计 applet 为 多项选择的;也就是一次可以在多于一个的逻辑通道上进行通信。多项选择的 applet 必须实现 javacard.framework.MultiSelectable 接口和相应的方法。
在一些卡片部署中,默认的 applet 可以在卡片复位后被定义为自动选择,以便在基逻辑通道上(通道 0)进行通信。Java Card 2.2 许可定义默认的 applet,但是没有指定定义默认 applet 的方式;此种机制是由供应商指定的。
Applet 的隔离和对象共享
Java Card 平台是一个安全的多应用程序环境 —— 许多不同供应商生产的不同 applet 可以在同一张卡片上安全地共存。每个 applet 被分配给一个 执行上下文,用于控制所分配对象的访问。在两个扩展上下文之间的边界通常被称作 applet 防火墙。它是沙箱的 Java 安全概念的 Java Card 运行时增强,结合了类加载器功能、java.ClassLoader 访问控制器和 java.AccessController。Java Card 防火墙创建了 虚拟堆,这样,一个对象只能访问在相同防火墙内的(公共的)方法和这些对象的数据。防火墙可能包含许多 applet 和其他对象,例如常见的安全密钥。Java Card 当前执行的上下文在软件包范围内。当每个对象被创建时,为对象分配调用程序的执行上下文。
Java Card 平台支持跨越防火墙的安全对象共享。图 12 描述 applet 隔离和对象共享:
图 13. Applet 防火墙和对象共享
在图 12 中描述了典型的流程,如图所示:
Appleta 通过调用系统的 JCSystem.getAppletShareableInterfaceObject() 方法请求访问 Appletc 的可共享接口。
为了表示 Appleta,JCRE 通过调用 applet 的 getShareableInterfaceObject() 方法请求 Appletc 的可共享接口。
如果 Appletc 允许共享,Appleta 将获得对一个 Appletc 共享对象的引用。Appleta 现在就可以访问 Appletc 了。Appleta 将拥有自己创建的任意对象,即使这些对象是在 Appletc 中定义的。
在同一个执行上下文中的 Applet 在默认情况下能够互相访问,因此,Appleta 和 Appletb 不需要遵循这一程序来共享对象。
管理内存和对象
在 Java Card 设备上,内存是最有价值的资源。在某些 Java Card 实现中,垃圾收集器有可能不可用。当一个对象被创建时,对象及其内容被保存在非易失内存中,使得这些内容在会话过程中可用。在某些情况下,应用程序数据不需要是持久性的 —— 它可以是临时性的或者是 瞬态的。要减少对智能卡持久性内存的损坏,因此要最大限度地增加此类内存的生命周期,尽可能将经常被更新的数据作为瞬态类型的数据处理。
Java Card 技术不支持 瞬态 关键字。Java Card API(javacard.framework.JCSystem)而是定义了使您能够在运行时创建瞬态数据的三种方法,以及使您能检查对象是否为瞬态的第四种方法。
static byte[] makeTransientByteArray(short length, byte event)
static Object makeTransientObjectArray(short length, byte event)
static short[] makeTransientShortArray(short length, byte event)
static byte isTransient(java.lang.Object theObj)
还可以创建 byte 或 short 的基本数据类型的瞬态数组,或者创建一个瞬态的 对象。但是请记住,下列行为可用于瞬态数据:
瞬态对象的状态在会话过程中不是持久的。请注意其内容(不是对象本身)是瞬态的。对于任意其他 Java 语言对象,只要引用瞬态对象,它就会一直存在。
当发生卡片复位或 applet 取消选中时,瞬态对象的内容可能会被复位为字段的默认值(零,false 或 null)。
出于安全原因,瞬态对象的字段不在持久内存中储存。
瞬态对象字段的更新 不是 原子的,且不受事务的影响。
在 Java Card 环境中,数组和基本类型在对象声明位置被声明,为了有利于对象的重用,应该最大限度减少对象实例化。在 applet 生命周期内只对对象进行一次实例化,最好是在 applet 初始化时通过在 生命周期内惟一调用一次 install() 方法进行实例化。
为了利于重用,对象保留在原有位置或在生命周期内被引用,且这些对象的状态(成员变量的值)在重用前适当复位。由于垃圾收集器并不是总是可用的,应用程序可能不再回收分配给超出范围的对象的存储空间。
持久性事务
JCRE 支持原子事务安全地更新一个或多个持久性对象。即使发生断电或程序错误,事务也要确保数据完整性。事务通过下列方法得到系统级支持:
JCSystem.beginTransaction()
JCSystem.commitTransaction()
JCSystem.abortTransaction()
在许多事务模型常见的模型中,Java Card 事务从调用 beginTransaction() 开始,并从调用 commitTransaction() 或 abortTransaction() 结束。让我们看一看使用这些 API 的代码片断:
...
private short balance;
...
JCSystem.beginTransaction();
balance = (short)(balance + creditAmount);
JCSystem.commitTransaction();
...
务必确保实例变量余额的更新操作为原子操作。如果发生程序错误或电源重置,确保事务会恢复 余额 的前值。
JCRE 不支持嵌套事务。
结束语
本系列文章的第一部分介绍了相当广泛的内容:使用智能卡存储敏感信息和安全处理事务,Java Card 技术的方方面面 —— Java Card VM、运行时环境、相关的 API 和 Java Card applet 的行为。系列文章的第二部分将介绍 Java Card 技术的开发部分。
使用 Java Card 技术的智能卡具备最佳的可移植性,并且是携带数字个人信息计算功能的安全方式;在当今的数字世界中,它是一项强大的必备技术。