Java Card体系结构
Java Card是能运行Java 程序的智能卡。针对这种新的Java平台,Sun公司的JavaSoft部门已经制订了Java Card 2.0 API技术规范,目前,已有若干授权方正在智能卡上实现这种API。要编写与2.0兼容的Java Card 应用程序,开发人员需要从体系结构上了解Java Card的内涵,其内核类的含义,以及如何针对智能卡开发应用程序。
什么是智能卡?
智能卡与信用卡大小相同,可通过嵌入到其塑料基体中硅片上的电子电路来存储和处理信息。智能卡主要分为两类:包含有微处理器并具有读、写和计算功能的智能卡(如小型微电脑)。另一类为没有微处理器的内存卡 ,它只能存储信息。内存卡利用安全逻辑算法控制对存储器的存取。
所有智能卡都包括三类存储器:永久不变存储器(persistent non-mutable memory);永久可变存储器(persistent mutable memory);和非永久可变存储器(non-persistent mutable memory)。ROM、EEPROM和RAM是目前智能卡的三类存储器中使用最普遍的。永久存储器又称非易失性存储器。在本文中我们将交替使用术语永久存储器和非易失性存储器。
国际标准组织规定的ISO 7816第1-7部分包括覆盖智能卡各个方面的一组标准。ISO 7816包括:物理特性(第1部分)
尺寸和触点位置(第2部分)
电子信号和传输协议(第3部分)
行业间交换指令(第4部分)
应用程序标识符(第5部分)
行业间数据元素(第6部分)
行业间SCQL指令(第7部分)
下图表示智能卡的物理特性,在ISO 7816第1部分中有规定。
Physical Characteristics ---物理特性
Magnetic Stripe ( Back of card ) ---磁条(卡的背面)
Contacts ---触点
Embossing area --- 刻字区
Front of Card ---卡的正面
有关ISO 7816和智能卡的详细情况,请参见"智能卡:入门"。
一般的说,智能卡不包括电源,显示屏,或键盘。它通过其8个触点,利用串行通信接口与外部世界交互。ISO 7816第2部分对智能卡的尺寸和触点位置有详细规定。
Eight Contact Points --- 8个触点
Power Supplier ---电源
Reset ---复位
Check ---检查
Ground ---接地
Optional Contact ---可选触点
Input/Output ---输入/输出
Optional Contacts ---可选触点
智能卡插入可能与另一台计算机相连的卡接收设备(CAD)。卡接收设备又可称作终端、读卡器和IFD (接口设备)。 都具有相同的基本功能,即向智能卡提供电源和建立数据传输连接。
当两台计算机彼此进行通信时,它们交换根据一系列协议构造的数据包。类似地,智能卡也使用自己的数据包---称作APDU (应用协议数据单元)与外部世界对话。APDU包含一条指令或响应信息。在智能卡的世界里 采用的是主 从 模式,而智能卡永远扮演从动的角色。换句话说,智能卡总是在等待来自终端的 命令APDU。随后,它执行APDU规定的动作,并以一个 应答APDU向终端作出回答。智能卡与终端之间互相交换 命令APDU和 应答APDU。
下表分别表示 命令APDU和 应答APDU的格式。ISO 7816第4部分对APDU的结构有专门描述。
命令APDU | ||||||
标题头(必须) | 主体(可选) | |||||
CLA | INS | P1 | P2 | Lc | 数据字段 | Le |
标题 头对被选指令进行编码。它包括4个字段:类(CLA)、指令(INS)、和参数1和2 (P1和P2)。每个字段包含一个字节:
CLA:类字节。在很多智能卡上,这个字节用来表示应用程序。
INS:指令字节。这个字节表示指令代码。
P1-P2:参数字节。这些字节对 命令APDU提供进一步说明。
Lc表示 命令APDU的数据字段的字节数;Le表示以下 应答APDU 的数据字段希望的字节数。
应答APDU | ||
主体(可选) | 尾部(必须) | |
数据字段 | SW1 | SW2 |
状态 字节 SW1和SW2表示 命令APDU在智能卡中的处理状态。
什么是Java Card?
Java Card是能运行Java程序的智能卡。Java Card 2.0技术规范刊登在//www.javasoft.com/javacard上。它包括有关在智能卡上创建Java Card虚拟机和应用编程接口(API)的详细信息。 最低系统要求为16 kbps只读存储器(ROM)、8 kbps EEPROM和256字节随机存取存储器(RAM)。
Java Card上的系统体系结构如下 页图所示。 其中:
Applet ---小应用程序
Industry Add on Classes ---企业添加类
javacard Framework --- Javacard框架
OS & Native Functions ---操作系统和本地功能
如 图所示,Java Card虚拟机是建立在特定集成电路(IC)和本机操作系统执行程序上的。JVM层利用一般语言和系统接口隐藏了制造商的专利技术。Java Card框架定义了一系列用来开发Java Card应用程序和为这些应用程序提供系统服务的应用程编程接口(API)。某特定 行业或特殊的商务应用可提供添加的库,以提供服务或优化安全性和系统模型。Java Card应用程序 称为applets。一个卡上可驻留多个applets。每个applets均被其AID (应用程序标识符)唯一标识。
应该注意的重要的一点是,智能卡不是个人计算机。它们只有有限的存储器资源和计算功能。用户不应该简单地认为Java Card 2.0是JDK的简化版本。
Java Card的 生命周期
Java Card的 生命周期从本机操作系统、Java Card虚拟机、API类库和可选的applets被 写入ROM时开始。将一个可处理进入 命令的永久性组件 写入芯片的非可变存储器,这一过程又称作掩模(masking)。
在安装入您的钱包之前,Java Card需要经过初始化和个人化。初始化指在卡的非可变存储器内装入一般数据。这种数据对很多智能卡都是相同的,而并非某种卡所特有的;例如发行商或制造商的名称。
下一个步骤,个人化,指将卡分配予某个人。这个过程可通过物理个人化或电子个人化完成。物理个人化指在卡的塑料表面上压印或激光压印您的名字和卡号。电子个人化指在卡的非可变存储器中装载个人数据,例如您的个人密钥、名字和个人身份代码。
各厂家和发行商所采用的初始化和个人化的方法不尽相同。对这两个过程来说,通常都是用EEPROM (一类非可变存储器)存储数据。
现在,Java Card就可以使用了。您可以从发行商 处购买Java Card,也可从零售商 处购买Java Card。零售商销售的卡是通用型的,一般都没有经过个人化。
现在,您可以把您的Java Card插入读卡器,并向驻留在卡上的applet发送 命令APDU,或向卡内下载其它applet或数据。
除非Java Card失效或因某种不可恢复的错误而被闭锁,否则Java Card就能一直使用下去。
Java Card虚拟机的 生命周期
与PC或工作站上的Java虚拟机(JVM)不同,Java Card虚拟机可永远工作。
即使电源断开(即卡从读卡器上取下),存储在卡上的多数信息也必须保存下来。Java Card虚拟机可在EEPROM上创建能保存永久信息的对象。Java Card虚拟机的执行寿命就是卡的寿命。在没有电源的情况下,虚拟机按照无限的时钟周期工作。
Java Card applet和对象的 生命周期
Applet的生命周期始自它正确安装并被注册到系统的注册表中,而当它被从 注册表中删除时其生命周期就结束了。被删除的applet的空间可能被再次使用,也可能不再使用,这取决于卡上是否实现了垃圾收集功能。卡上的applet只有当被终端明确选择后才会 处于激活 状态。
对象是在永久存储器(如EEPROM )米博体育建的。如果不被其它永久对象所引用,对象就可能丢失或被当做垃圾收集。但向EEPROM写入的速度要比向RAM写入的速度慢1000倍。
某些对象经常被使用,其字段的内容就不必是永久的。Java Card支持RAM中的瞬态(暂时)对象。如果某个对象被宣布为瞬态 ,则其内容就不能 写入永久存储器。
Java Card 2.0语言子集
Java Card程序当然是用Java编写的。它们 可以用一般的Java编译器来编译。因为存储器资源和计算能力有限,所以Java Card并不能支持Java语言技术规范中规定的所有语言功能。特别地,Java Card不支持:
动态类装载
安全管理器
线程和同步
对象复制
对象回收(finalization)
长基本数据类型( float、double、long、和char )
所以支持这些功能的关键词 被省略也就不足为奇了。在有更大存储器的更高级智能卡上,由虚拟机的实现者决定 是否支持32位整数类型及post-issuance applet的native方法。Post-issuance applet即在卡被发放给持卡人后安装在Java Card上的applet。
Java Card 2.0框架
智能卡在市场上已经出现20多年了,多数智能卡一般都可与ISO 7816第1-7部分和/或EMV兼容。我们在前面已经谈到ISO 7816。什么是EMV呢?即EMV,它是由Europay、Mastercard和Visa等公司规定的基于ISO 7816系列的标准,是具有可满足金融业特定要求的附加专利功能的标准。设计Java Card框架的目的是能轻松支持智能卡系统和应用程序。它隐藏了智能卡体系结构的细节,并为Java Card应用程序开发人员提供了相对简单和直接的编程接口。
Java Card框架包括4个包:
包名 | 说明 |
javacard.framework | 这是JavaCard的内核包。它定义诸如applet和个人身份代码(PIN)等类,这些类是Java Card程序和APDU、系统和Util (为Java Card程序、APDU提供运行时 服务和系统服务等,如APDU处理和对象共享等)的基本构件。 |
javacardx.framework | 这个包可为与ISO 7816-4兼容的文件系统提供面向对象的设计。它支持ISO 7816规定的基本文件(EF)、专用文件(DF)和面向文件的APDU。 |
javacardx.crypto和javacardx.cryptoEnc | 这两个包支持智能卡所要求的密码功能。 |
Java Cardx包符合Java命名原则,是Java Card框架的延伸。您 并非 必须在智能卡上支持这些包。
Java Card安全性
Java applets受Java安全性的限制,但Java Card系统的安全性模式在很多方面与标准Java有所不同。
Java Card不支持安全管理器类。语言 的安全策略是由虚拟机实施的。
Java applets创建可存储和处理数据的对象。对象由创建该对象的applet所拥有。即使applet 有对某个对象的引用,它也不能调用该对象的方法,除非它拥有该对象,或该对象已确定共享。某个applet可与另一个applet或所有applets共享其对象。
applet是Java Card内的独立实体。其选择、执行和功能不受驻留在同一张卡上的其它applet的影响。
Java Card内部工作原理
在Java Card内部,JCRE ( Java Card运行环境)引用Java Card虚拟机和Java Card框架中的类。Java Card内的每个applet都与JCRE赋予的特定AID关联。
当一个applet 被正确装入卡的永久存储器,并与Java Card框架和卡上的其它库关联后,作为applet安装过程的最后一步,JCRE就调用该applet 的安装方法。install是一种公共静态方法, 它被applet类实现以创建 applet的实例,并 在JCRE中为其注册。因为存储器是有限的,所以 较好的编程方法就是 在此时创建和初始化applet在其生命期内需要的对象。
驻留在卡上的applet 只有在被明确选择后才会被激活。终端向JCRE发送一个" SELECT APDU " 命令。JCRE暂停当前被选的applet ,并调用该applet的deselect方法进行必要的清理。随后,JCRE把AID已在" SELECT APDU "中规定的applet 标记为当前被选的applet,并调用新被选中的applet 的select方法。select方法使applet 做好接受APDU 命令的准备。JCRE将随后的APDU 命令发送给当前被选的applet,直到它接收到下一个" SELECT APDU " 命令为止。
怎样编写Java Card小应用程序?
演示如何创建Java Card 2.0 applet 的最好方法就是通过范例。下例为可存储电子现金的电子钱包应用程序。该钱包可处理read_balance、deposit和debit等APDU指令。只有知道钱包所有人的个人身份代码(PIN)才能进入钱包。
这个例子被制作成两栏的格式:左栏包括Java代码及Java风格的注解;右栏提供对左栏 代码的详尽解释。
package bank.purse | Java Card与标准Java一样也支持包和标识符名称惯例 |
import javacard.framework.* ; import javacardx.framework.* ; | |
public class Wallet extends Applet { | 一个applet是 javacard.framework.Applet 的继成类的实例 |
// code of CLA byte in the command APDU header final static byte Wallet_CLA = (byte)0xB0; | CLA标识该应用程序 |
// code of INS byte in the command APDU header final static byte Deposit = (byte)0x10; final static byte Debit = (byte)0x20; final static byte Balance = (byte)0x30; final static byte Validate = (byte)0x40; | INS标识应用程序指令 |
// maximum number of incorrect tries before the PIN is blocked final static byte PinTryLimit = (byte)0x03; // maximum size PIN final static byte MaxPinSize = (byte)0x04; | PIN对象参数 |
// status word (SW1-SW2) to signal that the balance become neagtive; final static short SW_NEGATIVE_BALANCE = (short)0x6910; | Applet特定静态字 |
OwnerPIN pin; byte balance; byte buffer[ ]; // APDU buffer | |
private Wallet( ) { // It is good programming practice to allocate // all the memory that an applet need during its // lifetime inside the constructor pin = new OwnerPIN(PinTryLimit, MaxPinSize); balance = 0; register( ); } // end of the constructor | private构造 方法---类Wallet的实例由其install方法 实例化 applet 通过调用Applet类中所定义的register 方法向JCRE登记注册。现在 对外部 而言,applet 是可见的。 |
public static void install(APDU apdu) { // create a Wallet applet instance new Wallet( ); } // end of install method | 在applet安装过程的最后一步,方法install被JCRE调用 |
public boolean select( ) { |
// returns true to JCRE to indicate that the applet // is ready to accept incoming APDUs . return true; } // end of select method | 这个方法被JCRE调用,表示该applet已被选择。它执行处理以下APDU信息所需要的必要初始化 |
public void process(APDU apdu) { // APDU object carries a byte array (buffer) to // transfer incoming and outgoing APDU header // and data bytes between card and CAD buffer = apdu.getBuffer( ); | 在applet被成功选择之后,JCRE向此方法发送进入的APDU。 APDU对象被JCRE拥有和维护。它封装了底层的基本传输协议 (如ISO7816-3规定的T0或T1 )的细节并提供了通用接口。 |
// verify that if the applet can accept this // APDU message if(buffer[ISO.OFFSET_CLA] !== Wallet_CLA) ISOException.throwIt (ISO.SW_CLA_NOT_SUPPORTED); | 当发生错误时,applet可能决定中止过程,并抛出一个包含状态字(SW1 SW2)的例外,状态字用于表示卡的处理状态。 |
switch (buffer[ISO.OFFSET_INS]) { case Balance: getBalance(apdu); return; case Debit: debit(apdu); return; case Deposit: deposit(apdu); return; case Validate: validate(apdu); return; default: ISOException.throwIt (ISO.SW_INS_NOT_SUPPORTED); } } // end of process method | process方法的主要功能是执行APDU规定的动作,并向终端返回正确的响应。 INS字节 指定需要执行的动作的类型 |
private void deposit(APDU apdu) { // access authentication if( ! pin.isValidated( ) ) ISOException.throwIt( ISO.SW_PIN_REQUIRED); // Lc byte denotes the number of bytes in the data // field of the command APDU byte numBytes = (byte) (buffer[ISO.OFFSET_LC]); // indicate that this APDU has incoming data and // receive data sharing from the offset // ISO.OFFSET_CDATA byte byteRead = (byte) (apdu.setIncomingAndReceive( ) ); // It is an error if the number of data bytes read does // not match the number in Lc byte if(byteRead != 1) ISOException.throwIt(ISO.SW_WRONG_LENGTH); // increase the balance by amount specified in the // data field of the command APDU balance = (byte) (balance + buffer[ISO.OFFSET_CDATA]); // return successfully return; } // end of deposit method | 参数APDU对象包含一个数据字段,它 指定 存款的金额。 在从JCRE接收到APDU对象后,APDU缓冲器中有前5个字节(CLA、INS、P1、P2、Lc/Le )可用。其在APDU缓冲器中的偏移量在类ISO中规定。因为数据字段是可选的,所以applet需要 显式通知JCRE 获取额外 的数据字节。 卡与CAD之间的通信是在命令APDU和应答APDU对之间交换的。在存钱( deposit ) 例子中, 应答APDU不包含数据字段。JCRE使用状态字0×9000 (正常处理)构成正确的应答APDU。applet开发人员不必关心构造正确的 应答APDU的细节。 当JCRE捕捉到一个exception (表示在处理指令时有错误)时,JCRE会使用Exception中包含的状态字构造 应答APDU 。 |
private void debit(APDU apdu) { // access authentication if( ! pin.isValidated( ) ) ISOException.throwIt(ISO.SW_PIN_REQUIRED); byte numBytes = (byte) (buffer[ISO.OFFSET_LC]); byte byteRead = (byte) (apdu.setIncomingAndReceive( ) ); if(byteRead != 1) ISOException.throwIt(ISO.SW_WRONG_LENGTH); // balance can not be negative if(balance - buffer[ISO.OFFSET_CDATA]) <0) ISOException.throwIt(SW_NEGATIVE_BALANCE); balance = (byte) (balance - buffer[ISO.OFFSET_CDATA]); } // end of debit method | 在debit方法中,APDU对象包含一个数据字段, 该数据字段 指定了提款的金额。 |
private void getBalance(APDU apdu) { // access authentication if(! Pin.isValidated( ) ) ISOException.throwIt(ISO.SW_PIN_REQUIRED); // inform system that the applet has finished processing // the command and the system should now prepare to // construct a response APDU which contains data field apdu.setOutgoing( ); // indicate the number of bytes in the data field apdu.setOutgoingLength(byte)1); // move the data into the APDU buffer starting at offset 0 buffer[0] = balance; // send 1 byte of data at offset 0 in the APDU buffer } // end of getBalance method | getBalance在 应答APDU的数据字段中返回钱包的余额。 因为应答APDU响应中的数据字段是可选的,所以applet 需要 显式 告诉JCRE 它要返回的数据。JCRE使用APDU对象缓冲器内的数组和正确的状态字构造一个完整的 应答APDU 。 |
private void validate(APDU apdu) { // retrieve the PIN data which requires to be validated // the user interface data is stored in the data field of the APDU byte byteRead = (byte) (apdu.setIncomingAnd Receive( ) ); // validate user interface and set the validation falg in the user interface // object to be true if the validation succeeds. // if user interface validation fails, PinException would be // thrown from pin.check( ) method pin.check(buffer, ISO.OFFSET_CDATA, byteRead); } // end of validate method } // end of class Wallet | PIN是智能卡常用的保护数据免遭越权使用的方法。 PIN中记录自上次正确的PIN确认后不成功的尝试次数。如果不成功的尝试次数超过PIN规定的允许最大尝试次数,则卡就被闭锁。 在成功选择applet后,首先必须使PIN生效,然后才能在applet上执行其它指令。 |
结论
本文首先介绍了智能卡的一些基本概念,然后阐述了Java Card 2.0的技术和开发Java Card应用程序的方法。
Java Card applet是用一般的Java 编译器编译的。 编译器的输出结果(class 文件)被输入Java Card转换器,后者 增强了Java Card 2.0 语言子集的兼容性,可执行名字解析和初始地址链接,并优化Java字节代码 使之更适合 在Java Card虚拟机上运行。转换器的输出结果被下载到Java Card上。本文对转换器和applet 安装协议没有详细说明,因为这些协议尚未标准化。我们 希望在今后的文章中能够阐述这方面的问题。
Java Card为Java世界添加了一种新的平台。Java Card的广泛采用和部署需要市场的推动、更多的应用程序和工具 的开发及时间。同时,在今后几年里,Java Card的使用数量将增加到上百万。也就是说,您不久就会使用随身携带的钱包里的小卡片来存储个人信息和下载应用程序了。