摘要:
从本文开始,我们陆续刊登一系列在智能卡上进行 java 开发的文章,以此把智能卡向您作一个基本的介绍。只要有一块智能卡、一个读卡机和一套可与智能卡通信的软件,你就可以开始进行实用应用程序的开发了。本文介绍的内容包括:遵循ISO7816标准的智能卡治理软件;如何使用Gemplus阅读器和Gemplus智能卡从智能卡中读写内存;一些利用智能卡的 存储 功能的应用程序。以后的文章将介绍不同厂家生产的智能卡,并且还将涉及智能卡的标准。
作为专题系列的第一篇,本文将主要为以后的讨论作一个铺垫工作。在此,我们将讨论一个称为OpenCard的新标准。以后的文章将涉及安全智能卡和电子钱包。最后,我们将向您介绍智能卡软件的基本框架结构。
无论是在四月份的JavaOne大会上(与该技术有关的四次演讲),还是在巨大的网络新闻站或是CNN,智能卡掀起了轩然大波。在本文里,我们将以实用智能卡实例向您展示真正的智能卡。这里的技术将答应你开发智能卡Java应用程序。
我们的讨论焦点主要集中在两种智能卡之上:存储智能卡,它可以被看作带有可选安全级别的小型可读写磁盘;处理器卡,它可以被看作带有一个输入输出端口的迷你型计算机。以后的文章将更为深入的介绍处理器卡。
本文的精华之处在于,我们将为读写智能卡创立一个简单的原形。我们要讨论一种医药处方卡,它将替你保存所有的药方清单,并检查保险、处方计划等等有用的信息。并且我们将围绕处方卡扩展开去。
在这一系列的文章之中,你会注重到伴随智能卡的一个常见主题,那就是安全性问题。这里所说的安全,主要是防止通过卡的滥插入或ActiveX 组件 等手段非法获取数据。为此目的,本文中关于读写智能卡中数据的范例将给你提供一个安全、一致且具可移植性的存储。
智能卡是什么
你可以把智能卡当作一个带有大脑的信用卡。其大脑就是一个小的嵌入式计算机芯片。这种芯片可以被 编程 执行某项任务或是 存储 一些数据,但应时刻牢记:该种芯片只不过是小型的,它的处理能力绝对比不上你的桌面计算机。
目前,智能卡仅仅用于电话、运输、银行和保健等行业,但是感谢众多的开发者,在不久的将来,我们就会看到智能卡应用于Internet应用程序之中。智能卡已经在日本和欧洲广为应用,并且在美国受到了欢迎。事实上,在美国的智能卡业界最近共发生了三件颇具意义的事件:
PC/SC
Microsoft联合其它几家公司推出了称为PC/SC的智能卡应用程序标准,用于Win32平台的个人计算机与智能卡之间实现互通信。PC/SC目前不支持非Win32的系统平台,或许Microsoft永远也不会那样做。稍后我们将对此详加讨论。
OpenCard框架
OpenCard是一个开放式的标准,它支持智能卡应用程序在网络计算机、POS、桌面和膝上计算机等平台之上实现互操作。OpenCard许诺提供100%纯Java的智能卡应用程序。通常智能卡应用程序不能完全借助于纯Java,因为它们必需与外设互通信或是利用客户之上的程序库。(当然,没有OpenCard,我们仍然能够使用100%的纯Java,但是,智能卡的开发者必需从头定制一个接口。)OpenCard还提供了一个到PC/SC的接口,使得开发者得以使用现有Win32平台上的设备。
JavaCard
JavaCard最初由Schlumberger提出,目前已被JavaSoft制定为一项标准。Schlumberger目前在市场上只提供Java智能卡,而且该公司是第一个获得JavaCard许可的厂商。作为一项极有潜力占踞统治地位的智能卡标准,JavaCard包括了标准的类库和API,使得Java小应用能够直接运行在遵循ISO7816标准的智能卡之上。JavaCard对不同的应用程序提供安全和与芯片无关的运行环境。
注重:
尽管本文集中于智能卡主题,但更为重要的是你决不应囿于这一种设备之上。从我个人角度将,我更为偏爱DallasSemicondUCtor生产的“Ibutton”设备。它象一个小型便携的信用卡一样,但是更为易用。为什么呢?因为你不再需要拿出钱包从几张卡中选出要用的那一张,Ibutton就在你的手中。对了,它是一枚指环。
尽管存在无接触型的智能卡(关于这方面的信息见下文),我认为Ibutton这样象珠宝饰品一样的产品一定会有利可图。关于Ibutton详情请见参考资料。顺便说一句,八月份在纽约的JavaInternetBusinessEXPo(JIBE)展会上,JavaCommerceTeam展示了“JavaRing”。
为什么使用智能卡?
使用智能卡有何好处呢?好,下面回答这个问题:
1.智能卡比磁卡更为可靠
2.智能卡能够存储数百倍于磁卡的数据
3.智能卡比磁卡更难于被破坏
4.智能卡可以被处理或是回收
5.智能卡在工业上可用的范围广阔,并可提供多种功能
6.智能卡与便携的电子设备兼容,比如说电话、PC或是个人数字助手(PDA)
7.智能卡在不断的发展(究竟它内部包含了一块计算机芯片)
智能卡的种类
正如前面所述,本文将集中讨论两种类型的智能卡:存储和处理器型。但是目前共有五个类型的智能卡:
存储 智能卡
处理器智能卡
电子钱包
安全卡
JavaCard
智能卡是硬件的便携部分,它必须借助于其它设备才能获取对某种显示设备或是网络的访问。可以将卡插入读卡器,这通常称为智能卡终端;也可通过射频无线电波来实现。
智能卡以下面两种方式与阅读器或是接收器互通信:
接触智能卡:当智能卡前端的芯片与阅读器相接触时,两者之间才传递信息。
无接触智能卡:这种信息传递通过天线来进行,省去了手工插入或拔出智能卡的动作。有了无接触型卡,你仅须走近阅读器,然后的信息传递将自动进行。这种类型的智能卡可用于对速度要求较高或是插入拔出并不可行的应用环境。
一些厂商对这两种类型的智能卡均已开始了生产。
为智能卡应用程序创建开发环境
为了开发智能卡应用程序,你只需要这几件东西:一个智能卡阅读器、与阅读器通信的软件、与插入阅读器的卡通信的软件,当然还要有智能卡及相关硬件设备。
智能卡阅读器
为了与智能卡相互传递信息,或是要开发一套在智能卡上运行的应用程序,你必须拥有一个阅读器。这个阅读器使应用程序能够从智能卡接收或是发出命令。在市场上有许多种类的阅读器,其中最为流行的是serial、PCCard和keyboard模型。(Keyboard模型总是不断涌现,我们期望大规模的PC厂商能在1998年六月之前直接提供这种产品。)
本文之中使用serial(串行)阅读器支持设备。一个串行阅读器与计算机的串口连接。请注重这里提供的代码同样适用于PCCard型的阅读器;许多的膝上设备内置了PCCard的端口。
每一家厂商都提供了自己的协议用来向阅读器输出数据。一旦你可以和阅读器交换信息,你就可以用一种协议与智能卡进行通信:即借助于APDU格式与智能卡互通。(关于APDU格式稍后讨论。)假如你想自己选购阅读器,请参阅参考资料中的“Gemplussmartcardreaders”。
与阅读器交换信息的软件
本文中所列举的智能卡需要配备一些面向对象的类。它们是:
遵循7816协议通信的ISO命令类
与阅读器通信的类
将数据转换为厂商特定格式的类
用于测试应用程序的软件
智能卡及相关硬件设备
正如本文前面所述,为了创建一个类似于下面例子的应用程序,你必须拥有智能卡的配套硬件和几块智能卡。你可以从Gemplus和Schlumberger等公司购买智能卡开发工具。
假如你已经拥有了阅读器,要想使用它,还要配备下面将要谈到的接口类。前面已经讲过,在与智能卡通信之前,我们必须首先和阅读器打交道。而且就象现存的许多种类的智能卡一样,如今已经有了许许多多的阅读器。
重要的智能卡标准
智能卡 应用 程序开发 中轻易使人迷惑的一点是标准协议问题。在我们的例子中基本上是应用程序与阅读器通信,然后由阅读器以一种标准协议与智能卡通信。而这种标准是国际标准化组织的7816协议。
象其它许多新技术一样,关于智能卡有许许多多令人眼花缭乱的技术标准。对于下面这些标准形成初步的了解之后,你就会大体上把握智能卡应用程序设计的基本技术要点。当然对于一些系统的非凡标准还须另外把握。我把这一向、整套标准分成“横向的”和“纵向的”两个部分:横向的标准可以被所有的应用程序所用,而纵向的标准仅仅适用于特定的系统。
横向的标准
ISO7816--描述到智能卡底层接口标准。这种标准定义智能卡阅读器和智能卡之间如何传递字节流。
PC/SC--定义运行Win3.1/Win95/NT的机器与智能卡之间通信的标准。
OCF--定义从Java应用环境和智能卡之间的通信标准,该标准完全是Java接口。(很快,OCF将答应开发者向OCF输出,并执行转换,这样开发者再无必要使用PC/SC了。)
JavaCard--描述JavaCard和它所支持的标准。
纵向的标准
Mondex--以 智能卡 形式实现的数据现金。Mondex不答应存在于卡片之外的现金。
VisaCash--这种借贷卡可以用于跟踪服务器上的卡。
PR oton--另外一种形式的电子货币卡
MPCOS-EMV--这是一种通用的智能卡,它答应你实现自己的货币或是令牌。
我自己经常感觉到迷惑不解:对于这样一块小小的塑料卡片,为什么会有如此之多的文档描述其标准,而且开发者又要把握大量的知识才能去开发它?
因为进行智能卡的开发要求高度的专业知识,所以市场上需要支持Beans的产品,这种产品应该用横向的标准去实现纵向标准的。这意味着你可以使用各式各样的横向标准组合开发出Beans产品来,就象OpenCard一样,为了实现特定的一个应用程序而采用其它几家商用标准或是其它的应用程序。
Java小应用或是Java应用程序与智能卡之间的通信
你知道了如何将所有硬件连接在一起。现在我们需要如何使用一些API,这些API可以从应用程序向 智能卡 阅读器发出命令。(阅读器然后与智能卡打交道,作为一个应用程序到智能卡之间的信息传递媒介。)智能卡阅读器移动其与智能卡接触的金属尖端传递数据。智能卡对数据做出处理之后反还给阅读器,而阅读器再将之传会应用程序。下面的问题是,在这些数据从应用程序流向智能卡之时,它们究竟处于何处?
正如前所述,应用程序与阅读器通信,而阅读器将使用上面介绍的标准再与智能卡通信。基本上,随着智能卡技术的发展,ISO推出了一套智能卡标准。该标准定义了智能卡的机械和电器特性以及与智能卡通信的标准。与ISO该标准相关的文档列在参考资料当中。不幸的是,ISO没有能够提供与阅读器相互通信的标准。因此,为了向智能卡发出一条命令,首先你要找出智能卡支持的命令集合,将该命令用ISO命令包封装,然后将这个包再以适合于阅读器的格式封装。下面的例程正是完成所有这些琐事。
application ProcotolsDataUnits(APDUs)
与 智能卡 交换信息的基本单元就是APDU包。从应用程序层传出的命令消息,加上从智能卡返回到应用程序的回应消息均称为ApplicationProcotolsDataUnits(APDU)。与智能卡和阅读器的通信以APDU形式实现。一个APDU包可以看作包含完整指令或是回应的数据包。为了提供这样的功能,在ISO7816规范家族里有一部分为APDU定义了一个良好的结构。
APDU包含如下域:
命令APDU格式
CLAINSP1P2LcDataLe
回应APDU格式
DataSW1SW2
下面是一些支持APDU传输的类及其功能描述:
Command--封装命令APDU
Response--封装回应APDU
ISOCardReader--规定一个接口。每一种设备必须实现该接口
ISOCommand--构成一个ISOCommand并从ISOCardReader接口执行该命令
与智能卡通信
Sun开发了JavaElectronicCommerceFramework(JECF),这是对核心Java平台的扩展,它答应开发者轻松快速的开发商用电子应用程序。JECF提供几种与智能卡通信的类。
我们这里讨论的 智能卡 分别有一条读取数据和写入数据的命令。它是由GemPlus提供的名为GFM卡。你也可以使用其它类型的智能卡,只要它们支持ISO7816标准并且你了解它们的APDU命令格式。当然还要做一点程序工作。GFM卡的内存是以64个比特或是8个字节为单位的。你必须用模8的算法读写数据。换句话说,你不能向GFM卡做一次长度为1k连续的写入。我们这里提供的Java代码完成这项功能。一些新的智能卡支持更大单位的读写单位。因此,为了写入字符串“0123456789”,你必须发出两条适当编址的命令。(是的,智能卡就是这样难于编程。)当 存储 型卡和处理器型卡相互融合时,这种限制也许会消失。
为了读取上面那条字符串,你应该发出“read”命令。这两种命令按照APDU的术语被格式化的写在了下面。在我们的例子中利用了Java读写智能卡。下面表中的值示出如何组成一个APDU。在GCM 编程 指南中定义了APDU的结构。
LocationofdataUpperLower
2560x000x00
10230x000x00
30930x000x00
“upper”和“lower”是地址的高位和低位字节。举几个例子可能会有助于明晰概念。这张表的upper和lower值提供了 存储 数据的确定地址。我们讨论过的向GPM896智能卡通信的两种方法是:
ISOCommand(0,0xD0,0,upper,lower,8);//Write8bytestotheaddress
ISOCommand(0,0xB0,0,upper,lower,8);//Read8bytesfromtheaddress
浏览器与 智能卡 之间的通信
三个本地接口的存在表明对主要的开发者集团缺乏了解,没有能够充分考虑如何向处于Java开发环境的开发者们提供简单易于记忆的API。假如所有的销售商均支持JNI,至少接口可保持一致,你不必化大量的时间去把接口绑定。当然你必须书写少量的本地代码,但拥有统一的接口还是有价值的。我尝试了所有三种API,最终发现JNI要比其它的更为一致,并且也最为简单、易于实现。联合HotJava一块使用是最佳的,这样你可以对与串口通信的类签名,然后安全的使用它们,比起另外两种浏览器来讲麻烦要少的多。Sun最近还公布帮助浏览器公司实现新版JDK/JVM的建议。
前面的讨论围绕如何与一个不支持JDK的硬件设备通信。在下面的几篇文章里我们将不再使用“本地接口”,而是使用一个工业标准与智能卡通信。我所选择的这项标准就是带有PC/SC桥的OpenCard标准。我将用OpenCard而不是PC/SC书写应用程序,为什么呢?
作为开发者,你拥有多种选择。通常来讲这是一件好事,但这也可能导致成本升高和功能的不一致性,尤其是当你选择的API并不被多种平台所支持时。例如,你决定用PC/SC标准书写支持Win32系列平台的 智能卡 应用程序。假如你的应用程序是一个顾客使用的应用并且将用在WeBTV之上,你的选择就是完全错误的。显示器上将会闪动一条信息:“等待WebTV的Pentium版本。”智能卡是用在Win32桌面和CE单元以外的市场之上的。那么你应该如何呢?使用OpenCard标准,抛弃缺乏一致JNI绑定的InternetExplorer。事实上,我认为将所有API抽象至单一平台是一个极为明智的选择。
实用的应用程序
现在应该开始编写一个更具实用价值的智能卡应用程序了。将来,我们去看医生,他建议采取特定的治疗,我们可能处于如下场面:
医生要求你的处方卡。
卡被插入阅读器,医生查看你以前的处方单。(对于拥有复杂医疗历史的人来说,可能需要一个专家系统。)
医生注重到与此同时另外一位医生在为你治疗,而他们俩人开出的药方有不良反应。因此目前这位医生就会向智能卡输入另外一种药方。(理想化的结果是:智能卡可以将药方传给药房。)
你现在可以将卡取出送到药房并插入那里的阅读器。
药剂师读出你的药房单。
设想药剂师比医生更为了解药性,他认为医生应该重新考虑所开出的药方。药剂师给医生打电话,而电话号码包括在智能卡的记录之中。经过短暂的讨论之后,两个人达成了一致并且更新卡中的记录。
药剂师填写你的药方单,将院方的计划信息从卡中取出,并用加密的协议与之通信。
院方验证你是真正的成员,药方出自授权医生之手,并且适时更新卡上的数据。
药剂师 向你收取5美元。
这听起来是不是一个更为安全的系统?当然是比较目前的系统而言,现在基本上是纸和笔的时代,人充当彼此相分离的计算机系统的连接点。事实上在德国,医疗 智能卡 已经在使用了。
智能处方卡的优点
比较传统的处方计划卡片而言,智能卡到底带给我们那些好处呢?下面将它们一一罗列出来:
当你要改变卡中的数据、出门旅行或是到新的医疗机构之时,可以获得以前接受治疗时开具的各种处方数据。
在紧急救护或是ER工作人员需要时及时的提供过去的医疗和护理历史。
详尽清楚的医疗历史信息,包括时间、地点和程度等具体信息。
可以有所选择的将数据提供给特定的人员,当然也可选择从何处接受数据更新。
为了对以上的优点提供支持,我们需要开发一个应用程序,答应我们用一种安全的方式向 智能卡 读写数据。假如拥有了硬件设备,我们需要将特定的字符串写入或从智能卡读出。这些要通过调用CardStrings.java中提供的方法来完成。这些原形类的提供大大便利了对智能卡的 编程 工作。我们还添加了一个Beans风格的事件处理器,用以通知用户象智能卡的插入这样的事件。(这最后一点的改进要感谢JECF的高级高级开发人员DanGuinab。)
考虑如下来自RWString.java的代码片段:
importjava.commerce.smartcards.*;PackagesformJECFtosupportsmartcards
importjava.commerce.gemplus.*;
importjava.commerce.DeviceManager.*;
import java .awt.event.*;
/**
*ReadandwriteGemplusMemorycards.Thefollowingcards
*aresupported:
*GFM4k
*/
publicclassRWString{
publicstaticvoidmain(Stringargs[]){
WriteStringws=newWriteString(args);
}
}
classWriteStringimplementsActionListener{
ISOCardReaderisoReader=null;
intportNumber;
StringdeviceName;
publicWriteString(Stringargs[]){
//Processthearguments
for(inti=0;i
CardStrings类提供了一些用于向智能卡读写字符串的方法。这些字符串在智
能卡中的存储开始于两个字节的长度域,后面跟着六个空的字节,再后面
是字符串数据。
对于那些真正开始智能卡应用 程序开发 的人来说,必须面对一项挑战,那就是修改Cardstring的方法,完成Java对象的读写。这种工作要比读写字符串来得更为灵活。假如实现了Java对象的写入,你就不必考虑所要 存储 数据的格式。我想从一个大家都说得出的简单问题入手,即将字符串存储到智能卡中。
下面的例子是一个完整的数据读写应用程序,它适用于GCR400阅读器和GemPlusGFM 智能卡 。仅需一点点的附加工作它就可以支持其它类型的阅读器。但你不必费心去做那样的工作,因为下个月里,我们将提供一个用OpenCard完成这种工作的例子。这种OpenCard将迅速成为这个行业中的标准。
packagejava.commerce.MemoryCards;
importjava.io.IOException;
importjava.commerce.smartcards.*;
import java .commerce.gemplus.*;
publicclassCardStrings{
/**
*WriteaString,sincethecardismodulo8andweare
*notusingserializedobjects--thefirsttwobytesarethe
*lengthfollowedbysixsparebytes.Stringslongerthan4096-48
*bitswillbetruncated.
*/
publicstaticvoidwriteGFMString(Strings,
ISOCardReaderisoreader){
ISOCommandwcmd;
ISOCardinputStreamwinput;
intupper,lower;
shortlength=(short)s.length();
//Lengthoftheinputstring
System.out.println("Lengthis"+length);
try{
//Writethecontrolsectionout
wcmd=newISOCommand(0,0xd0,0,0,8,0);
//Savethelength
wcmd.data.writeShort(length);
System.out.println("WriteouttheLength");
winput=wcmd.execute(isoreader,
newGemplusReaderFailureHandler());
//WritetheStringout
intwholeAmount=length/8;
//Groupsof8
intremainder=length%8;
//Remainder
//WritetheStringoutgroupsof8
for(intl=1;l<=wholeAmount;l++){System.out.println("Writing8bytesat"+(l*8));upper="(l"*8)>>8;
lower=(l*8)&0xff;
wcmd=newISOCommand(0,0xd0,upper,lower,8,0);
intindex=((l-1)*8);
wcmd.data.writeString(s.substring(index),8);
System.out.println("Writeoutbytesat"+index);
winput=wcmd.execute(isoreader,
newGemplusReaderFailureHandler());
}
//Writetheremainderout
upper=((wholeAmount+1)*8)>>8;
lower=((wholeAmount+1)*8)&0xff;
wcmd=newISOCommand(0,0xd0,upper,
lower,remainder,0);
intindex=(wholeAmount*8);
wcmd.data.writeString(s.substring(index),remainder);
winput=wcmd.execute(isoreader,
newGemplusReaderFailureHandler());
}catch(Exceptione){
System.out.println("Exception"+e);
e.printStackTrace();
}
}
/**
*ReadaString,sincethecardismodulo8andweare
*notusingserializedobjects--thefirsttwobytesarethe
*lengthfollowedbysixsparebytes.Stringslongerthan4096-48
*bitswillbetruncated.
*/
publicstaticStringreadGFMString(ISOCardReaderisoreader){
ISOCommandrcmd;
ISOCardinputStreamrinput;
intupper,lower;
shortlength;
StringBuffersb=newStringBuffer();
try{
//Readthecontrolsection
rcmd=newISOCommand(0,0xb0,0,0,0,8);
//Readthelength
rinput=rcmd.execute(isoreader,newGemplusReaderFailureHandler());
length=(short)rinput.readShort();
System.out.println("Thelengthis:"+length);
//ReadtheString
intwholeAmount=length/8;//Groupsof8
intremainder=length%8;//Remainder
//ReadtheStringingroupsof8
for(intl=1;l<=wholeAmount;l++){System.out.println("Reading8bytesat"+(l*8));upper="(l"*8)>>8;
lower=(l*8)&0xff;
rcmd=newISOCommand(0,0xb0,upper,lower,0,8);
rinput=rcmd.execute(isoreader,newGemplusReaderFailureHandler());
sb.append(rinput.readString(8));
System.out.println("Stringtothispoint:"+sb.toString());
}
//Readtheremainder
upper=((wholeAmount+1)*8)>>8;
lower=((wholeAmount+1)*8)&0xff;
rcmd=newISOCommand(0,0xb0,upper,lower,0,8);
rinput=rcmd.execute(isoreader,newGemplusReaderFailureHandler());
sb.append(rinput.readString(remainder));
System.out.println("Stringtothispoint:"+sb.toString());
}catch(Exceptione){
System.out.println("Exception"+e);
e.printStackTrace();
return(null);
}
return(sb.toString());
}
}
以后的文章
在后面的文章里面,我们将讨论这样一个问题:医生如何能够对他加入你智能卡中的条目进行安全的签名,这样处方卡才能够发挥它的实际作用。该应用程序还必须提供显示你处方卡历史信息的功能。这样的功能可以为用户自己所用,当然也可提供给 药剂师 或是紧急救护人员。再加上你过去医疗历史带有警报色彩的概要,以及外科手术和慢性病等信息,你的智能处方卡将会提供许多种应用前景。
结论
本文为这一系列关于 智能卡 的文章做了一个铺垫工作。在JavaWorld中的这一系列专题包括四篇文章。均将由本文作者提供,希望对于你在智能卡方面的应用 程序开发 有所裨益。我将利用JavaElectronicCommerceFramework(JECF)作为构筑使用程序的基础结构。JECF为简化与智能卡的通信提供了几个类。程序中的大部分代码和例子来自JECF。你可以从参考资料中找到下载JECF的方法,那之中已经包括了对智能卡的支持。
关于作者
RinaldoS.DiGiorgio是纽约SunMicrosystems的工程师。他目前正在从事将多种技术集成到HotJava和Java之中的工作,包括商业、 数据库 互连、文件夹治理和一些分析应用程序,这些分析程序主要是用于金融和新兴的基因工程市场。他把Java看作是能够降低两种计算机工业界主要成本的技术:发行和代码开发。Rinaldo的电子邮件地址是:rinaldo.digiorgio@ java world.com。
从本文开始,我们陆续刊登一系列在智能卡上进行 java 开发的文章,以此把智能卡向您作一个基本的介绍。只要有一块智能卡、一个读卡机和一套可与智能卡通信的软件,你就可以开始进行实用应用程序的开发了。本文介绍的内容包括:遵循ISO7816标准的智能卡治理软件;如何使用Gemplus阅读器和Gemplus智能卡从智能卡中读写内存;一些利用智能卡的 存储 功能的应用程序。以后的文章将介绍不同厂家生产的智能卡,并且还将涉及智能卡的标准。
作为专题系列的第一篇,本文将主要为以后的讨论作一个铺垫工作。在此,我们将讨论一个称为OpenCard的新标准。以后的文章将涉及安全智能卡和电子钱包。最后,我们将向您介绍智能卡软件的基本框架结构。
无论是在四月份的JavaOne大会上(与该技术有关的四次演讲),还是在巨大的网络新闻站或是CNN,智能卡掀起了轩然大波。在本文里,我们将以实用智能卡实例向您展示真正的智能卡。这里的技术将答应你开发智能卡Java应用程序。
我们的讨论焦点主要集中在两种智能卡之上:存储智能卡,它可以被看作带有可选安全级别的小型可读写磁盘;处理器卡,它可以被看作带有一个输入输出端口的迷你型计算机。以后的文章将更为深入的介绍处理器卡。
本文的精华之处在于,我们将为读写智能卡创立一个简单的原形。我们要讨论一种医药处方卡,它将替你保存所有的药方清单,并检查保险、处方计划等等有用的信息。并且我们将围绕处方卡扩展开去。
在这一系列的文章之中,你会注重到伴随智能卡的一个常见主题,那就是安全性问题。这里所说的安全,主要是防止通过卡的滥插入或ActiveX 组件 等手段非法获取数据。为此目的,本文中关于读写智能卡中数据的范例将给你提供一个安全、一致且具可移植性的存储。
智能卡是什么
你可以把智能卡当作一个带有大脑的信用卡。其大脑就是一个小的嵌入式计算机芯片。这种芯片可以被 编程 执行某项任务或是 存储 一些数据,但应时刻牢记:该种芯片只不过是小型的,它的处理能力绝对比不上你的桌面计算机。
目前,智能卡仅仅用于电话、运输、银行和保健等行业,但是感谢众多的开发者,在不久的将来,我们就会看到智能卡应用于Internet应用程序之中。智能卡已经在日本和欧洲广为应用,并且在美国受到了欢迎。事实上,在美国的智能卡业界最近共发生了三件颇具意义的事件:
PC/SC
Microsoft联合其它几家公司推出了称为PC/SC的智能卡应用程序标准,用于Win32平台的个人计算机与智能卡之间实现互通信。PC/SC目前不支持非Win32的系统平台,或许Microsoft永远也不会那样做。稍后我们将对此详加讨论。
OpenCard框架
OpenCard是一个开放式的标准,它支持智能卡应用程序在网络计算机、POS、桌面和膝上计算机等平台之上实现互操作。OpenCard许诺提供100%纯Java的智能卡应用程序。通常智能卡应用程序不能完全借助于纯Java,因为它们必需与外设互通信或是利用客户之上的程序库。(当然,没有OpenCard,我们仍然能够使用100%的纯Java,但是,智能卡的开发者必需从头定制一个接口。)OpenCard还提供了一个到PC/SC的接口,使得开发者得以使用现有Win32平台上的设备。
JavaCard
JavaCard最初由Schlumberger提出,目前已被JavaSoft制定为一项标准。Schlumberger目前在市场上只提供Java智能卡,而且该公司是第一个获得JavaCard许可的厂商。作为一项极有潜力占踞统治地位的智能卡标准,JavaCard包括了标准的类库和API,使得Java小应用能够直接运行在遵循ISO7816标准的智能卡之上。JavaCard对不同的应用程序提供安全和与芯片无关的运行环境。
注重:
尽管本文集中于智能卡主题,但更为重要的是你决不应囿于这一种设备之上。从我个人角度将,我更为偏爱DallasSemicondUCtor生产的“Ibutton”设备。它象一个小型便携的信用卡一样,但是更为易用。为什么呢?因为你不再需要拿出钱包从几张卡中选出要用的那一张,Ibutton就在你的手中。对了,它是一枚指环。
尽管存在无接触型的智能卡(关于这方面的信息见下文),我认为Ibutton这样象珠宝饰品一样的产品一定会有利可图。关于Ibutton详情请见参考资料。顺便说一句,八月份在纽约的JavaInternetBusinessEXPo(JIBE)展会上,JavaCommerceTeam展示了“JavaRing”。
为什么使用智能卡?
使用智能卡有何好处呢?好,下面回答这个问题:
1.智能卡比磁卡更为可靠
2.智能卡能够存储数百倍于磁卡的数据
3.智能卡比磁卡更难于被破坏
4.智能卡可以被处理或是回收
5.智能卡在工业上可用的范围广阔,并可提供多种功能
6.智能卡与便携的电子设备兼容,比如说电话、PC或是个人数字助手(PDA)
7.智能卡在不断的发展(究竟它内部包含了一块计算机芯片)
智能卡的种类
正如前面所述,本文将集中讨论两种类型的智能卡:存储和处理器型。但是目前共有五个类型的智能卡:
存储 智能卡
处理器智能卡
电子钱包
安全卡
JavaCard
智能卡是硬件的便携部分,它必须借助于其它设备才能获取对某种显示设备或是网络的访问。可以将卡插入读卡器,这通常称为智能卡终端;也可通过射频无线电波来实现。
智能卡以下面两种方式与阅读器或是接收器互通信:
接触智能卡:当智能卡前端的芯片与阅读器相接触时,两者之间才传递信息。
无接触智能卡:这种信息传递通过天线来进行,省去了手工插入或拔出智能卡的动作。有了无接触型卡,你仅须走近阅读器,然后的信息传递将自动进行。这种类型的智能卡可用于对速度要求较高或是插入拔出并不可行的应用环境。
一些厂商对这两种类型的智能卡均已开始了生产。
为智能卡应用程序创建开发环境
为了开发智能卡应用程序,你只需要这几件东西:一个智能卡阅读器、与阅读器通信的软件、与插入阅读器的卡通信的软件,当然还要有智能卡及相关硬件设备。
智能卡阅读器
为了与智能卡相互传递信息,或是要开发一套在智能卡上运行的应用程序,你必须拥有一个阅读器。这个阅读器使应用程序能够从智能卡接收或是发出命令。在市场上有许多种类的阅读器,其中最为流行的是serial、PCCard和keyboard模型。(Keyboard模型总是不断涌现,我们期望大规模的PC厂商能在1998年六月之前直接提供这种产品。)
本文之中使用serial(串行)阅读器支持设备。一个串行阅读器与计算机的串口连接。请注重这里提供的代码同样适用于PCCard型的阅读器;许多的膝上设备内置了PCCard的端口。
每一家厂商都提供了自己的协议用来向阅读器输出数据。一旦你可以和阅读器交换信息,你就可以用一种协议与智能卡进行通信:即借助于APDU格式与智能卡互通。(关于APDU格式稍后讨论。)假如你想自己选购阅读器,请参阅参考资料中的“Gemplussmartcardreaders”。
与阅读器交换信息的软件
本文中所列举的智能卡需要配备一些面向对象的类。它们是:
遵循7816协议通信的ISO命令类
与阅读器通信的类
将数据转换为厂商特定格式的类
用于测试应用程序的软件
智能卡及相关硬件设备
正如本文前面所述,为了创建一个类似于下面例子的应用程序,你必须拥有智能卡的配套硬件和几块智能卡。你可以从Gemplus和Schlumberger等公司购买智能卡开发工具。
假如你已经拥有了阅读器,要想使用它,还要配备下面将要谈到的接口类。前面已经讲过,在与智能卡通信之前,我们必须首先和阅读器打交道。而且就象现存的许多种类的智能卡一样,如今已经有了许许多多的阅读器。
重要的智能卡标准
智能卡 应用 程序开发 中轻易使人迷惑的一点是标准协议问题。在我们的例子中基本上是应用程序与阅读器通信,然后由阅读器以一种标准协议与智能卡通信。而这种标准是国际标准化组织的7816协议。
象其它许多新技术一样,关于智能卡有许许多多令人眼花缭乱的技术标准。对于下面这些标准形成初步的了解之后,你就会大体上把握智能卡应用程序设计的基本技术要点。当然对于一些系统的非凡标准还须另外把握。我把这一向、整套标准分成“横向的”和“纵向的”两个部分:横向的标准可以被所有的应用程序所用,而纵向的标准仅仅适用于特定的系统。
横向的标准
ISO7816--描述到智能卡底层接口标准。这种标准定义智能卡阅读器和智能卡之间如何传递字节流。
PC/SC--定义运行Win3.1/Win95/NT的机器与智能卡之间通信的标准。
OCF--定义从Java应用环境和智能卡之间的通信标准,该标准完全是Java接口。(很快,OCF将答应开发者向OCF输出,并执行转换,这样开发者再无必要使用PC/SC了。)
JavaCard--描述JavaCard和它所支持的标准。
纵向的标准
Mondex--以 智能卡 形式实现的数据现金。Mondex不答应存在于卡片之外的现金。
VisaCash--这种借贷卡可以用于跟踪服务器上的卡。
PR oton--另外一种形式的电子货币卡
MPCOS-EMV--这是一种通用的智能卡,它答应你实现自己的货币或是令牌。
我自己经常感觉到迷惑不解:对于这样一块小小的塑料卡片,为什么会有如此之多的文档描述其标准,而且开发者又要把握大量的知识才能去开发它?
因为进行智能卡的开发要求高度的专业知识,所以市场上需要支持Beans的产品,这种产品应该用横向的标准去实现纵向标准的。这意味着你可以使用各式各样的横向标准组合开发出Beans产品来,就象OpenCard一样,为了实现特定的一个应用程序而采用其它几家商用标准或是其它的应用程序。
Java小应用或是Java应用程序与智能卡之间的通信
你知道了如何将所有硬件连接在一起。现在我们需要如何使用一些API,这些API可以从应用程序向 智能卡 阅读器发出命令。(阅读器然后与智能卡打交道,作为一个应用程序到智能卡之间的信息传递媒介。)智能卡阅读器移动其与智能卡接触的金属尖端传递数据。智能卡对数据做出处理之后反还给阅读器,而阅读器再将之传会应用程序。下面的问题是,在这些数据从应用程序流向智能卡之时,它们究竟处于何处?
正如前所述,应用程序与阅读器通信,而阅读器将使用上面介绍的标准再与智能卡通信。基本上,随着智能卡技术的发展,ISO推出了一套智能卡标准。该标准定义了智能卡的机械和电器特性以及与智能卡通信的标准。与ISO该标准相关的文档列在参考资料当中。不幸的是,ISO没有能够提供与阅读器相互通信的标准。因此,为了向智能卡发出一条命令,首先你要找出智能卡支持的命令集合,将该命令用ISO命令包封装,然后将这个包再以适合于阅读器的格式封装。下面的例程正是完成所有这些琐事。
application ProcotolsDataUnits(APDUs)
与 智能卡 交换信息的基本单元就是APDU包。从应用程序层传出的命令消息,加上从智能卡返回到应用程序的回应消息均称为ApplicationProcotolsDataUnits(APDU)。与智能卡和阅读器的通信以APDU形式实现。一个APDU包可以看作包含完整指令或是回应的数据包。为了提供这样的功能,在ISO7816规范家族里有一部分为APDU定义了一个良好的结构。
APDU包含如下域:
命令APDU格式
CLAINSP1P2LcDataLe
回应APDU格式
DataSW1SW2
下面是一些支持APDU传输的类及其功能描述:
Command--封装命令APDU
Response--封装回应APDU
ISOCardReader--规定一个接口。每一种设备必须实现该接口
ISOCommand--构成一个ISOCommand并从ISOCardReader接口执行该命令
与智能卡通信
Sun开发了JavaElectronicCommerceFramework(JECF),这是对核心Java平台的扩展,它答应开发者轻松快速的开发商用电子应用程序。JECF提供几种与智能卡通信的类。
我们这里讨论的 智能卡 分别有一条读取数据和写入数据的命令。它是由GemPlus提供的名为GFM卡。你也可以使用其它类型的智能卡,只要它们支持ISO7816标准并且你了解它们的APDU命令格式。当然还要做一点程序工作。GFM卡的内存是以64个比特或是8个字节为单位的。你必须用模8的算法读写数据。换句话说,你不能向GFM卡做一次长度为1k连续的写入。我们这里提供的Java代码完成这项功能。一些新的智能卡支持更大单位的读写单位。因此,为了写入字符串“0123456789”,你必须发出两条适当编址的命令。(是的,智能卡就是这样难于编程。)当 存储 型卡和处理器型卡相互融合时,这种限制也许会消失。
为了读取上面那条字符串,你应该发出“read”命令。这两种命令按照APDU的术语被格式化的写在了下面。在我们的例子中利用了Java读写智能卡。下面表中的值示出如何组成一个APDU。在GCM 编程 指南中定义了APDU的结构。
LocationofdataUpperLower
2560x000x00
10230x000x00
30930x000x00
“upper”和“lower”是地址的高位和低位字节。举几个例子可能会有助于明晰概念。这张表的upper和lower值提供了 存储 数据的确定地址。我们讨论过的向GPM896智能卡通信的两种方法是:
ISOCommand(0,0xD0,0,upper,lower,8);//Write8bytestotheaddress
ISOCommand(0,0xB0,0,upper,lower,8);//Read8bytesfromtheaddress
浏览器与 智能卡 之间的通信
三个本地接口的存在表明对主要的开发者集团缺乏了解,没有能够充分考虑如何向处于Java开发环境的开发者们提供简单易于记忆的API。假如所有的销售商均支持JNI,至少接口可保持一致,你不必化大量的时间去把接口绑定。当然你必须书写少量的本地代码,但拥有统一的接口还是有价值的。我尝试了所有三种API,最终发现JNI要比其它的更为一致,并且也最为简单、易于实现。联合HotJava一块使用是最佳的,这样你可以对与串口通信的类签名,然后安全的使用它们,比起另外两种浏览器来讲麻烦要少的多。Sun最近还公布帮助浏览器公司实现新版JDK/JVM的建议。
前面的讨论围绕如何与一个不支持JDK的硬件设备通信。在下面的几篇文章里我们将不再使用“本地接口”,而是使用一个工业标准与智能卡通信。我所选择的这项标准就是带有PC/SC桥的OpenCard标准。我将用OpenCard而不是PC/SC书写应用程序,为什么呢?
作为开发者,你拥有多种选择。通常来讲这是一件好事,但这也可能导致成本升高和功能的不一致性,尤其是当你选择的API并不被多种平台所支持时。例如,你决定用PC/SC标准书写支持Win32系列平台的 智能卡 应用程序。假如你的应用程序是一个顾客使用的应用并且将用在WeBTV之上,你的选择就是完全错误的。显示器上将会闪动一条信息:“等待WebTV的Pentium版本。”智能卡是用在Win32桌面和CE单元以外的市场之上的。那么你应该如何呢?使用OpenCard标准,抛弃缺乏一致JNI绑定的InternetExplorer。事实上,我认为将所有API抽象至单一平台是一个极为明智的选择。
实用的应用程序
现在应该开始编写一个更具实用价值的智能卡应用程序了。将来,我们去看医生,他建议采取特定的治疗,我们可能处于如下场面:
医生要求你的处方卡。
卡被插入阅读器,医生查看你以前的处方单。(对于拥有复杂医疗历史的人来说,可能需要一个专家系统。)
医生注重到与此同时另外一位医生在为你治疗,而他们俩人开出的药方有不良反应。因此目前这位医生就会向智能卡输入另外一种药方。(理想化的结果是:智能卡可以将药方传给药房。)
你现在可以将卡取出送到药房并插入那里的阅读器。
药剂师读出你的药房单。
设想药剂师比医生更为了解药性,他认为医生应该重新考虑所开出的药方。药剂师给医生打电话,而电话号码包括在智能卡的记录之中。经过短暂的讨论之后,两个人达成了一致并且更新卡中的记录。
药剂师填写你的药方单,将院方的计划信息从卡中取出,并用加密的协议与之通信。
院方验证你是真正的成员,药方出自授权医生之手,并且适时更新卡上的数据。
药剂师 向你收取5美元。
这听起来是不是一个更为安全的系统?当然是比较目前的系统而言,现在基本上是纸和笔的时代,人充当彼此相分离的计算机系统的连接点。事实上在德国,医疗 智能卡 已经在使用了。
智能处方卡的优点
比较传统的处方计划卡片而言,智能卡到底带给我们那些好处呢?下面将它们一一罗列出来:
当你要改变卡中的数据、出门旅行或是到新的医疗机构之时,可以获得以前接受治疗时开具的各种处方数据。
在紧急救护或是ER工作人员需要时及时的提供过去的医疗和护理历史。
详尽清楚的医疗历史信息,包括时间、地点和程度等具体信息。
可以有所选择的将数据提供给特定的人员,当然也可选择从何处接受数据更新。
为了对以上的优点提供支持,我们需要开发一个应用程序,答应我们用一种安全的方式向 智能卡 读写数据。假如拥有了硬件设备,我们需要将特定的字符串写入或从智能卡读出。这些要通过调用CardStrings.java中提供的方法来完成。这些原形类的提供大大便利了对智能卡的 编程 工作。我们还添加了一个Beans风格的事件处理器,用以通知用户象智能卡的插入这样的事件。(这最后一点的改进要感谢JECF的高级高级开发人员DanGuinab。)
考虑如下来自RWString.java的代码片段:
importjava.commerce.smartcards.*;PackagesformJECFtosupportsmartcards
importjava.commerce.gemplus.*;
importjava.commerce.DeviceManager.*;
import java .awt.event.*;
/**
*ReadandwriteGemplusMemorycards.Thefollowingcards
*aresupported:
*GFM4k
*/
publicclassRWString{
publicstaticvoidmain(Stringargs[]){
WriteStringws=newWriteString(args);
}
}
classWriteStringimplementsActionListener{
ISOCardReaderisoReader=null;
intportNumber;
StringdeviceName;
publicWriteString(Stringargs[]){
//Processthearguments
for(inti=0;i
CardStrings类提供了一些用于向智能卡读写字符串的方法。这些字符串在智
能卡中的存储开始于两个字节的长度域,后面跟着六个空的字节,再后面
是字符串数据。
对于那些真正开始智能卡应用 程序开发 的人来说,必须面对一项挑战,那就是修改Cardstring的方法,完成Java对象的读写。这种工作要比读写字符串来得更为灵活。假如实现了Java对象的写入,你就不必考虑所要 存储 数据的格式。我想从一个大家都说得出的简单问题入手,即将字符串存储到智能卡中。
下面的例子是一个完整的数据读写应用程序,它适用于GCR400阅读器和GemPlusGFM 智能卡 。仅需一点点的附加工作它就可以支持其它类型的阅读器。但你不必费心去做那样的工作,因为下个月里,我们将提供一个用OpenCard完成这种工作的例子。这种OpenCard将迅速成为这个行业中的标准。
packagejava.commerce.MemoryCards;
importjava.io.IOException;
importjava.commerce.smartcards.*;
import java .commerce.gemplus.*;
publicclassCardStrings{
/**
*WriteaString,sincethecardismodulo8andweare
*notusingserializedobjects--thefirsttwobytesarethe
*lengthfollowedbysixsparebytes.Stringslongerthan4096-48
*bitswillbetruncated.
*/
publicstaticvoidwriteGFMString(Strings,
ISOCardReaderisoreader){
ISOCommandwcmd;
ISOCardinputStreamwinput;
intupper,lower;
shortlength=(short)s.length();
//Lengthoftheinputstring
System.out.println("Lengthis"+length);
try{
//Writethecontrolsectionout
wcmd=newISOCommand(0,0xd0,0,0,8,0);
//Savethelength
wcmd.data.writeShort(length);
System.out.println("WriteouttheLength");
winput=wcmd.execute(isoreader,
newGemplusReaderFailureHandler());
//WritetheStringout
intwholeAmount=length/8;
//Groupsof8
intremainder=length%8;
//Remainder
//WritetheStringoutgroupsof8
for(intl=1;l<=wholeAmount;l++){System.out.println("Writing8bytesat"+(l*8));upper="(l"*8)>>8;
lower=(l*8)&0xff;
wcmd=newISOCommand(0,0xd0,upper,lower,8,0);
intindex=((l-1)*8);
wcmd.data.writeString(s.substring(index),8);
System.out.println("Writeoutbytesat"+index);
winput=wcmd.execute(isoreader,
newGemplusReaderFailureHandler());
}
//Writetheremainderout
upper=((wholeAmount+1)*8)>>8;
lower=((wholeAmount+1)*8)&0xff;
wcmd=newISOCommand(0,0xd0,upper,
lower,remainder,0);
intindex=(wholeAmount*8);
wcmd.data.writeString(s.substring(index),remainder);
winput=wcmd.execute(isoreader,
newGemplusReaderFailureHandler());
}catch(Exceptione){
System.out.println("Exception"+e);
e.printStackTrace();
}
}
/**
*ReadaString,sincethecardismodulo8andweare
*notusingserializedobjects--thefirsttwobytesarethe
*lengthfollowedbysixsparebytes.Stringslongerthan4096-48
*bitswillbetruncated.
*/
publicstaticStringreadGFMString(ISOCardReaderisoreader){
ISOCommandrcmd;
ISOCardinputStreamrinput;
intupper,lower;
shortlength;
StringBuffersb=newStringBuffer();
try{
//Readthecontrolsection
rcmd=newISOCommand(0,0xb0,0,0,0,8);
//Readthelength
rinput=rcmd.execute(isoreader,newGemplusReaderFailureHandler());
length=(short)rinput.readShort();
System.out.println("Thelengthis:"+length);
//ReadtheString
intwholeAmount=length/8;//Groupsof8
intremainder=length%8;//Remainder
//ReadtheStringingroupsof8
for(intl=1;l<=wholeAmount;l++){System.out.println("Reading8bytesat"+(l*8));upper="(l"*8)>>8;
lower=(l*8)&0xff;
rcmd=newISOCommand(0,0xb0,upper,lower,0,8);
rinput=rcmd.execute(isoreader,newGemplusReaderFailureHandler());
sb.append(rinput.readString(8));
System.out.println("Stringtothispoint:"+sb.toString());
}
//Readtheremainder
upper=((wholeAmount+1)*8)>>8;
lower=((wholeAmount+1)*8)&0xff;
rcmd=newISOCommand(0,0xb0,upper,lower,0,8);
rinput=rcmd.execute(isoreader,newGemplusReaderFailureHandler());
sb.append(rinput.readString(remainder));
System.out.println("Stringtothispoint:"+sb.toString());
}catch(Exceptione){
System.out.println("Exception"+e);
e.printStackTrace();
return(null);
}
return(sb.toString());
}
}
以后的文章
在后面的文章里面,我们将讨论这样一个问题:医生如何能够对他加入你智能卡中的条目进行安全的签名,这样处方卡才能够发挥它的实际作用。该应用程序还必须提供显示你处方卡历史信息的功能。这样的功能可以为用户自己所用,当然也可提供给 药剂师 或是紧急救护人员。再加上你过去医疗历史带有警报色彩的概要,以及外科手术和慢性病等信息,你的智能处方卡将会提供许多种应用前景。
结论
本文为这一系列关于 智能卡 的文章做了一个铺垫工作。在JavaWorld中的这一系列专题包括四篇文章。均将由本文作者提供,希望对于你在智能卡方面的应用 程序开发 有所裨益。我将利用JavaElectronicCommerceFramework(JECF)作为构筑使用程序的基础结构。JECF为简化与智能卡的通信提供了几个类。程序中的大部分代码和例子来自JECF。你可以从参考资料中找到下载JECF的方法,那之中已经包括了对智能卡的支持。
关于作者
RinaldoS.DiGiorgio是纽约SunMicrosystems的工程师。他目前正在从事将多种技术集成到HotJava和Java之中的工作,包括商业、 数据库 互连、文件夹治理和一些分析应用程序,这些分析程序主要是用于金融和新兴的基因工程市场。他把Java看作是能够降低两种计算机工业界主要成本的技术:发行和代码开发。Rinaldo的电子邮件地址是:rinaldo.digiorgio@ java world.com。