用微控制器使用高级程序设计语言

申请号 CN97181037.0 申请日 1997-10-22 公开(公告)号 CN1183449C 公开(公告)日 2005-01-05
申请人 施卢默格系统公司; 发明人 T·J·维尔金森; S·B·古特赖; K·克里斯纳; M·A·蒙特格梅赖;
摘要 一种用于终端的 集成 电路 卡 。 集成电路卡 包括一个 存储器 ,存储器存储解释程序和具有高级程序设计语言格式的应用程序。卡的处理器被配置成用解释程序来解释执行应用程序并用卡的通信器来与终端通信。
权利要求

1.一种有一组资源限制的微控制器(10),包含:
一个存储器
一个装在存储器中的解释程序(16),它能在该组资源限 制范围内操作,该微控制器(10)的特征在于具有:
至少有一个装在该存储器中、要由该解释程序解释的应用 程序,其中该至少一个应用程序被生成
一个编译器(22),用于将高级语言源代码形式的应用程 序源程序(20)编译成一种字节代码格式的编译形式(24);
一个转换器(26),用于将该编译形式(24)后处理成适 合由该解释程序(16)解释的最小形式(27),该后处理采用包 括下述步骤的过程中的至少一个步骤:
a)记录原始字节代码中的所有跳转和它们的目的地(61);
b)将专用字节代码转换成等价通用字节代码或者相反 (63);
c)将字节代码操作数从采用标识串的引用修改到采用唯一 标识符的引用(64);
d)将该编译形式(24)中的字节代码重新编号成为适合解 释的格式的等价字节代码;以及
e)重新链接其目的地地址受转换步骤a)、b)、c)或d)影响 的跳转(67)。
2.权利要求1的微控制器(10),其中,该编译形式(24) 包括属性,该转换器(26)包含一种用于包括该解释程序(16) 所需属性而不包括该解释程序(16)所不需属性的装置(51d)。
3.权利要求1或2的微控制器(10),其中,该编译形式 (24)的格式是标准Java类文件格式,该转换器(26)接收标 准Java类文件格式的编译形式(24)作为输入,并以适合由解 释程序(16)解释的形式输出(27)。
4.权利要求1或2的微控制器(10),其中,该编译形式 (24)包括关联一个表示对象、类、域或过程的标识串,该转 换器包含一个用于将这类串映射到唯一标识符(51b)的装置 (57)。
5.权利要求4的微控制器(10),其中,每个唯一标识符 都是一个整数。
6.权利要求5的微控制器(10),其中,串到唯一标识符 的映射存储在一个串到标识符映射文件(30、32)中。
7.权利要求1、2或5的微控制器(10),其中,该高级 语言支持第一个特征集合和第一个数据类型集合,该解释程序 (16)支持第一个特征集合的一个子集和第一个数据类型集合 的一个子集,并且其中该转换器(26)要检验(51c、52)该编 译形式(24)仅含有第一个特征集合的该子集中的特征并仅含 有第一个数据类型集合的该子集中的数据类型。
8.权利要求1、2或5的微控制器(10),其中,该应用 程序被编译成这样一种编译形式(24),执行或解释该编译形 式(24)所需的资源超出在该微控制器上所能得到的资源。
9.权利要求1、2或5的微控制器(10),其中,该编译 形式(24)被设计成能在不同计算机平台上移植。
10.权利要求1、2或5的微控制器(10),其中,该解释 程序(16)被进一步配置成能在应用程序的解释期间判定该应 用程序是否符合一个从一个规则集中选择的安全标准,其中该 规则集含有至少一个从以下集合中选择的规则:
不允许该应用程序访问未授权的存储器部分,
不允许该应用程序访问未授权的微控制器资源,
其中,该应用程序由字节代码组成,执行之前要对多个字 节代码检查至少一次,以确认该字节代码的执行不侵犯安全限 制。
11.权利要求1、2或5的微控制器(10),其中,至少一 个应用程序是由包括下列步骤的过程生成的:
加载该应用程序之前,确认该应用程序不侵犯任何安全限 制;
以安全的方式加载该应用程序。
12.权利要求11的微控制器(10),其中,以安全方式加 载应用程序步骤包括从加载实体来进行加载,并包含这样的步 骤:
确认该加载实体有在该微控制器上加载应用程序的许可
13.权利要求11的微控制器(10),其中,以安全方式加 载应用程序步骤包含的步骤是:
用加载密钥来加密要被加载的该应用程序。
14.一种微控制器编程方法,其中,该微控制器有一个存 储器和一个按照一个资源限制集合进行操作的处理器,该方法 包含的步骤是:
输入用第一程序设计语言编写的应用程序(20);
将用该第一程序设计语言编写的该应用程序(20)编译(22) 成与该第一程序设计语言关联的第一中间代码(24);
其中,该第一中间代码(24)被至少一个第一中间代码虚 拟机解释;
其中编程该微控制器的方法的特征在于:
将该第一中间代码(24)转换成(26)第二中间代码(27);
其中,通过执行下述步骤中的至少一个步骤,该第二中间 代码(27)被至少一个第二中间代码虚拟机解释,这些步骤有:
a)记录原始字节代码中的所有跳转和它们的目的地(61);
b)将专用字节代码转换成等价通用字节代码或者相反 (63);
c)将字节代码操作数从采用标识串的引用修改到采用唯一 标识符的引用(64);以及
d)将采用编译格式的字节代码重新编号,使其成为采用适 合解释的格式的等价字节代码(66);以及
e)重新链接其目的地地址受转换步骤a)、b)、c)或d)影响 的跳转(67);以及
将该第二中间代码加载到该微控制器(10)的该存储器中。
15.权利要求14的微控制器(10)编程方法,其中,该转换 步骤进一步包含:
关联一个表示对象、类、域或过程的标识串并将这类串映 射到唯一标识符(51b)。
16.权利要求14或15的微控制器编程方法,其中,该映 射步骤包含将串映射到整数的步骤。
17.权利要求14或15的微控制器编程方法,其中该方法 的进一步特征在于:将该第二中间代码(27)加载进该微控制器 的存储器还包括在加载该第二中间代码(27)之前检查该第二中 间代码(27)以验证该第二中间代码(27)满足一个预定的完整性 检查,并且根据一个安全协议进行加载。
18.权利要求17的微控制器编程方法,其中该安全协议要 求在加载该第二中间代码(27)之前必须对一个特定的身份进行 确认以允许加载。
19.权利要求18的微控制器编程方法,其特征还在于提供 一个解密密钥,并且其中该安全协议要求使用对应于该解密密 钥的一个加载密钥加密该第二中间代码(27)。
20.一种微控制器(10),其具有一个存储器和一个被装入 存储器中的解释程序(16),其可操作执行作为以一种可解释 程序设计语言编写的程序(24)衍生物的衍生程序(27),该 微控制器的特征在于:
(a)该微控制器在一个资源限制集合内操作,该资源限制 包括其大小不足以允许解释执行以该可解释程序设计语言编写 的程序的存储器;
(b)该存储器包括一个可对以该可解释语言的衍生物编写 的衍生程序进行解释的解释程序,其中一个以该可解释程序设 计语言编写的程序的衍生物是从以该可解释程序设计语言编写 的该程序通过应用从下列一个规则集合中选择的至少一个规则 导出的:
将串映射到标识符;
在解释之前或解释期间进行安全检查;
在解释之前或解释期间进行结构检查;
在解释之前或解释期间进行语义检查。
21.权利要求20的微控制器(10),其中该衍生程序(27) 是类文件或类文件的衍生物。
22.权利要求20或21的微控制器(10),其特征进一步 在于:
该存储器的存储量小于1M字节。
23.权利要求20或21的微控制器(10),其中该微控制 器的特征还在于该安全检查:
接收来自一个请求者的请求以访问多个衍生程序(27)之 一的逻辑;
接收该请求之后,判断该多个衍生程序(27)之一是否满 足一个预定的规则集合;以及
基于该判断,有选择地授权给该请求者去访问该多个应用 程序之一。
24.权利要求23的微控制器(10),其中在该衍生程序(27) 被解释期间,通过判断该衍生程序(27)对其试图访问的存储 器的一个特定部分是否具有访问权限由该解释程序实行该预定 的规则。
25.权利要求20、21或24的微控制器(10),其特征还在 于该微控制器被配置成执行从具有下列成员的集合中选择的至 少一个安全检查:
在解释该衍生程序(27)期间实行预定的安全检查,从而 防止该衍生程序访问存储器的未授权部分或其他未授权的微控 制器资源;
该解释程序被配置成在执行每一字节代码之前检查每一字 节代码至少一次,以确定根据执行前和执行后的检查可执行该 字节代码;
在该衍生程序被加载进该微控制器之前对其进行检查,以 检验该衍生程序(27)的完整性,以及根据一个安全协议进行 加载。
26.权利要求25的微控制器(10),其中该安全协议要求必 须对一个特定的身份进行确认以允许将一个衍生程序(27)加 载到一个卡上。
27.权利要求25的微控制器(10),其特征还在于具有一个 解密密钥,其中该安全协议要求使用对应于该解密密钥的一个 加载密钥加密要加载的该衍生程序。
28.权利要求27的微控制器(10),其特征还在于其被配置 成提供从包括加密、解密、签字、签名验证、相互认证、传输 密钥和会话密钥的集合中所选择的密码服务。
29.权利要求20的微控制器(10),其特征还在于具有一个 文件系统,并且被配置成通过从包括以下成员的集合中选择的 一种装置提供对该文件系统的安全访问:
该微控制器具有访问控制表,用于授权对文件的读取、对 文件的写或删除一个文件;
该微控制器实行密钥确认,以建立对文件的所授权的访问; 以及
该微控制器检验持卡者身份以确定访问文件的权利。

说明书全文

发明总的来说涉及程序设计领域,更具体来说涉及用智能卡或 微控制器来使用高级程序设计语言。

用JAVA高级程序设计语言编写的软件应用程序是这样设计的, 即用JAVA编写的应用程序能不加改变地在许多不同的计算机品牌或 计算机平台上运行。这是通过以下步骤实现的。在编写JAVA应用程 序时,它被编译成含有字节代码的“类”文件,字节代码是称为JAVA 虚拟机的假想计算机的指令。这个虚拟机的实现要为每个被支持的平 台编写。当某个用户希望在选定平台上运行特定JAVA应用程序时, 从所要求的应用程序编译成的类文件就被加载到选定平台上。选定平 台的JAVA虚拟机被运行,解释类文件中的字节代码,这样实际就是 在运行JAVA应用程序。

以下参考文献描述了JAVA,本文引用它们作为参考:(1)Arnold、 Ken和Jame Gosling的《The Java Programming Language》 (Addison-Wesley出版,1996年);(2)Jame Gosling、Bill Joy 和Guy Steele的《The Java Language Specification》(Sun Micorsystems出版,1996年,web网址: http://java.sun.com/doc/language_environment/);(3)Jame Gosling和Henry McGilton的《 The Java Language Environment: A White Paper》(Sun Micorsystems出版,1995年,web网址: http://java.sun.com/doc/language_environment/);(4)Tim Lindholm和Frank Yellin的《The Java Virtual Machine Specification》(Addison-Wesley出版,1997年)。这些文章和 其它文章都描述了如何用JAVA编写程序。

为了让JAVA应用程序能在特定平台上运行,要编写一个在该平 台的限制条件内运行的JAVA虚拟机实现,还必须提供一个用于将所 要求的JAVA应用程序加载到该平台的机构,并且还是保持在该平台 的限制条件内。

常规的支持JAVA的平台通常是基于微处理器的计算机,能访问 相对大量的内存和硬盘存储空间。这种微处理器实现经常用在桌面电 脑和个人电脑中。然而,在智能卡通常使用的微控制器中却没有常规 的JAVA实现。

微控制器与微处理器的差别有许多方面。例如,微处理器一般有 一个中央处理单元,它要求一定的外部部件(例如存储器、输入控制 和输出控制)要功能正常。典型的微处理器可以访问从Mb级到Gb级 容量的存储器,用一条指令就能处理16、32或64位或更大的数据。 与微处理器不同,微控制器包括的中央处理单元、存储器和其它功能 单元都在一个半导体基片或集成电路(例如“芯片”)上。与微处理 器访问的相对较大的外部存储器相比,典型的微控制器访问的存储器 容量小得多。典型的微控制器能访问1到64Kb的内置存储器,16Kb 则非常普遍。

一般来说有三种不同类型的存储器被使用:随机存取存储器 (RAM)、只读存储器(ROM)、电可擦除可编程只读存储器(EEPROM)。 在微控制器中,每一种存储器的可用容量受到集成电路上用于每一种 存储器的空间的限制。通常,RAM占用的空间最多,处于短缺状态; ROM占用空间最少,处于充足状态;EEPROM比RAM更充足,但不及 ROM。

每一种存储器适合于不同的用途。尽管ROM最价廉,但它只适用 于没有变化的数据,诸如操作系统的代码。EEPROM用于存储断电时必 须保留的数据,但是写的速度极慢。RAM可以高速读写,但是价格高, 断电时数据丢失。

微处理器系统通常有较小的ROM和EEPROM,有1~128Mb的RAM -这是因为不受在单个集成电路设备上要受到的约束,经常能访问外 部磁盘存储器系统,后者作为大型可写、非易失性存储区,费用比 EEPROM更低。然而,微控制器通常有个0.1~2.0K的小RAM、2K~8K 的EEPROM、8K~56K的ROM。

由于所需的外部部件数量少,体积小,微控制器经常用于集成电 路卡,诸如智能卡。这种智能卡有各种形式,包括基于接触的卡和无 接触的卡,前者必须插入读卡机才能使用,后者则不必插入。事实上, 以无接触方式通讯的微控制器通常被嵌入特殊造型中,例如手表和戒 指中,实际上以符合人机工程学的引人入胜方式集成了智能卡的功 能。

由于环境要求的限制,用于智能卡的应用程序通常是用低级程序 设计语言(例如汇编语言)编写的,以节约存储空间。

法国专利公开号90 118 18描述了一种用高级程序设计语言为智 能卡编程的方法(该专利名称为“用于易于编程的微电路的便携式支 持介质和为该微电路的编程步骤”(Portable support medium for an easily programmable microcircuit and programming procedure for said microcircuit),公开号为2,667,171)。然而该专利申请 没有涉及数据安全问题,没有说明如何防止对智能卡上数据和信息的 非授权访问,也没有说明如何提供这样一种程序设计环境,使得程序 员能用诸如Java语言的功能丰富的程序设计语言来为智能卡编程, 并且还能用智能卡上的一种在智能卡执行限制内操作的解释程序来 执行该程序。

集成电路卡是一种安全、耐用、抗干扰、便于携带的数据存储设 备。集成电路卡是最个人化的个人电脑,因为其体积小,因为其具有 集成电路卡所独有的硬件和软件数据安全特点。

集成电路卡和卡上的微控制器的主要任务是保护存储在卡上的 数据。因此,自1974发明以来,出于这些相同的安全原因,智能卡 技术受到严密的保护。智能卡首先由法国的行作为信用卡使用。在 这种应用中,在基于信用卡的金融交易被认可之前,信用卡用户除了 拥有其持的卡外还必须表明其知道一个4位数的个人标识号(PIN)。 任何可能导致发现遗失或被偷信用卡上的个人标识号的信息都被堵 塞在公共传播之外。事实上,由于在这一方面没有人能分辨哪些信息 可能有用,几乎所有有关集成电路卡的信息都被隐瞒起来。

出于保密性考虑,为集成电路卡编写的应用程序具有独特的性 质。例如,每个应用程序通常只以某特定所有人或身份来标识。由于 应用程序通常是用低级程序设计语言诸如汇编语言编写的,所以应用 程序是为特定类型的微控制器编写的。由于低级程序设计语言的性 质,未经认可的应用程序可以访问集成电路卡上的数据。为特定集成 电路卡编写的程序要用特定本体来标识,使得如果两个本体想要执行 同一个编程功能,集成电路卡的微控制器上应用程序的有些部分必须 有两份拷贝。

集成电路卡系统历史上一直是封闭的系统。集成电路卡含有为了 与特定终端应用共同工作而设计的专有应用程序。集成电路卡使用时 的安全检查的内容,主要是确认卡应用程序与终端应用程序是匹配的 一对,卡上的数据是有效数据。

随着集成电路卡流行程度的提高,集成电路卡用户显然会不愿意 携带适合各个集成电路卡应用的不同集成电路卡。因此开始在一个提 供商的集成电路卡上提供多个合作应用。于是,例如一个自动柜员机 (ATM)访问卡和一个借方卡可以共存于单一的集成电路卡平台上。 不过,这仍然是一种封闭系统,因为终端和卡中的所有应用都是由一 个清楚地了解其它提供商的提供商建立的。

有关集成电路卡的信息-特别是有关如何与它们通讯以及如何 为它们编写程序的信息-的缺乏,已经阻碍了集成电路卡的普遍应 用。然而,公用数字网络(例如因特网和万维网)的出现已经为集成电 路卡的应用开放了新的领域。特别地,这已经导致需要在卡上装入不是清 楚地了解其它提供商的新的应用程序,但是不可能破坏卡的保密性。然而, 对于用低级程序设计语言编制的常规卡来说,这种做法是不切实际的。

根据本发明的一个方面,提供一种有一组资源限制的微控制器, 包含:

一个存储器;

一个装在存储器中的解释程序,它能在该组资源限制范围内操 作,该微控制器的特征在于具有:

至少有一个装在该存储器中、要由该解释程序解释的应用程序, 其中该至少一个应用程序被生成

一个编译器,用于将高级语言源代码形式的应用程序源程序编译 成一种字节代码格式的编译形式;

一个转换器,用于将该编译形式后处理成适合由该解释程序解释 的最小形式,该后处理采用包括下述步骤的过程中的至少一个步骤:

a)记录原始字节代码中的所有跳转和它们的目的地;

b)将专用字节代码转换成等价通用字节代码或者相反;

c)将字节代码操作数从采用标识串的引用修改到采用唯一标识 符的引用;

d)将该编译形式中的字节代码重新编号成为适合解释的格式的 等价字节代码;以及

e)重新链接其目的地地址受转换步骤a)、b)、c)或d)影响的跳 转。

根据本发明的另一方面,提供一种微控制器编程方法,其中,该 微控制器有一个存储器和一个按照一个资源限制集合进行操作的处 理器,该方法包含的步骤是:

输入用第一程序设计语言编写的应用程序;

将用该第一程序设计语言编写的该应用程序编译成与该第一程 序设计语言关联的第一中间代码;

其中,该第一中间代码被至少一个第一中间代码虚拟机解释;

其中编程该微控制器的方法的特征在于:

将该第一中间代码转换成第二中间代码;

其中,通过执行下述步骤中的至少一个步骤,该第二中间代码被 至少一个第二中间代码虚拟机解释,这些步骤有:

a)记录原始字节代码中的所有跳转和它们的目的地;

b)将专用字节代码转换成等价通用字节代码或者相反;

c)将字节代码操作数从采用标识串的引用修改到采用唯一标识 符的引用;以及

d)将采用编译格式的字节代码重新编号,使其成为采用适合解释 的格式的等价字节代码;以及

e)重新链接其目的地地址受转换步骤a)、b)、c)或d)影响的跳 转;以及

将该第二中间代码加载到该微控制器的该存储器中。

根据本发明的第三方面,提供一种微控制器,其具有一个存储器 和一个被装入存储器中的解释程序,其可操作执行作为以一种可解释 程序设计语言编写的程序衍生物的衍生程序,该微控制器的特征在 于:

(a)该微控制器在一个资源限制集合内操作,该资源限制包括其 大小不足以允许解释执行以该可解释程序设计语言编写的程序的存 储器;

(b)该存储器包括一个可对以该可解释语言的衍生物编写的衍生 程序进行解释的解释程序,其中一个以该可解释程序设计语言编写的 程序的衍生物是从以该可解释程序设计语言编写的该程序通过应用 从下列一个规则集合中选择的至少一个规则导出的:

将串映射到标识符;

在解释之前或解释期间进行安全检查;

在解释之前或解释期间进行结构检查;

在解释之前或解释期间进行语义检查。

总之,作为本发明特点的一个方面,本发明描述了一种微控制 器,例如一个集成电路卡或智能卡。出于解释的目的,在该概述部分 之后的详细描述中说明了本发明在与终端一起使用的集成电路卡中 的应用。然而本发明也可应用于小型计算机处理器的其它应用中,例 如,使用微控制器的嵌入式系统

本发明的微控制器的微处理器包括一个存储一个解释程序和至 少一个具有高级程序设计语言格式的应用程序的存储器。该存储器的 至少一部分可以位于该微处理器中。该微处理器被配置为用解释程序 解释执行该应用程序。

以下是本发明的其中一些优点。新的应用程序可以下载到智能卡 或具有微控制器的其它系统中,而不破坏智能卡或微控制器的安全 性。这些应用程序可以是由不同的公司提供的,可以用不同的终端在 不同的时间加载。安全性之所以不受破坏,是因为应用程序受到防止 未授权访问任何应用程序代码或数据的保护,这种保护是由JAVA虚 拟机提供的安全特点决定的。应用程序可以采用功能强大的主流程序 开发工具、以诸如JAVA和Eiffel的高级语言进行编制。新的应用程 序能在数小时内迅速成型并下载到智能卡,无须借助软屏蔽。

采用微控制器的嵌入式系统通过使用本发明,也能获得许多这些 优点,来下载新的应用程序、进行高级语言程序开发和快速成型。

在微控制器上使用诸如Java和Eiffel的高级语言的困难在于解 释程序和用这种语言产生的应用程序的编译形式都需要大量的存储 器。本发明通过在该应用程序处理链中引入一个转换器而克服了这一 困难。

本发明的实现包括以下若干内容。应用程序的高级程序设计语言 格式可以有类文件格式,可以有JAVA程序设计语言格式。该应用程 序可以包括字节代码,诸如Java字节代码。

实现本发明的系统可以包括一个编译器,用于将用高级编程语言 编写的那些应用的源程序编译成一种编译形式。本发明还可以包括一 个转换器,用于将该编译形式后处理成适合由该解释器解释的形式。

根据本发明的一个方面,该转换器可进行操作以包括该解释器所 要求的那些应用程序属性,而不包括该解释器不要求的属性。该转换 器还可以接受采取标准高级语言格式的一种编译形式作为输入,以及 产生一种适合由该解释器解释的形式的输出。在一个方面,该转换器 包括用于将标识串映射成唯一标识符(例如一个整数)的装置。这一映 射过程可以记录在一个串到标识符映射文件中。

根据本发明的解释器可以进一步限制成支持该高级语言的特征 和数据类型的全部集合的一个子集。在这种情况下,该转换器进一步 验证该应用程序的编译形式是否只包含所支持的特征和数据类型。

在一个实施例中,该转换器包括用于将编译形式的字节代码转换 成适合由该微控制器编译器解释的形式的字节代码的装置,在其转换 过程中使用从包括下列步骤中的集合中选择的至少一个步骤:记录跳 转和目的地,将特殊的字节代码转换成通用字节代码(或反之),修改 所引用的字节代码操作数以使用唯一的标识符来代替标识串,将字节 代码重新编号为等价的字节代码,以及将其目的地地址受这种转换步 骤影响的跳转重新链结。

该解释器可以配置成实现安全检查。该安全检查可以包括建立防 火墙,以及该安全检查可以包括实现一个砂箱安全模型(sandbox security model)。例如,该解释器可以确定该应用是否满足从一个 集合选择的一个安全准则,该集合包括不允许访问未授权的存储器部 分,不允许访问未授权的微处理器资源,检查字节代码的执行不会违 反一个安全限制。本发明的一个实施例也可以包括在加载一个应用程 序前的处理,双检验该应用程序不会超过任何安全限制,以及以一种 安全的方式加载该应用程序。后一步骤的例子包括检验该加载实体允 许将应用程序加载到该微控制器以及使用一个加载密钥加密要加载 的应用程序。

在另一方面,本发明是一种编程微控制器的方法,其包括步骤: 将以第一编程语言编写的一个应用程序从一个由一个编译器产生的 用于由一个第一虚拟机解释的第一中间代码转换成一个第二中间代 码,其中该第二中间代码可由一个第二虚拟机解释并被加载到该微处 理器的存储器中。

根据本发明的该方法,该转换步骤还可以进一步包括将对象, 类,域或过程的标识串关联以及将这种标识串映射到唯一标识符,例 如整数。

本发明的该方法的一个实施例还可以进一步包括下列步骤中的 至少一个步骤:记录跳转以及目的地,将特殊的字节代码转换成通用 字节代码(或反之),修改所使用的字节代码操作数以使用唯一的标识 符来代替标识串,将字节代码重新编号为等价的字节代码,以及将其 目的地地址受这种转换步骤影响的跳转重新链结。

根据本发明的另一特征,一个微控制器对一个资源限制集合进行 操作,该资源限制集合包括其存储器的大小不足以允许对以该可解释 编程语言编写的程序进行解释。该存储器包括一个可对作为一个高级 编程语言的衍生物程序进行解释的解释器,该衍生物是通过应用从一 个规则集中选择的至少一个规则而得到的,该规则集包括将标识串映 射到标识符,在解释之前或解释期间执行安全检查,在解释之前或解 释期间执行结构检查,以及在解释之前或解释期间执行语义检查。

这些安全检查也可以是一个逻辑,从一个请求者接收访问多个衍 生程序中的一个的请求。在接收到该请求时,检查该衍生程序是否满 足一个预定的规则集,并且根据该确定,有选择地授权请求者访问该 衍生程序。在通过对该衍生程序是否有权访问(该衍生程序试图要访 问的)特定的存储器部分的确定解释该衍生程序的同时,该预定的规 则可以由该解释器执行。

该微控制器可以进一步配置成在解释该衍生程序的同时,执行从 所执行的安全规则中所选择的至少一种安全检查,该解释器被配置成 在执行每一字节代码之前至少对其检查一次以确定可以根据执行前 和执行后检查执行该字节代码,以及在该衍生程序被加载进该微控制 器之前要对其进行检查以检验该衍生程序的完整性,以及根据一个安 全协议执行该加载。该安全协议可以要求必须对一个特定的身份进行 检验以允许将一个衍生程序加载进卡中。该安全协议还可以包括一个 解密密钥,要求使用一个对应于该解密密钥的加载密钥加密要加载的 衍生程序。

该微控制器可以进一步配置成提供从一个包括加密,解密,签 名,签字验证,互相认证,传送密钥以及会话密钥的集合中所选择的 密码业务。

该微控制器也可以具有一个文件系统,以及被配置成通过使该微 控制器具有用于授权对文件的读取,对文件的写入或者对文件的删除 的一个访问控制表提供对该文件系统的安全访问。另外,该微控制器 可以进行密钥确认以建立对一个文件的授权的访问或者验证卡持有 者的身份以建立对一个文件的授权的访问。

阅读以下说明和权利要求,其它优点和特点将是显而易见的。

图1是一个集成电路卡系统的框图

图2是表示对要下载到集成电路卡的JAVA应用程序进行准备的 流图。

图3是由卡类文件转换器使用并生成的文件的框图。

图4是表示应用程序类文件变换成卡类文件的框图。

图5是表示类文件转换器的工作的流图。

图6是表示字节代码的修改的流图。

图7是表示专用字节代码变换成通用字节代码的框图。

图8是表示用常量替换常量引用的框图。

图9是表示将引用用它们的更新值进行替换的框图。

图10是表示对原始字节代码重新编号的框图。

图11是表示为不同的虚拟机结构翻译原始字节代码的框图。

图12是表示将应用程序加载到集成电路卡的框图。

图13是表示在集成电路卡中执行应用程序的框图。

图14是表示ROM、RAM和EEPROM的存储器组织结构的示意图。

图15是表示卡JAVA虚拟机的总体体系结构的流图。

图16是表示在带有安全检查的卡JAVA虚拟机中的过程执行的流 图。

图17是表示卡JAVA虚拟机中的字节代码执行的流图。

图18是表示在没有安全检查的卡JAVA虚拟机中的过程执行的流 图。

图19是表示卡应用程序与身份之间的关联性的框图。

图20是表示专用运行应用程序的访问权的框图。

图21是智能卡上的微控制器的透视图。

图22是电话上的微控制器的透视图。

图23是钥匙环上的微控制器的透视图。

图24是戒指上的微控制器的透视图。

图25是汽车电路卡上的微控制器的透视图。

参见图1,构造一个集成电路卡10(例如智能卡)来提供一个高 级的、基于JAVA的多应用程序编程和执行环境。集成电路卡10有一 个通信器12a,它被配置成与终端14的终端通信器12b进行通信。在 有些实施例中,集成电路卡10是一个智能卡,带有8位的微控制器、 512字节的RAM、4K字节的EEPROM和20K字节的ROM;终端通信器 12b是常规的接触式智能卡读卡机;终端14是运行Windows NT操作 系统的常规个人电脑,该操作系统支持个人电脑智能卡(PC/SC)标 准并提供JAVA开发支持。

在有些实施例中,微控制器、存储器和通信器内置于与通常的信 用卡尺寸几乎相同的塑料卡中。在另一些实施例中,微控制器、存储 器和通信器安装在塑料卡以外的基体中,诸如首饰(例如手表、戒指 或项链)、汽车设备、电信设备(例如用户身份模(SIM)卡)、安 全设备(例如加密模块)和家用电器中。

终端14准备JAVA应用程序并用终端通信器12b将JAVA应用程 序加载到集成电路卡10。终端通信器12b是一种能在集成电路卡10 与终端14之间建立通信通道的通信设备。有些通信选择包括接触式 读卡机、通过无线频率或红外技术的无线通信,串行通信协议、分组 通讯协议、ISO 7816通讯协议,等等。

终端14也能与集成电路卡10中运行的应用程序交互作用。在有 些情况下,可以用不同的终端来实现这种目的。例如,可以用一种终 端来准备应用程序,用不同的终端来下载应用程序,用另外其它终端 来运行各种应用程序。终端可以是自动柜员机(ATM)、销售点终端、 防盗系统、付费系统、存取控制系统、或者任何其它与集成电路卡 或微控制器通信的系统。

集成电路卡10含有卡JAVA虚拟机(卡JVM)16,它被用于解释 卡10上含有的应用程序。

参见图2,JAVA应用程序20包括三个JAVA源代码文件A.java 20a、B.java 20b和C.java 20c。这些源代码文件是在JAVA应用 程序开发环境22中被准备和编译的。当JAVA应用程序20被开发环 境22编译时,就产生了应用程序类文件24,其中这些类文件A.class 24a、B.class 24b和C.class 24c分别对应它们各自的类JAVA源 代码20a、20b和20c。应用程序类文件24符合《JAVA虚拟机规范》 第4章记载的标准类文件格式(《JAVA虚拟机规范》为Tim Lindholm 和Frank Yellin所著,1996年Addison-Wesley公司出版)。这些 类文件24被馈送到卡类文件转换器26,后者将文件合并、压缩,生 成单一的卡类文件27。用常规的卡加载器28将卡类文件27装入集成 电路卡10。

参见图3,卡类文件转换器26是一个处理一组按标准JAVA类文 件格式编码的类文件24的类文件后处理器,它可选择采用一种串到 ID输入映射文件30来生成某种卡类文件格式的JAVA卡类文件27。 附录A描述了一个这种卡类文件格式,本文引为参考。此外,在有些 实施例中,卡类文件转换器26产生一个串到ID输出映射文件32,用 作卡类文件转换器的后继执行的输入。

在有些实施例中,为了使串到ID映射与以前生成的卡类文件一 致(在有多个类文件引用相同的串的情况下),卡类文件转换器26 能从串到ID输入映射文件30接受以前定义的串到ID映射。如果没 有这一个文件,各ID就由卡类文件转换器26生成。本文引为参考的 附录B描述了实现并产生串到ID输入映射文件30和串到ID输出映 射文件32的一种可能方法,并举例说明了这种映射表。

参见图4,典型的应用程序类文件24a包括类文件数据41,类常 量池42,类、域创建的、界面引用的和过程数据43,以及各种属性 数据44,它们在前文所述的《JAVA虚拟机规范》中有详细说明。注 意,属性数据44中许多对于本实施例来说并不需要,因此被卡类文 件转换器26剔除45。剔除的属性包括SourceFile、ConstantValue、 Exceptions、LineNumberTable、LocalVariableTable以及任何可 选择的售主属性。如附录A中所述的典型卡类文件27是按下列方式 从应用程序类文件24导出的。卡类文件数据46是从所有应用程序类 文件24a、24b、24c的合计类文件数据41导出的。卡类文件常量池 47是从所有应用程序类文件24a、24b、24c的合计类常量池42导出 的。卡类、域创建的、界面引用的和过程数据48是从所有应用程序 类文件24a、24b、24c的合计类、域创建的、界面引用的和过程数据 43导出的。本实施例中的卡属性数据49仅仅是从所有应用程序类文 件24a、24b、24c的合计属性数据44的代码属性导出的。

为了避免在卡中进行动态链接,在构成应用程序24的若干JAVA 类文件24a、24b、24c之间分配的所有数据被图5中流图中所示的进 程合并成一个卡类文件27。在51a,选择要处理的第一类文件。在51b 以下述方式压缩常量池42。JAVA类文件24a中引用的所有对象、类、 域、过程都被用类文件24a的常量池42中的串来标识。卡类文件转 换器26将在JAVA类文件24a中发现的常量池42压缩成一个优化版 本。这种压缩是通过将类文件常量池42中发现的所有串映射成整数 而完成的(其大小依微控制器的体系结构而定)。这些整数也被称作 ID。每个ID唯一地标识应用程序20中的某特定对象、类、域或过程。 因此,卡类文件转换器26将JAVA类文件常量池42中的串替换为其 对应的唯一ID。附录B显示一例应用程序HelloSmartCard.java,位 于附录下部的表说明的是与该应用程序的类文件的常量池中发现的 串对应的ID。本例中所用的ID都是16位无符号整数。

下一步,在51c卡类文件转换器26在输入JAVA类文件24a的代 码属性中检查不受支持的特征码。卡JVM16只支持全部JAVA字节代 码的一个子集,具体如本文引为参考的附录C中所述。因此,卡类文 件转换器26在JAVA类文件24a的代码属性中检查不受支持的字节代 码。如果在52发现任何不受支持的字节代码,卡类文件转换器设立 一个错误标志并停止转换53。附录D中作“A”标记的程序代码片断 显示了这些寄生字节代码是如何被理解的。通过要求标准JAVA开发 环境22编译带“-g”标志的应用程序22可以进行另一个层次的检查。 根据上述JAVA虚拟机规范,该选项要求JAVA编译器将关于JAVA应 用程序20中所使用变量的数据放置到类文件24a的 LocalVariableTable属性中。卡类文件转换器26利用这个数据来检 查JAVA类文件24a是否引用不被JAVA卡支持的数据类型。

下一步,卡类文件转换器26丢弃进行解释时不要求的、JAVA类 文件24a中的所有不需要的部分。JAVA类文件24a存储与在JAVA类 文件的属性节44中的类文件中的字节代码有关的数据。在45卡JVM 16进行解释时所不要求的属性,诸如SourceFile、ConstantValue、 Exceptions、LineNumberTable、LocalVariableTable可以安全地 丢弃。唯一被保留的属性是代码属性。代码属性含有对应于JAVA类 文件24a中过程的字节代码。

修改字节代码54涉及到检查类文件中每个过程的代码属性数据 44,以及修改引用JAVA类文件常量池42的条目的字节代码的操作数 以反映卡类文件常量池47中的条目。在有些实施例中,字节代码也 被修改,如下文所述。

修改字节代码54涉及五遍(两遍是可选择的),如图6中的流 图所述。原始字节代码60被发现在正被处理的JAVA类文件24a的代 码属性44中。第一遍61记录所有跳转及其在源字节代码中的目的 地。在以后进行的字节代码翻译中,有些单字节代码可能被翻译成2 个或3个字节。图7所示的例子中,字节代码ILOAD_0被用两个字节 代替:字节代码ILOAD和参数0。这一步完成后,代码大小发生变化, 这就要求对受其影响的任何跳转目的地进行调整。因此,在完成这些 转换之前,要分析原始字节代码60,找出任何跳转字节代码,记下它 们的位置和当前目的地。附录D中作“B”标记的程序代码片断显示 了如何记录这些跳转指令。附录D被本文引为参考。

一旦记录了跳转指令,如果不是正在进行可选择的字节代码翻译 62,卡类文件转换器26就可以进行第三遍64。

否则,卡类文件转换器就将专用字节代码转换成通用字节代码。 通常,翻译出的字节代码不在卡JVM16中被解释,但是却得到支持, 因为能将该字节代码转换成能被卡JVM16解释的等价字节代码(见图 7)。字节代码70可以替换为语义上等价的另一个不同字节代码72。 这通常导致将诸如ILOAD_0的专用短单字节代码翻译成更通用的形 式。例如,ILOAD_0可以被带参数0的字节代码ILOAD代替。进行这 种翻译,目的是减少由卡JVM 16翻译的字节代码的数量,由此减少 对卡JVM 16的复杂程度和代码空间的要求。附录D中作“C”标记的 程序代码片断显示了这种翻译是如何完成的。注意,这种翻译增加了 结果字节代码的大小,必须对任何受影响的跳转指令进行重新计算。

在第三遍64中,卡类文件转换器重建常量引用,方法是剔除用 于表示这些常量的串。图8表示的例子中,引用通过JAVA类文件24a 的常量池42中的索引发现的常量“18”的字节代码LDC 80可以被翻 译成BIPUSH字节代码82。在这遍中,卡类文件转换器26修改引用 JAVA类文件常量池42的条目的所有字节代码的操作数以反映它们在 卡类文件常量池47中的新位置。图9表示的例子中,一个字节代码 的参数INVOKESTATIC 90引用JAVA类文件常量池42的某个条目,该 条目被修改以反映该条目在卡类文件常量池47中的新位置。修改后 的操作数94表示这种转换。附录D中作“D”标记的程序代码片断显 示了这种修改是如何完成的。

一旦常量引用被再链接后,如果不是正在进行可选择的字节代码 修改,卡类文件转换器就继续到第五个也是最后一遍67。

否则,卡类文件转换器就将原始字节代码修改成由正使用的特定 卡JVM 16支持的某个不同的字节代码集合。一种可能的修改是将原 始字节代码60重新编号变成卡JVM 16的字节代码(见图10)。这种 重新编号方式导致原始字节代码60中的字节代码100被修改成重新 编号后的字节代码102。由值21识别的字节代码ILOAD可以被重新编 号变成由值50来识别。可以用这种修改来优化卡JVM 16中的类型测 试(在现有技术中也称为第三遍检查)。附录D中作“E”标记的程 序代码片断显示了这个实施例的一个实现。为了减少卡JVM 16解释 字节代码所需的程序空间可以进行这种修改。这种修改的实质是将字 节代码重组成卡JVM 16字节代码,使得具有类似操作数和结果的字 节代码被组合在一起,卡JVM 16的字节代码之间没有空隙。这就使 得卡JVM 16能在其执行时高效地检查卡JVM 16字节代码并确认类 型。

在有些实施例中,卡类文件转换器将原始字节代码60修改成旨 在用于不同虚拟机体系结构的不同字节代码集合,如图11中所示。 旨在在字堆栈114上使用的JAVA字节代码ILOAD 112可以被要在字 节堆栈118上使用的卡JVM 16的字节代码ILOAD_B 116替换。字堆 栈114中的一个单元要求分配4个字节的堆栈空间,而字堆栈118中 的一个单元仅要求1个字节的堆栈空间。尽管这种选择有助于提高执 行速度,其险是丧失原始字节代码所具有的安全特性。

因为前面的步骤63、64或66都可能会改变字节代码60的大小, 在67所以卡类文件转换器26只好再链接任何已经受影响的跳转指 令。由于跳转指令已经在卡类文件转换器26的第一个步骤61被记 录,所以进行这种调整的方法是将跳转目的地确定为适当的值。附录 D中作“F”标记的程序代码片断显示了这些跳转是如何确定的。

卡类文件转换器现在已经修改与待命加载的原始字节代码60等 价的字节代码68。从JAVA类文件24a到卡类文件27的转换现在完 成。

参见图5,如果在55还有类文件24等待处理,则对其余的每个 类文件重复执行前面的步骤51a、51b、51c、52、54。在56卡类文 件转换器26收集已处理过的类24的映射和修改后的字节代码,在57 将它们合并放置并生成卡类文件27。如果需要的话,卡类文件转换器 26就生成一个串到ID输出映射文件32,它含有为翻译期间在JAVA 类文件24的常量池42中遇到的串所分配的所有新ID的清单。

参见图12,终端14内的卡加载器28用标准ISO 7816命令将卡 类文件发送到集成电路卡中的加载与执行控件120。加载与执行控件 120有一个卡操作系统122,卡操作系统提供必要的系统资源,包括 支持卡文件系统124,卡文件系统可用于存储几个卡应用程序126。 有许多常规的卡加载器是用由卡操作系统122支持的低级语言编写 的。在优选实施例中,引导程序加载器是用JAVA编写的,集成电路 卡10包括运行这个应用程序的JAVA虚拟机。在本文引为参考的附录 E中表示了加载与执行控件120的一个Java实现。加载与执行控件 120接收卡类文件26,生成在集成电路卡10的EEPROM中卡文件系统 126中存储的JAVA卡应用程序126x。多个JAVA卡应用程序126x、 126y和126z都能以这种方式存储在单一的卡中。加载与执行控件120 支持终端能用来选择哪一个Java卡应用程序立即或在下一次卡复位 时运行的命令。

参见图13,当接收到来自加载与执行控件120的一个复位或执行 命令时,卡JAVA虚拟机(卡JVM)16开始在所选JAVA卡应用程序 126z中所选类的某个预定过程(例如主过程)执行。卡JVM 16提供 JAVA卡应用程序126z访问基础卡操作系统122,操作系统用本机JAVA 过程来提供诸如I/O、EEPROM支持、文件系统、存取控制以及其它系 统功能。所用本机JAVA过程如本文引为参考的附录F中所示。

所选的JAVA卡应用程序126z用通信器12a与终端14中的适当 应用程序通信,以便建立通向终端14的通信通道。从通信器12a到 终端14的数据经过终端中的通信驱动程序132,通信驱动程序是专门 为处理通信器12a所用的通讯协议而编写的。数据然后传送到集成电 路卡驱动程序134,集成电路卡驱动程序是专门为访问正被使用的特 定集成电路卡10的能而编写的,它向终端应用程序136提供高级 软件服务。在优选实施例中,这个驱动程序是合适的PC/SC智能卡服 务提供商(SSP-Smartcard Service Provider)软件。数据然后传 送到终端应用程序136,后者必须处理由正在运行的特定卡应用程序 126z所提供的能力。命令和响应就这样在终端应用程序136和选定的 卡应用程序126z之间来回传送。终端应用程序与用户交互作用,接 收来自用户的命令-其中有些命令被传送到选定的JAVA卡应用程序 126z,接收来自选定的JAVA卡应用程序126z的响应-这些响应经处 理后被传送回用户。

参见图14,卡JVM 16是一个解释卡应用程序126x的解释器。微 控制器中影响卡JVM 16的存储器资源是卡ROM 140、卡RAM 141和 卡EEPROM 142。卡ROM 140用于存储卡JVM 16和卡操作系统122。 卡ROM 140也可用于存储固定的卡应用程序140a和类库140b。可加 载的应用程序141a、141b和库141c也可以存储在卡RAM 141中。卡 JVM 16解释卡应用程序141a、141b或140a。卡JVM 16用卡RAM来 存储VM堆栈144a和系统状态变量144b。卡JVM 16通过VM堆栈144a 来跟踪所进行的操作。由卡JVM 16创建的对象或者存储在RAM堆144c 中,或者存储在EEPROM堆146a中,或者存储在文件系统147中。

所有由卡JVM 16操纵的堆都可以作为RAM堆144c存储在卡 RAM141中,也可以作为EEPROM堆分布到卡EEPROM142。卡RAM141 也用于记录用微控制器的本机代码编写的例程所用的系统堆栈148的 状态。卡JVM 16用卡EEPROM 142来把应用程序数据存储在EEPROM堆 146a或文件系统147中。存储在文件中的应用程序数据可以通过到卡 操作系统122的接口来操纵。这种接口由卡ROM 140中存储的类库 140b提供,由卡EEPROM 142中存储的可加载类库141c提供。附录F 中描述了一个这样的接口。卡中的应用程序和数据被防火墙机构149 隔离。

为了适应微控制器上所能得到的有限资源,卡JVM 16执行JAVA 程序设计语言的一个严格的子集。结果,JAVA应用程序20编译成一 个含有JAVA字节代码的一个严格子集的类文件。这就使应用程序设 计者能以这个JAVA的严格子集编写程序并仍然保持与现有JAVA虚拟 机的兼容。由卡JVM 16解释的JAVA字节代码的语义在前面所述的 《JAVA虚拟机规范》中有描述。附录C中有由卡JVM 16解释的字节 代码子集。卡类文件转换器26检查JAVA应用程序20以确保只使用 这个子集中可用的特征,并将应用程序转换成能被卡JVM 16理解和 解释的形式。

在其它实施例中,卡JVM 16被设计成能解释字节代码116的一 个不同集合或增广集合。尽管不同的字节代码集合可能导致某些性能 上的提高,从原始JAVA字节代码具有的安全性或者从与主流JAVA开 发工具的兼容性度来说,偏离严格的JAVA子集可能并不可取。

所有卡JVM 16应用程序126都有一个定义好的入口点,入口点 由某个类和该类中的某个过程指示。这种入口点在串到ID输入映射 30中映射,并由卡类文件转换器26分配。JAVA应用程序20中的类、 过程和域是由卡类文件转换器26分配ID的。例如,对应主应用程序 类的ID可被定义为F001,对应其主过程诸如“main()V”的ID可被 定义为F002。

图15中的流图描述了卡JVM的总体执行体系。卡JVM 16的执行 从执行控件120开始,执行控件选择一个要执行的卡应用程序126z。 它接着就寻找并分配卡应用程序中的一个入口点152(某过程)供卡 JVM 16去解释。卡JVM 16解释该过程153。如果解释进行得成功154, 卡JVM 16就报告成功155,将控制返还给执行控件120。如果在进行 解释的过程中153,卡JVM 16遇到未处理的错误或异常(通常是资源 限制或安全侵犯),则卡JVM 16就停止156并将适当的错误报告给 终端14。

卡JVM 16的核心部分是一个处理字节代码的执行的子例程。图 16中的流图描述了这个子例程。该流图假定一个过程160,该子例程 执行该过程中的字节代码。子例程的开始是准备该过程的参数161。 这涉及到设置VM堆栈144a指针、VM堆栈144a限制,以及将程序 计数器设置到过程的第一个字节代码。

下一步检查过程各标志162。如果过程被标志为本地的,则过程 实际上是一个对本机过程代码(用微控制器的本地处理器代码编写的 子例程)的调用。在这种情况下,卡JVM 16为有效调用进行准备163 并返回到本机代码例程。参数可以在VM堆栈144a上或通过系统堆栈 148传送给本机过程。在进行适当的安全检查后本机过程子例程被调 用。返回时,本机过程子例程的结果(如果有结果的话)被置于VM 堆栈144a上,以便能被下一个要执行的字节代码来存取。

然后就进入了卡JVM 16的调度循环164。字节代码调度循环负责 每个字节代码的准备、执行和退役。循环结束的条件是,循环结束了 对过程160中字节代码的解释,或者卡JVM 16遇到资源限制或安全 侵犯。

如果上一个字节代码导致要转移165,则卡JVM 16准备转移 165a。提取下一个字节代码165b。为了保持以低代价处理每个字节代 码,要尽可能提取并存储常用的单元,诸如字节代码参数、长度、类 型。

为了提供程序设计语言的安全模型具有的安全性,要检验类文件 中字节代码,判断是否符合该模型。这种检查在现有技术中通常是由 一个称为字节代码检验器的程序执行的,按照《JAVA虚拟机规范》, 字节代码检验器进行四遍操作。为了提供由字节代码检验器保障的运 行时的安全性,卡JVM 16必须进行与检验器的第三、第四遍相关的 检查。这种检查可以被卡JVM 16省略,条件是由卡JVM 16解释的字 节代码60的安全性能得到保证(这几乎不可能做到)。至少,只要 对象引用不可能是假造的、VM堆栈144a和本地变量界限得到遵守, 代码安全性就可能得到保持。这就要求针对正在执行的字节代码检查 VM堆栈144a的状态。

为了实施程序设计语言的安全模型,要创建一个256字节的表, 如本文引为参考的附录G所示。该表由字节代码的编号标引。该表含 有与索引字节代码关联的类型和长度数据。其编码方式是前5位代表 类型,后3位代表长度。字节代码的类型和长度是直接从该表由字节 代码编号索引的。这种类型和长度然后被用于进行由本文引为参考的 附录H所示的检查。附录H中,这种检查始于从本文引为参考的附录 G的表中解码长度和类型。长度用于递增程序计数器。类型首先用于 执行前的检查,以保证在VM堆栈144a上的数据类型对于要执行的字 节代码来说是正确的。用256个字节的ROM存储该表,使得原始Java 字节代码能在卡JVM 16中运行并使对要加载到卡的JAVA类文件所需 的改变最少。其它JAVA字节代码可以容易地被支持,因为更新适当 的表条目是相对容易的事情。

在其它实施例中,如图10所示,过程中JAVA字节代码是这样重 新编号的,即要使得存储在附录H的表中的字节代码类型和长度数据 在重定序中是隐含的。附录H被本文引为参考。于是,必须对VM堆 栈144a的状态和正在处理的字节代码进行的检查并不涉及对表的查 找。通过执行本文引为参考的附录I中所示的几个简单比较就能进行 检查。当ROM空间非常宝贵时,最好采用这个实施例,因为它去除了 一个256字节的表。然而要向被支持字节代码的集合添加新字节代 码,必须仔细地设计周到,因为新字节代码必须适合被支持字节代码 的隐含编号方案。

在另一个实施例中,卡JVM 16为了卡JVM 16的执行速度而选择 不进行任何安全检查。这在图18的流图中表示。图18的流图与去掉 安全检查后的图16的流图相同。从安全的观点来说,这种选择是不 可取的,除非能保证字节代码是安全的。

卡JVM 16也能实施其它安全检查。如果字节代码可以引用局部 变量,卡JVM 16就检查这种引用是否有效,如果无效,就发出出错 信号。如果引用有效,卡JVM 16就将该局部变量的类型存储起来用 于将来的检查。要检查VM堆栈144a指针,看其是否仍然处于有效区 间。如果不在有效区间,就发出异常信号。要检查字节代码编号,如 果不受支持,就发出异常信号。

最后,字节代码本身被调度165d。由卡JVM 16翻译的字节代码 清单列举在附录C中。前述的《JAVA虚拟机规范》中有针对字节代码 调度之前和之后VM堆栈144a的状态对字节代码的语义的描述。注 意,有些字节代码(字节代码INVOKESTATIC、INVOKESPECIAL、 INVOKENONVIRTUAL和INVOKEVIRTUAL)可能导致重新进入卡JVM 16, 要求在子例程161的入口处开始处理。图17表示字节代码执行例程 的流图。该例程被赋予一个要执行的字节代码171。卡JVM 16执行该 字节代码所要求的指令172。如果在执行的过程中,卡JVM 16遇到某 资源限制173,它就返回一个错误156。这个错误被卡JVM 16返回给 终端16。如果字节代码执行成功,它就返回一个成功信号175。

执行之后,结果的类型被用于正确地设置VM堆栈144a(165e), 适当地在VM堆栈144a上设置数据类型标志。上一次从字节代码信息 表收集(165b)的字节代码数据被用于按照刚刚执行的字节代码来设 置VM堆栈144a的状态。

在其它实施例中,针对被执行的字节代码来设置VM堆栈144a的 输出状态在字节代码是重新编号过的情况下被简化。这种简化在本文 引为参考的附录I中表示。

在另一个实施例中,卡JVM 16为了卡JVM 16的执行速度,可能 省略对VM堆栈144a的输出状态的设置。从安全的观点来说,这种选 择并不可取-除非能保证字节代码是安全的。

在字节代码已经执行后,字节代码被退役(165f)。这涉及到使 参数从VM堆栈144a出栈。一旦字节代码的处理完成,循环164就对 过程的下一个字节代码重复。

一旦调度循环164终止,VM堆栈144a就被清空(166)。这就防 止有任何对象引用渗漏到其它卡JVM 16调用,破坏卡JVM 16的安全。 字节代码调度循环164的停止167,标志卡JVM 16已经完成了对所 请求过程的执行。

为了将集成电路卡10中的数据和应用程序互相隔离,集成电路 卡10要依靠由卡JVM 16提供的防火墙机构149。因为卡JVM实施标 准的第3和第4遍的检验器检查,它能检测到某应用程序引用另一个 应用程序所用数据或代码空间的任何企图并发出安全错误信号 (156)。例如,常规低级应用程序能将非引用数据类型变造成引用, 由此使得能访问未经授权的存储空间,侵犯安全。如果采用本发明, 则要是卡应用程序126z企图用一个非引用数据类型作为引用的话, 就会触发一个安全侵犯错误(156)。在常规JAVA中,这种受保护的 应用程序环境被称为沙箱应用程序-解释环境。

然而,这些防火墙设施并不是独立工作的。实际上,这些设施是 与下表所示的常规存取控制列表和加密机构重叠并互相加强的: 存取控制列表 虚拟机 加密   数据保护 操作前的存取控制 只访问自己的名 字空间 去向另一个程序 的数据被加密   程序保护 执行前的存取控制 只对正确的类型 执行 数据在程序的名 字空间中加密   通信保护 通道上的存取控制 在自己的名字空 间中的通道控制 只有互相验证的 各方才能通信

这些设施结合在一起,隔离集成电路卡10上的数据和应用程序, 保证每个卡应用程序126只能访问集成电路卡10的授权资源。

参见图19,当卡应用程序126执行时,卡应用程序126x、126y、 126z能被赋予特别的特权。这些特权例如确定,卡应用程序126能访 问哪些数据文件,卡应用程序126在文件系统147上能执行哪些操 作。授予卡应用程序126的特权一般是在特定的卡应用程序126z被 用户通常从终端14启动时设置的。

集成电路卡10用加密验证过程来将某身份190(例如身份190a、 190b和190c)并因此将一组特权关联到卡应用程序126的执行。特 定身份190c与卡应用程序126z的关联是在卡应用程序126z开始执 行时作出的,因此而创建一个特定的运行应用程序200,如图20所 示。身份190是一种独特可识别的文字,与某身份标志可靠关联。身 份标志(例如个人身份证号(PIN)或RSA私人密钥)是一种密钥。

参见图20,为了运行特定的卡应用程序126z,必须验证卡应用 程序126z的身份190c。身份190c是通过展示知道与身份190c关联 的身份标志而验证的。因此,为了运行卡应用程序126z,代理(例如 持卡者或希望运行该应用程序的另一个应用程序)必须显示其拥有或 知道该应用程序的定义身份的密钥。

展示拥有密钥的一种方式是简单地展示该钥本身。PIN验证就是 这种验证形式的一个例子。另一种不展示密钥本身就能显示拥有密钥 的方式是,展示有能力以该密钥来加密或解密普通文字。

因此,集成电路卡10上特定的运行应用程序200包括一个卡应 用程序126z和一个验证过的身份190c。没有这两个单元的就位,任 何卡应用程序126都不能运行。卡应用程序126z定义要执行的数据 处理操作,验证过的身份190c确定这些操作可以在什么计算对象上 执行。例如,某特定应用程序126z只能访问文件系统147中与该特 定身份190c关联的该身份C的文件202,该特定卡应用程序126z不 能访问与该特定身份190c以外的身份关联的其它文件204。

集成电路卡10可以采取其它步骤来保证应用程序和数据的隔 离。集成电路卡10具有三个软件部件集:验证过的身份的存取控制 列表、基于JAVA的虚拟机、分别保护数据文件、应用程序执行和通 信通道的一次性会话密钥。这些部件集联合起来用于一个实施例时, 能为一个实施例提供应用程序防火墙149。下面讨论每个软件部件 集,然后展示这三个集是如何共同工作来保证在集成电路卡10上隔 离应用程序和数据的。

集成电路卡10上每一个受保护的-即对其的访问要受控制的一 计算对象(例如数据文件或通信通道)都有一个关联的存取控制列表 (ACL)。(特定计算对象的)ACL上的条目的数据格式称为e-tuple:

type(类型):identity(身份):permissions(允许)

type域指示后面的(identity域中的)身份-例如某用户(例 如“John Smith”)或某小组-的类型。permissions域指示能由该 身份对计算对象进行的操作(例如读、追加数据和更新)的一个列表。

例如,假定某数据文件有ACL条目:

USER:AcmeAirlines:RAU,

则任何身份是“AcmeAirlines”的应用程序都能对该数据文件进 行读(“R”)、追加数据(“A”)和更新(“U”)。此外,该ACL 可以选择性地用于允许创建和删除数据文件。此外,该ACL可以选择 性地用于允许执行一个应用程序。

每当某计算对象被运行应用程序200访问时,访问都被卡JVM 16 截获并传送到卡操作系统122,后者判断是否有ACL与该对象关联。 如果有关联的ACL,则与运行应用程序200关联的身份190c在该ACL 上被匹配。如果该身份不存在,或者不允许该身份进行正在请求的类 型的访问,则访问被拒绝。否则,就允许进行访问。

参见图13,为了防止由于集成电路卡10与终端14之间只有单一 的数据通路而可能产生的问题,要完成通信通道的隔离,方法是在身 份验证进程中增加在卡应用程序126z与终端应用程序136之间交换 一次性会话密钥209。密钥209然后被用于加密随后在验证终端应用 程序136与验证过的卡应用程序126z之间的通信。有了一次性会话 密钥209后,无赖终端应用程序既不能“收听”在终端14与集成电 路卡10之间的验证过的通信,也不能“电子欺骗”卡应用程序去代 表无赖终端应用程序进行没有验证的操作。

卡-终端通信的加密解密,既可以由卡操作系统122处理,也可 以有卡应用程序本身126z处理。在前一种情况下,与终端14的通信 是对应用程序透明地进行加密的,信息通信解密后到达应用程序的数 据空间。在后一种情况下,卡应用程序126z选择进行加密和解密来 提供一个额外的安全层,这是因为应用程序能在数据一旦创建时就加 密数据,在数据要使用时就解密数据。否则,数据就用会话密钥209 保持在加密状态。

所以,应用程序防火墙包括三个互相加强的软件集。数据文件受 到验证过的身份存取控制列表的保护。应用程序执行空间受卡JVM 16 的保护。通信通道用一次性会话密钥209保护。

在其它实施例中,上述用于微控制器(诸如处理器12)的技术可 以控制集成电路卡以外的设备(例如汽车发动机的一部分)。在这些 应用中,微控制器提供一个小平台(即一个中央处理单元和一个存储 器,二者都位于一个半导体衬底上)来存储和执行高级程序设计语 言。多数采用微控制器的现有设备和新设计都能用本发明来提供用高 级语言为微控制器编程的能力,特别是包括了本发明对这类设备的应 用。

应用程序这个用语包括任何诸如JAVA应用程序、JAVA小应用程 序、JAVA aglets、JAVA小服务程序(servlets)、JAVA小通信程 序(commlets)、JAVA部件程序,以及其它能产生如下所述的类文 件的非JAVA程序。

类文件可以具有一个不是JAVA程序文件的源。有一些非JAVA的 程序设计语言也有用于从各自的源文件生成类文件的编译程序或汇 编程序。例如,程序设计语言Eiffel可用于用Pirmin Kalberer的 “J-Eiffel”来生成类文件。“J-Eiffel”是具有JVM字节代码生成 功能的Eiffel编译程序(web网址:http://www.spin.ch/~ kalberer/jive/index.htm)。在以下的(本文引为参考的)参考文 献中描述了一种将Ada 95到JAVA字节代码的翻译器:《用Ada 95 编写因特网程序》(原文标题为“Programming the Internet in Ada 95”,作者taft,S.Tucker,1996年发表于’96欧洲Ada学会会刊)。 Jasmin是一种JAVA字节代码汇编程序,能用于生成类文件,方法如 以下的(本文引为参考的)参考文献中所描述:《JAVA虚拟机》(原 文标题为“JAVA Virtual Machine”作者Meyer、Jon和Troy Downing, 1997年O’Reilly出版)。不管类文件的源是什么,上述描述可应用 于JAVA以外的语言以生成要被解释的代码。

图21表示一种集成电路卡或智能卡,它包括的微控制器210安 装在塑料卡212上。塑料卡212具有与典型的信用卡大致相同的形式 因素。通信器12a能用接触焊点(contact pad)214来建立通信通 道,通信器12a或者也能采用无线通信系统。

在其它实施例中,微控制器210被安装在移动式或固定的电话 220中,实际上向电话添加了智能卡的功能,如图22所示。在这些实 施例中,微控制器210被安装在能插入电话220或从电话中摘除的模 块(诸如用户身份模块(SIM-Subscribe Identity Module))中。

在其它实施例中,微控制器210被加到钥匙环230上,如图23 所示。这可用于保护安装有识别与钥匙环230上微控制器210相关联 的身份的装备的汽车的出入。

诸如手表或戒指240等贵重物品上也能以符合人机工程学的方式 安装微控制器210,如图24所示。这种实施例通常用无线通信系统来 建立通信通道,是一种对用户妨碍最小的存取控制的实现方式。

图25表示一个在汽车254的电子子系统252中安装的微控制器 210。在这个实施例中,微控制器被用于各种用途,诸如控制进出汽 车(例如检查身份或驾车人是否清醒后才启动汽车的点火系统)、通 过无线通信来付路桥费、或者与全球定位系统(GPS)交互作用来追 踪汽车的位置,如此等等。

本文描述了本发明的特定实施例,但本领域的熟练人员通过阅读 本说明书显然知道存在各种修改和替代方案。这种修改和替代并不超 出本发明的范围。

附录A

优选实施例的卡类文件格式

介绍

该卡类文件是原始类文件的一种压缩形式。卡类文件只含有解释 从原始类文件来的JAVA程序所需的语义数据。原始类文件中的间接 引用被替换为直接引用,结果产生一种紧凑的表示。

卡类文件的格式基于下列原则:

贴近标准类文件格式。卡类文件格式应当尽可能地接近标准类文 件格式。类文件中的JAVA字节代码保持不变。不改变字节代码,保 证了对它们的结构和静态约束依然可以验证是完好无缺的。

容易实现。卡类文件格式应当足够简单,以吸引JAVA虚拟机的 实现者。它必须允许不同形式但行为相当的实现。

可行性。卡类文件格式必须紧凑,以便能与智能卡技术结合。必 须既符合当今技术条件,又不失对明日创新的远见。

本文件基于题为《JAVATM虚拟机规范》[1]一书中的第4章“类文 件格式”,此后称该书为红皮书。由于本文件基于红皮书中说明的标 准类文件格式,我们只展示不同的数据。任何澄清都要以红皮书为最 终权威依据。

与标准类文件格式的主要不同之处是:

常量池被优化成只含有16位的标识符,在可能的地方,间接引 用被替换成直接引用。

原始类文件中的属性被剔除或重新分组。

JAVA卡类文件格式

本节描述JAVA卡类文件格式。每个卡类文件含有一个或许多 JAVA类型,其中的类型可以是类或界面。

卡类文件由8位字节的流组成。所有16位、32位和64位数都是 通过分别读取两个、四个和八个连续的8位字节而构造的。多字节的 数据项总是以大结尾顺序(big-endian order)存储的,其中高位字 节位于前面。在JAVA语言中,这种格式受界面java.io.DataInput 和java.io.DataOutput以及诸如java.io.DataInputStream和 java.io.DataOutputStream的类的支持。

我们定义并使用相同的数据类型集表示Java类文件数据:类型 u1、u2和u4分别代表无符号的1字节、2字节或4字节数。在JAVA 语言中,这些类型可以被诸如界面java.io.DataInput的 readUnsignedByte、readUnsignedShort和readInt的过程读取。卡 类文件格式采用用与C语言相象的结构记号编写的伪结构来表示。为 了避免与JAVA卡虚拟机类和类实例的域混淆,描述卡类文件格式的 结构的内容被称为项。与C结构的域不同,连续的项被顺序存储在卡 类文件中,没有填充或对齐。由可变长项组成的可变长表在几个类文 件结构中被使用。尽管将用类似C的数组语法来引用表项,但事实上 表是可变长度结构的流,这就意味着不可能直接将表索引翻译成表内 字节位移地址。

当我们称某数据为数组时,它实际就是一个数组。

为了区别卡类文件结构与标准类文件结构,我们增加了大写表示 法。例如我们将原始类文件中的field_info重新命名为卡类文件中 的FieldInfo。

卡类文件

卡类文件含有一个单一的CardClassFile结构:

CardClassFile{

u1 major_version;

u1 minor_version;

u2 name_index;

u2 const_size;

u2 max_class;

Cpinfo constant_pool[const_size];

ClassInfo class[max_class];

}

CardClassFile结构中的项如下:

minor_version,major_version

minor_version和major_version项的值是产生该卡类文件的卡 外JAVA卡虚拟机的低和高版本号。JAVA卡虚拟机的实现一般支持具 有给定高版本号和0到某特定minor_version的低版本号的卡类文 件。

只有Java卡论坛(Java Card Forum)才可以定义卡类文件版本 号的意思。

name_index

name_index的值必须代表一个有效的Java类名。由name_index 代表的Java类名必须与要在卡中运行的主应用程序对应的Java类名 严格相同。卡类文件含有若干类或界面,是它们构成了在卡中运行的 应用程序。由于Java允许每个类都含有一个主过程,必须有方法来 区别含有与卡应用程序对应的主过程的类文件。

const_size

const_size的值给出在卡类文件常量池中条目的数量。 constant_pool下标如果大于等于0并且小于const_size时才被视 为有效。

max_class

该值指的是卡类文件中出现的类的数目。由于Java卡中的名字 归结和链接都是由卡外Java虚拟机完成的,所以应用程序所需的所 有类文件或类都被一起放置在一个卡类文件中。

constant_pool[]

constant_pool是一个可变长度结构的表(),代表各种串常量、 类名、域名和其它在CardClassFile结构和其子结构内被引用的常 量。

卡类文件中的第一个条目是constant_pool[0]。

下标在0到const_size的constant_pool表的每个条目都是一 个可变长度的结构()。

class[]

该类是构成加载到卡上的应用程序的max_class个类的一个表。

constant pool

所有constant_pool表条目都具有以下的通用格式:

CpInfo{

u1 tag;

u1 info[];

}

constant_pool表中的每项都必须以标志cp_info条目的种类的 1字节的标签开始。该info数组的内容随标签的值而变化。有效标签 以及它们的值与红皮书中规定的一样。

每个标签字节后必须跟随两个或更多给出有关特定常量的数据 的字节。额外数据的格式随标签值而变。

当前要包括的标签只有CONSTANT_Class、CONSTANT_FiledRef、 CONSTANT_MethodRef和CONSTANT_InterfaceRef。也可以增加对其 它标签的支持,因为它们包含在规范中。

CONSTANT_Class

CONSTANT_Class_info结构用于代表一个类或一个界面:

CONSTANT_Classinfo{

u1 tag;

u2 name_index;

}

CONSTANT_Class_info结构的诸项如下所述:

tag

tag项具有值CONSTANT_Class(7)。

name_index

name_index的值必须代表一个有效的Java类名。由name_index 代表的Java类名必须是与由原始类文件的constant_pool中对应的 CONSTANT_Class条目所描述的完全相同的Java类名。

CONSTANT_Fieldref、CONSTANT_Methodref和 CONSTANT_InterfaceMethodref域、过程和界面过程由类似结构代 表:

CONSTANT_FieldrefInfo{ u1 tag; u2 class_index; u2 name_sig_index; } CONSTANT_MethodrefInfo{ u1 tag; u2 class_index; u2 name_sig_index; } CONSTANT_InterfaceMethodrefInfo{ u1 tag; u2 class_index; u2 name_sig_index; }

这些结构的诸项如下所述:

tag

CONSTANT_FieldrefInfo结构的tag项具有值 CONSTANT_Fieldref(9)。

CONSTANT_MethodrefInfo结构的tag项具有值 CONSTANT_Methodref(10)。

CONSTANT_InterfaceMethodrefInfo结构的tag项具有值 CONSTANT_InterfaceMethodref(11)。

CLass_index

class_index项的值必须代表一个有效的Java类或界面名。由 class_index代表的名必须是与由原始类文件的constant_pool中对 应的CONSTANT_Class_info条目所描述的完全相同的名。

name_sig_index

name_sig_index的值必须代表一个有效的Java名和类型。由 name_sig_index代表的名和类型必须是与由原始类文件的 constant_pool结构中的CONSTANT_NameAndType_info条目所描述 的完全相同的名和类型。

Class

每个Class都由一个定长的ClassInfo结构来描述。这种结构的 格式是:

ClassInfo{ u2 name_index; u1 max_field; u1 max_sfield; u1 max_method; u1 max_interface; u2 superclass; u2 access_flags FieldInfo field[max_field+max_sfield]; InterfaceInfo interface[max_interface]; MethodInfo method[max_method]; }

ClassInfo结构的诸项如下所述:

name_index

name_index项的值必须代表一个有效的Java类名。由 name_index代表的Java类名必须是与由原始类文件的对应 ClassFile结构所描述的完全相同的Java类名。

max_field

max_field项的值给出域表中代表由该类或界面类型说明的实例 变量的FieldInfo()结构的数目。这个值指的是卡类文件中非静态域 的数目。如果类代表一个结构则max_field的值为0。

max_sfield

max_sfield项的值给出域表中代表由该类或界面类型说明的类 变量的FieldInfo结构的数目。这个值指的是卡类文件中静态域的数 目。

max_method

max_method项的值给出过程表中MethodInfo()结构的数目。

max_interface

max_interface项的值给出该类或界面类型的直接超界面的数 目。

superclass

对于一个类来说,superclass项的值必须代表一个有效的Java 类名。由superclass代表的Java类名必须是与由原始类文件的对应 ClassFile结构所描述的完全相同的Java类名。无论该superclass 还是其任何超类都不以是一个最后类。

如果superclass的值是0,则该类必须代表类 java.lang.Object,这是唯一没有超类的类或界面。

对于界面来说,superclass的值总是代表Java类 java.lang.Object。

access_flags

access_flags项的值是用在类和界面说明中的修改符的一个屏 蔽。这些access_flags修改符和它们的值是与原始类文件的对应 ClassFile结构中的完全相同的access_flags修改符。

field[]

field表中的每个值都必须是一个定长的FieldInfo()结构,能 全面描述类或界面类型中的一个域。field表仅包括那些由该类或界 面说明过的域。它不包括代表从超类或超界面继承的域的项。

interface[]

interface数组中每个值都必须代表一个有效的界面名。由每个 条目代表的界面名必须是与由原始类文件的对应界面数组所描述的 完全相同的界面名。

method[]

method表中的每个值都必须是一个可变长的MethodInfo()结 构,能全面描述类或界面中某过程的Java虚拟机代码。

MethodInfo结构代表由该类或界面类型所说明的所有过程,包括 实例过程和一对于类来说一类(静态)过程。该method表只包括由 该类所显式说明的那些过程。界面只有一个过程,即界面 初始化过程。说method表不包括表示从超类或超界面所继承的过程 的那些项。

Fields

每个field都由一个定长的field_info结构来描述。这种结构 的格式是:

FieldInfo{

u2 name_index;

u2 signature_index;

u2 access_flags;

}

FieldInfo结构的诸项如下所述:

name_index

name_index项的值必须代表一个有效的Java域名。由 name_index代表的Java域名必须是与由原始类文件的对应 field_info结构所描述的完全相同的Java域名。

signature_index

signature_index项的值必须代表一个有效的Java域描述符。 由name_index代表的Java域描述符必须是与由原始类文件的对应 field_info结构所描述的完全相同的Java域描述符。

access_flags

access_flags项的值是用于描述对域的访问许可和域的特性的 修改符的一个屏蔽。这些access_flags修改符和它们的值是与原始 类文件的对应field_info结构中的完全相同的access_flags修改 符。

methods

每个method(过程)都由一个变长的MethodInfo结构来描述。 MethodInfo结构是一个变长的结构,含有用于一个单一Java过程、 实例初始化过程、或类或界面初始化过程的Java虚拟机指令和辅助 信息。该结构具有以下的格式:

MethodInfo{ u2 name_index; u2 signature_index; u1 max_local; u1 max_arg; u1 max_stack; u1 access_flags; u2 code_length u2 exception_length; u1 code[code_length]; {u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; }einfo[exception_length]; }

MethodInfo结构的诸项如下所述:

name_index

name_index项的值必须要么代表一个特殊内部过程名- 或者,要么代表一个有效的Java过程名。由name_index 代表的Java过程名必须是与由原始类文件的对应method_info结构 所描述的完全相同的Java过程名。

signature_index

signature_index项的值必须代表一个有效的Java过程描述 符。由signature_index代表的Java过程描述符必须是与由原始类 文件的对应method_info结构所描述的完全相同的Java过程描述 符。

max_local

max_local项的值给出该过程所用的局部变量的数目,不包括调 用时传递给过程的参数。第一个局部变量的变址值是0。对于一个单 字值最大的局部变量变址量是max_locals-1。

max_arg

max_arg项的值规定向该过程传递的参数的最大数目。

max_stack

max_stack项的值规定在执行该过程期间的任何时刻操作数堆栈 上的最多字数。

access_flags

access_flags项的值是用于描述对过程或实例初始化过程的访 问许可和过程的特性的修改符的一个屏蔽。这些access_flags修改 符和它们的值是与原始类文件的对应method_info结构中的完全相 同的access_flags修改符。

code_length

code_length项的值给出该过程的代码数组中的字节数。 code_length的值必须大于0,代码数组不得为空数组。

exception_length

exception_length项的值给出exception_info表中的条目数。

code[]

code(代码)数组给出实现该过程的Java虚拟机代码的实际字 节。当代码数组被读入字节可寻址机器上的存储器中时,如果数组的 第一个字节是在4字节边界上对齐的,则表切换(tableswitch)和 查找切换(lookupswitch)的32位位移地址是4字节对齐的;关于 代码数组对齐的重要性可参阅对这些指令的说明以了解更多情况。对 代码数组内容的具体限制很广泛,与《Java虚拟机规范》一文中所述 的相同。

einfo[]

einfo数组中的每个条目都描述代码数组中的一个异常处理程 序。每个einfo条目都含有以下各项:

start_pc、end_pc

start_pc、end_pc这两项的值表示代码数组中异常处理程序活 动时所处的范围。

start_pc的值必须是一个在某指令的操作码的代码数组内的有 效变址量。end_pc的值要么必须是在某指令的操作码的代码数组内的 有效变址量,要么必须等于code_length即代码数组的长度。 start_pc的值必须小于end_pc的值。

start_pc是包含的,end_pc是排除的,就是说,当程序计数器 位于区间[start_pc,end_pc]内时,异常处理程序必须是活动的。

handler_pc

handler_pc项的值指示异常处理程序的开始。该项的值必须是代 码数组内的一个有效变址,必须是某指令的操作码的变址,必须小于 code_length项的值。

catch_type

如果catch_type项的值是非零的,它必须代表一个有效的Java 类类型。由catch_type代表的Java类类型必须是与由原始类文件的 对应method_info结构中的catch_type所描述的完全相同的Java 类类型。该类必须是类Throwable或者它的其中一个子类。异常处理 程序仅当被抛出的异常是给定类或者它的其中一个子类的一个实例 时才被调用。

如果catch_type项的值为零,则该异常处理程序就对所有异常 而被调用。这被用于最终的执行。

Attributes

原始类文件中使用的属性要么被剔除,要么被重组,目的是为了 紧凑。

可以将预先定义的属性SourceFile、ConstantValue、 Exception、LineNumberTable和Local-VariableTable剔除,而不 牺牲Java字节代码解释所需的任何信息。

预先定义的含有特定过程的所有字节代码的属性Code,在对应的 MethodInfo结构中被移动。

对Java卡虚拟机代码的限制

过程、实例初始化过程、或者类或界面初始化过程的Java卡虚 拟机代码被存储在卡类文件的MethodInfo结构的数组代码中。对这 个代码数组的静态的和结构性的两种限制都与红皮书中所述的相 同。

Java卡虚拟机和Java卡类文件格式的限制

该版本的《Java卡虚拟机规范》在Java卡虚拟机中施加以下限 制:

每个卡的类文件常量池由CardClassFile结构()的16位 const_size域限制在65535个条目。这作为对一个单一卡类文件的 总体复杂程度的内部限制。这个计数也包括对应卡中应用程序可用的 类层次的常量池的条目。

每个过程的代码量由MethodInfo结构中的变址量大小限制在 65535个字节。

过程中的局部变量个数由MethodInfo结构()的max_local项的 大小限制在255。

类的域的个数由ClassInfo结构()的max_field和max_sfield 项的大小限制在510。

类的过程个数由ClassInfo结构()的max_method项的大小限制 在255。

操作数堆栈的大小由MethodInfo结构()的Max_stack域限制 到255个字。

附录B

串到ID输入和输出

为了卡JVM的正确操作,很重要的一点是对所说明和生成 的各ID加以正确的管理。这种管理由串到ID输入文件 String-ID INMap中的定义来控制。这个文本文件的基础 如以下所示,它说明名字的哪些地方可以用于什么用途 。这个映射的一种可能安排可以保留一些ID供卡JVM 解释器作内部使用,其余的被分配给卡JVM应用程序。

#

# String-ID INMap  文件

#

#  4000-7FFF    可供应用程序所用

#  F000-FFFE    保留供卡JVM作内部使用

#

constantBase F000       #从F000到FFFF的区域保留

                        #供卡JVM作内部使用

                        #

MainApplication         #F000-Startup类的名字

                        #(随各应用程序而变)

main()V                 #F001-Startup过程的名字

                        #(可以随应用程序而变)

java/lang/Object        #F002

java/lang/String        #F003

()V               #F004

()V             #F005

[L                      #F006

[I                      #F007

[C                      #F008

[B                      #F009

[S                      #F000A

#

constantBase FFF0       #本区保留用于简单返回类型

L                       #FFF0

V                    #FFF1

I                    #FFF2

S                    #FFF3

C                    #FFF4

B                    #FFF5

Z                    #FFF6

#

constantBase 4000    #此处开始的该空间依应用程序而定。

实际上,所有要被加载进智能卡中的应用程序都在OX 4000-OX7FFF之 间被分配一个它们自己的ID。对于每一个应用程序,这一空间是空闲的, 因为不允许所加载的应用程序访问其它应用程序。

一定要注意管理预加载的类库的ID。通过串到ID输出文件String-ID OUTMap文件的生成(可选)可有助于这些ID的管理。这种映射是以新 String-ID绑定扩充的String-ID INMap。这些绑定可以是在卡类文件 转换器应用程序终止时产生。产生String-ID OUTMap用于在卡上加 载的支持库和OS接口。该映射可用

作使用加载在该卡上的支持库和OS接口的智能卡应用程序的String- ID INMap。

作为一个例子,考虑下面的Java程序,Hello Smart Card.Java。在对其 编译时,产生一个类文件Hello Smart Card.Class。该类文件已嵌入表示该 类名、过程和类型信息的串中。根据上述的String-ID INMap,卡类文件转 换器产生一个卡类文件,该文件用卡类文件转换器分配的ID替换该类文件中 的串。表1列出了在Hello Smare Card。Class的常量池中发现的串,这些串具有各 自的卡类文件转换器分配的ID。

注:一些串(象“Java/Lang/Object”)具有预先分配的值(F002),一些 串(象“()V”)获得一个新值(4004)。

Program:HelloSmartCard.java

public class HelloSmartCard{

 public byte aVariable;

 public static void main(){

  HelloSmartCard h=new HelloSmartCard();

  h.aVariable=(byte)13;

 }

}

String-ID OUT Map的相关条目

附录C

由优选实施例中的卡JVM支持的字节代码

AALOAD AASTORE ACONST_NULL ALOAD ALOAD_0 ALOAD_1 ALOAD_2 ALOAD_3 ARETURN ARRAYLENGTH ASTORE ASTORE_0 ASTORE_1 ASTORE_2 ASTORE_3 ATHROW BALOAD BASTORE CHECKCAST DUP DUP2 DUP2_X1 DUP2_X2 DUP_X1 DUP_X2 GETFIELD GETSTATIC GOTO IADD IALOAD IAND IASTORE ICONST_0 ICONST_1 ICONST_2 ICONST_3 ICONST_4 ICONST_5 ICONST_M1 IDIV IFEQ IFGE IFGT IFLE IFLT IFNE IFNONNULL IFNULL IF_ACMPEQ IF_ACMPNE IF_ICMPEQ IF_ICMPGE IF_ICMPGT IF_ICMPLE IF_ICMPLT IF_ICMPNE IINC ILOAD ILOAD_0 ILOAD_1 ILOAD_2 ILOAD_3 IMUL INEG INSTANCEOF INT2BYTE INT2CHAR INT2SHORT INVOKEINTERFACE INVOKENONVIRTUAL INVOKESTATIC INVOKEVIRTUAL IOR IREM IRETURN ISHL ISHR ISTORE ISTORE_0 ISTORE_1 ISTORE_2 ISTORE_3 ISUB IUSHR IXOR JSR LDC1 LDC2 LOOKUPSWITCH NEW NEWARRAY NOP POP POP2 PUTFIELD PUTSTATIC RET RETURN SALOAD SASTORE SIPUSH SWAP TABLESWITCH BIPUSH

优选实施例中支持的字节代码的标准Java字节代码号

package util; /* * 由该JVM处理的实际Java字节代码的列表 * 参见Lindohlm和Yellin的文章。 * * 版权属于美国的Schlumberger Austin Products Center, * Schlumberger,Austin,Texas,1996。 */ public interface BytecodeDefn{ public static final byte j_NOP=(byte)0; public static final byte ACONST_NULL=(byte)1; public static final byte ICONST_M1=(byte)2; public static final byte ICONST_0=(byte)3; public static final byte ICONST_1=(byte)4; public static final byte ICONST_2=(byte)5; public static final byte ICONST_3=(byte)6; public static final byte ICONST_4=(byte)7; public static final byte ICONST_5=(byte)8; public static final byte BIPUSH=(byte)16; public static final byte SIPUSH=(byte)17; public static final byte LDC1=(byte)18; public static final byte LDC2=(byte)19; public static final byte ILOAD=(byte)21; public static final byte ALOAD=(byte)25; public static final byte ILOAD_0=(byte)26; public static final byte ILOAD_1=(byte)27; public static final byte ILOAD_2=(byte)28; public static final byte ILOAD_3=(byte)29; public static final byte ALOAD_0=(byte)42; public static final byte ALOAD_1=(byte)43; public static final byte ALOAD_2=(byte)44; public static final byte ALOAD_3=(byte)45; public static final byte IALOAD=(byte)46; public static final byte AALOAD=(byte)50; public static final byte BALOAD=(byte)51; public static final byte CALOAD=(byte)52; public static final byte ISTORE=(byte)54; public static final byte ASTORE=(byte)58; public static final byte ISTORE_0=(byte)59; public static final byte ISTORE_1=(byte)60; public static final byte ISTORE_2=(byte)61; public static final byte ISTORE_3=(byte)62; public static final byte ASTORE_0=(byte)75; public static final byte ASTORE_1=(byte)76; public static final byte ASTORE_2=(byte)77; public static final byte ASTORE_3=(byte)78; public static final byte IASTORE=(byte)79; public static final byte AASTORE=(byte)83; public static final byte BASTORE=(byte)84; public static final byte CASTORE=(byte)85; public static final byte POP=(byte)87; public static final byte POP2=(byte)88; public static final byte DUP=(byte)89; public static final byte DUP_X1=(byte)90; public static final byte DUP_X2=(byte)91; public static final byte DUP2=(byte)92; public static final byte DUP2_X1=(byte)93; public static final byte DUP2_X2=(byte)94; public static final byte SWAP=(byte)95; public static final byte IADD=(byte)96; public static final byte ISUB=(byte)100; public static final byte IMUL=(byte)104; public static final byte IDIV=(byte)108; public static final byte IREM=(byte)112; public static final byte INEG=(byte)116; public static final byte ISHL=(byte)120; public static final byte ISHR=(byte)122; public static final byte IUSHR=(byte)124; public static final byte IAND=(byte)126; public static final byte IOR=(byte)128; public static final byte IXOR=(byte)130; public static final byte IINC=(byte)132; public static final byte INT2BYTE=(byte)145; public static final byte INT2CHAR=(byte)146; public static final byte INT2SHORT=(byte)147; public static final byte IFEQ=(byte)153; public static final byte IFNE=(byte)154; public static final byte IFLT=(byte)155; public static final byte IFGE=(byte)156; public static final byte IFGT=(byte)157; public static final byte IFLE=(byte)158; public static final byte IF_ICMPEQ=(byte)159; public static final byte IF_ICMPNE=(byte)160; public static final byte IF_ICMPLT=(byte)161; public static final byte IF_ICMPGE=(byte)162; public static final byte IF_ICMPGT=(byte)163; public static final byte IF_ICMPLE=(byte)164; public static final byte IF_ACMPEQ=(byte)165; public static final byte IF_ACMPNE=(byte)166; public static final byte GOTO=(byte)167; public static final byte j_JSR=(byte)168; public static final byte RET=(byte)169; public static final byte TABLESWITCH=(byte)170; public static final byte LOOKUPSWITCH=(byte)171; public static final byte IRETURN=(byte)172; public static final byte ARETURN=(byte)176; public static final byte RETURN=(byte)177; public static final byte GETSTATIC=(byte)178; public static final byte PUTSTATIC=(byte)179; public static final byte GETFIELD=(byte)180; public static final byte PUTFIELD=(byte)181; public static final byte INVOKEVIRTUAL=(byte)182; public static final byte INVOKENONVIRTUAL=(byte)183; public static final byte INVOKESTATIC=(byte)184; public static final byte INVOKEINTERFACE=(byte)185; public static final byte NEW=(byte)187; public static final byte NEWARRAY=(byte)188; public static final byte ARRAYLENGTH=(byte)190; public static final byte ATHROW=(byte)191; public static final byte CHECKCAST=(byte)192; public static final byte INSTANCEOF=(byte)193; public static final byte IFNULL=(byte)198; public static final byte IFNONNULL=(byte)199;

附录D

卡类文件转换程序的字节代码转换过程

/* *再处理代码块 */ static void reprocessMethod(iMethod*imeth) { int pc; int npc; int align; bytecode*code; int codelen; int i; int opad; int npad; int apc; int high; int low; /*Codeinfo表跟踪有效Java字节代码及其对应 * 的翻译 */ code=imeth->external->code; codelen=imeth->external->code_length; jumpPos=0; align=0; /* 扫描不支持的操作码 */ for(pc=0;pc<codelen;pc=npc){ if(codeinfo[code[pc]].valid==0){ error(″Unsupported opcode%d″,code[pc]); } npc=nextPC(pc,code); } /* 扫描跳转指令并插入跳转表 */ for(pc=0;pc<codelen;pc=npc){ npc=nextPC(pc,code); if(codeinfo[code[pc]].valid==3){ insertJump(pc+1,pc,(int16)((code[pc+1]<<8)|code[pc+2])); } eise if(codeinfo[code[pc]].valid==4){ apc=pc & -4; low=(code[apc+8]<<24)|(code[apc+9]<<16) |(code[apc+10]<<8)|code[apc+11]; high=(code[apc+12]<<24)|(code[apc+13]<<16) |(code[apc+14]<<8)|code[apc+15]; for(i=0;i<high-low+1;i++){ insertJump(apc+(i*4)+18,pc, (int16)((code[apc+(i*4)+18]<<8)|code[apc+(i*4)+19])); } insertJump(apc+6,pc,(int16)((code[apc+6]<<8)|code[apc+7])); } else if(codeinfo[code[pc]].valid==5){ apc=pc & -4; low=(code[apc+8]<<24)|(code[apc+9]<<16) |(code[apc+10]<<8)|code[apc+11]; for(i=0;i<low;i++){ insertJump(aPc+(i*8)+18,pc, (int16)((code[apc+(i*8)+18]<<8)|code[apc+(i*8)+19])); } insertJump(apc+6,pc,(int16)((code[apc+6]<<8)|code[apc+7])); } } #ifdef TRANSLATE_BYTECODE /* 翻译专用操作的以生成通用操作码 */ for(pc=0;pc<codelen;pc=npc){ /* 这是一个翻译代码 */ if(codeinfo[code[pc]].valid==2){ switch(code[pc]){ case ILOAD_0: case ILOAD_1: case ILOAD_2: case ILOAD_3: insertSpace(code,&codelen,pc,1); align+=1; code[pc+1]=code[pc]-ILOAD_0; code[pc+0]=ILOAD; break: case ALOAD_0: case ALOAD_1: case ALOAD_2: case ALOAD_3: insertSpace(code,&codelen,pc,1); align+=1; code[pc+1]=code[pc]-ALOAD_0; code[pc+0]=ALOAD; break; case ISTORE_0: case ISTORE_1: case ISTORE_2: case ISTORE_3: insertSpace(code,&codelen,pc,1); align+=1; code[pc+1]=code[pc]-ISTORE_0; code[pc+0]=ISTORE; break; case ASTORE_0: case ASTORE_1: case ASTORE_2: case ASTORE_3: insertSpace(code,&codelen,pc,1); align+=1; code[pc+1]=code[pc]-ASTORE_0; code[pc+0]=ASTORE; break; case ICONST_M1: insertSpace(code,&codelan,pc,2); align+=2; code[pc+2]=255; code[pc+1]=255; code[pc+0]=SIPUSH; break; case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: insertSpace(code,&codelen ,pc,2); align+=2; code[pc+2]=code[pc]-ICONST_0; code[pc+1]=0; code[pc+0]=SIPUSH; break; case LDC1: insertSpace(code,&codelen,pc,1); align+=1; code[pc+1]=0; code[pc+0]=LDC2; break; case BIPUSH: insertSpace(code,&codelen,pc,1); align+=1; if((int8)code[pc+2]>=0){ code[pc+1]=0; } else{ code[pc+1]=255; } code[pc+0]=SIPUSH; break; case INT2SHORT: removeSpace(code,&codelen,pc,1); align-=1; npc=pc; continue; } } elseif(codeinfo[code[c]].valid==4‖codeinfo[code[pc]].valid==5){ /* 切换被对齐到4字节的边界。由于我们在插入,删除字节代码, * 这会改变切换指令的对齐。 * 因此我们必须重新调整切换中的填充来加以补偿。 */ opad=(4-(((pc+1)-align)%4))%4;/*当前切换填充 */ npad=(4-((pc+1)%4))%4; /*新切换填充 */ if(npad>opad){ insertSpace(code,&codelen,pc+1,npad-opad); align+=(npad-opad); } else if(npad<opad){ removeSpace(code,&codelen,pc+1,opad-npad); align-=(opad-npad); } } npc=nextPC(pc,code); } #endif /*重链接常量 */ for(pc=0;pc<codelen;pc=npc){ npc=nextPC(pc,code); i=(uint16)((code[pc+1]<<8)+code[pc+2]); switch(code[pc]){ case LDC2: /*′i′==通用变址*/ switch(cltem(i).type){ case CONSTANT_lnteger. i=cltem(i).v.tint; code[pc]=SIPUSH; break; case CONSTANT_String: i=buildStringlndex(i); break; default: error(″Unsupported loading of constant type″); break; } break; case NEW: case INSTANCEOF: case CHECKCAST: /*′i′==类变址 */ i=buildClasslndex(i); break; case GETFIELD: case PUTFIELD: /*′i′==域变址 */ /*i=buildFieldSignaturelndex(i);*/ i=buildStaticFieldSignaturelndex(i); break; case GETSTATIC: case PUTSTATIC: /*′i′==域变址 */ i=buildStaticFieldSignaturelndex(i); break; case INVOKEVIRTUAL: case INVOKENONVIRTUAL: case INVOKESTATIC: case INVOKEINTERFACE: /*′i′==过程签名变址 */ i=buildSignaturelndex(i); break; } /*插入应用程序的常量引用 */ code[pc+1]=(i>>8)& 0×FF; code[pc+2]=i & 0×FF; } #ifdef MODIFY_BYTECODE /*翻译代码 */ for(pc=0;pc<codelen;pc=npc){ npc=nextPC(pc,code); code[pc]=codeinfo[code[pc]].translation; } #endif /*重链接跳转*/ for(i=0;i<jumpPos;i++){ apc=jumpTable[i].at; pc=jumpTable[i].from; npc=jumpTable[i].to-pc; code[apc+0]=(npc>>8)& 0×FF; code[apc+1]=npc & 0×FF; } /*固定长度 */ imeth->external->code_length=codelen; imeth->esize=(SIZEOFMETHOD+codelen+3)&-4; }

附录E

加载和执行控制程序举例

public class Bootstrap{ ∥整个程序所使用的常量 static final byte BUFFER_LENGTH =32; static final byte ACK_SIZE =(byte)1; static final byte ACK_CODE =(byte)0; static final byte OS_HEADER_SIZE =(byte)0×10; static final byte GPOS_CREATE_FILE =(byte)0×E0; static final byte ST_INVALID_CLASS =(byte)0×C0; static final byte ST_INVALID_PARAMETER =(byte)0×A0; static final byte ST_INS_NOT_SUPPORTED =(byte)0×B0; static final byte ST_SUCCESS =(byte)0×00; static final byte ISO_COMMAND_LENGTH =(byte)5; static final byte ISO_READ_BINARY =(byte)0×B0; static final byte ISO_UPDATE_BINARY =(byte)0×D6; static final byte ISO_INIT_APPLICATION =(byte)0×F2; static final byte ISO_VERIFY_KEY =(byte)0×2A; static final byte ISO_SELECT_FILE =(byte)0×A4; static final byte ISO_CLASS =(byte)0×C0; static final byte ISO_APP_CLASS =(byte)0×F0; publc static void main(){ byte pbuffer[]=new byte[ISO_COMMAND_LENGTH]; byte dbuffer[]=new byte[BUFFER_LENGTH]; byte ackByte[]=new byte[ACK_SIZE]; ∥short fileld; short offset; byte bRetumStatus; ∥初始化通信 _OS.SendATR(); do{ ∥提取命令首部 _OS.GetMessage(pbuffer,ISO_COMMAND_LENGTH,ACK_CODE); ∥检验报文的类-仅ISO和应用程序 if((pbuffer[0]!=ISO_APP_CLASS) &&(pbuffer[0]!=ISO_CLASS)){ _OS.SendStatus(ST_INVALID_CLASS); } else{ ∥通过切换 ∥发送应答代码 ∥检验数据长度是否太大 if(pbuffer[4]>BUFFER_LENGTH){ bReturnStatus=ST_INVALID_PARAMETER; } else { switch(pbuffer[1]){ case ISO_SELECT_FILE: ∥假定长度总是2 if(pbuffer[4]!=2){ bRetumStatus=ST_INVALID_PARAMETER; } else { ∥读取数据缓冲器中的field(offset) _OS.GetMessage(dbuffer,(byte)2,pbuffer[1]); ∥将dbuffer[0..1]变换成短整数 offset=(short)((dbuffer[0]<<8)|(dbuffer[1]&0×00FF)); bReturnStatus=_OS.SelectFile(offset); } break; case ISO_VERIFY_KEY: ∥从终端取得密钥 _OS.GetMessage(dbuffer,pbuffer[4],pbuffer[1]); bReturnStatus=_OS.VerifyKey(pbuffer[3], dbuffer, pbuffer[4]); break; case ISO_INIT_APPLICATION: ∥应发送有效程序文件的ID _OS.GetMessage(dbuffer,(byte)1,pbuffer[1]); ∥通过变换从pbuffer[2..3]计算fileld(offset) offset=(short)((pbuffer[2]<<8)|(pbuffer[3]&0×00FF)); bReturnStatus=_OS.Execute(offset, dbuffer[0]); break; case GPOS_CREATE_FILE: if(pbuffer[4]=OS_HEADER_SIZE){ bReturnStatus=ST_INVALID_PARAMETER; break; } ∥接收数据 _OS.GetMessage(dbuffer,pbuffer[4],pbuffer[1]); bReturnStatus=_OS.CreateFile(dbuffer); break; case ISO_UPDATE_BINARY: _OS.GetMessage(dbuffer,pbuffer[4],pbuffer[1]); ∥通过变换从pbuffer[2..3]计算偏移 offset=(short)((pbuffer[2]<<8)|(pbuffer[3]&0×00FF)); ∥假定有文件已被选择 bReturnStatus=_OS.WriteBinaryFile(offset, pbuffer[4], dbuffer); break; case ISO_READ_BINARY: ∥通过变换从pbuffer[2..3]计算偏移 offset=(short)((pbuffer[2]<<8)|(pbuffer[3]&0×00FF)); ∥假定某文件已经被选择 bReturnStatus=_OS.ReadBinaryFile(offset, pbuffer[4], dbuffer); ∥如果成功则发送数据 ackByte[0]=pbuffer[1]; if(bReturnStatus==ST_SUCCESS){ _OS.SendMessage(ackByte,ACK_SIZE); _OS.SendMessage(dbuffer,pbuffer[4]); } break; default: bReturnStatus=ST_INS_NOT_SUPPORTED; } } _OS.SendStatus(bReturnStatus); } } while(true); } }

附录F

优选实施例中访问卡操作系统功能的过程

public class_OS{ static native byte SelecfFile (short file_id); static native byte SelectParent (); static native byte SelectCD (); static native byte SelectRoot (); static native byte CreateFile (byte file_hdr[]); static native byte DeleteFile (short file_id); ∥一般文件操作 static native byte ResetFile (); static native byte ReadByte (byte offset); static native short ReadWord (byte offset); ∥首部操作 static native byte GetFileInfo (byte file_hdr[]); ∥二进制文件支持 static native byte ReadBinaryFile (short offset, byte data_length, byte buffer[]); static native byte WriteBinary File (short offset, byte data_length, byte buffer[]); ∥记录文件支持 static native byte SelectRecord (byte record_nb, byte mode); static native byte NextRecord (); staric native byte PreviousRecord (); static native byte ReadRecord (byte record_data[], byte record_nb, byte offset, byte length); static native byte WriteRecord (byte buffer[], byte record_nb, byte offset, byte length); ∥循环文件支持 static native byte LastUpdatedRec (); ∥报文传递功能 static native byte GetMessage (byte buffer[], byte expected_length, byte ack_code); static native byte SendMessage (byte buffer[], byte data_length); static native byte SetSpeed (byte speed); ∥身份管理 static native byte CheckAccess (byte ac_action); static native byte VerifyKey (byte key_number, byte key_buffer[], byte key_length); static native byte VerifyCHV (byte CHV_number, byte CHV_buffer[], byte unblock_flag); static native byte ModifyCHV (byte CHV_number, byte old_CHV_buffe[], byte new_CHV_buffer[], byte unblock_flag); static native byte GetFileStatus (); static native byte SetFileStatus (byte file_status); static native byte GrantSupervisorMode (); static native byte RevokeSupervisorMode(); static native byte SetFileACL (byte file_acl[]); static native byte GetFileACL (byte file_acl[]); ∥文件上下文操作 static native void Init FileStatus (); static native void Backup FileStatus (); static native void RestoreFileStatus (); ∥实用程序 static native byte CompareBuffer (byte pattern_length, byte buffer_1[], byte buffer_2[]); static native short AvailableMemory (); static native void ResetCard (byte mode); static native byte SendATR (); static native byte SetDefaultATR (byte buffer[], byte length); static native byte Execute (short file_id, byte flag); ∥全局状态变量函数 static native byte Getldentity (); static native byte GetRecordNb (); static native short GetApplicationld (); static native byte GetRecordLength (); static native byte GetFileType (); static natiye short GetFileLength (); static native void SendStatus (byte status); }

附录G

字节代码属性表

划分Java字节代码成类型组

每个字节代码都被分配一个与之关联的5位类型。它用于将 代码划分为行为相似的各个集合。一般来说,这种行为反 映的是各类型的字节代码是如何在堆栈上操作的;但是, 类型0、13、14和15反映的是特殊种类的指令,如注解 栏所注明。

下表表示在每类指令被执行前后堆栈的状态。

类型  执行前                   执行后        注解

0                                               非法指令

1      stk0==int stk1==int   pop(1)

2      stk0==int               pop(1)

3      stk0==int stk1==int   pop(2)

4

5      push(1)

6      stk0==int stk1==int   pop(3)

7      stk0==int               pop(1)

8      stk0==ref               pop(1)

9      stk0==int               pop(1)

10     push(1)                   stk0<-int

11     push(1)                   stk0<-ref

12     stk0==ref               stk0<-int

13                                               DUPs,SWAP指令

14                                               INVOKE指令

15                                               FIELDS指令

16                               stk0<-ref

使用标准Java字节代码(设有重新排序)-属性查找表

/* *字节代码译码信息表。该表内容是字节代码类型和 *字节代码长度。当前支持代码0-201的所有 *标准字节代码(共202个代码) */ #define T_ 0 #define T3 1 #define T6 2 #define T1 3 #define T2 4 #define T7 5 #define T9 6 #define T8 7 #define T12 8 #define T10 9 #define T5 10 #define T11 11 #define T16 12 #define T4 13 #define T13 14 #define T14 15 #define T15 16 #define D(T,L) _BUILD_ITYPE_AND_ILENGTH(T,L) #define _BUILD_ITYPE_AND_ILENGTH(T,L) (_BUILD_ITYPE(T)|_BUILD_ILENGTH(L)) #define _BUILD_ITYPE(T) ((T)<<3) #define _BUILD_ILENGTH(L) (L) #define _GET_ITYPE(I) ((I)&0×F8) #define _GET_ILENGTH(I) ((I)&0×07) const uint8_SCODE_decodeinfo[256]={ D(T4,1), /*NOP */ D(T11,1), /*ACONST_NULL */ D(T10,1), /*ICONST_M1 */ D(T10,1), /*ICONST_0 */ D(T10,1), /*ICONST_1 */ D(T10,1), /*ICONST_2 */ D(T10,1), /*ICONST_3 */ D(T10,1), /*ICONST_4 */ D(T10,1), /*ICONST_5 */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T10,2), /*BIPUSH */ D(T10,3), /*SIPUSH */ D(T_,2), /*LDC1 */ D(T11,3), /*LDC2 */ D(T_,3), D(T5,2), /*ILOAD */ D(T_,2), D(T_,2), D(T_,2), D(T5,2), /*ALOAD */ D(T5,1), /*ILOADD_0 */ D(T5,1), /*ILOAD_1 */ D(T5,1), /*ILOAD_2 */ D(T5,1), /*ILOAD_3 */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T5,1), /*ALOAD_0 */ D(T5,1), /*ALOAD_1 */ D(T5,1), /*ALOAD_2 */ D(T5,1), /*ALOAD_3 */ D(T_,1), /*IALOAD */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), /*AALOAD */ D(T7,1), /*BALOAD */ D(T_,1), /*CALOAD */ D(T7,1), /*SALOAD */ D(T2,2), /*ISTORE */ D(T_,2), D(T_,2), D(T_,2), D(T8,2), /*ASTORE */ D(T2,1), /*ISTORE_0 */ D(T2,1), /*ISTORE_1 */ D(T2,1), /*ISTORE_2 */ D(T2,1), /*ISTORE_3 */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T8,1), /*ASTORE_0 */ D(T8,1), /*ASTORE_1 */ D(T8,1), /*ASTORE_2 */ D(T8,1), /*ASTORE_3 */ D(T_,1), /*IASTORE*/ D(T_,1), D(T_,1), D(T_,1), D(T_,1), /*AASTORE */ D(T6,1), /*BASTORE */ D(T_,1), /*CASTORE */ D(T6,1), /*SASTORE */ D(T2,1), /*POP */ D(T3,1), /*POP2 */ D(T13,1), /*DUP */ D(T13,1), /*DUP_X1 */ D(T13,1), /*DUP_X2 */ D(T13,1), /*DUP2 */ D(T13,1), /*DUP2_X1 */ D(T13,1), /*DUP2_X2 */ D(T13,1), /*SWAP */ D(T1,1), /*IADD */ D(T_,1), D(T_,1), D(T1,1), D(T_,1), /*ISUB */ D(T_,1), D(T_,1), D(T_,1), D(T1,1), /*IMUL */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), /*IDIV */ D(T_,1), D(T_,1), D(T_,1), D(T1,1), /*IREM */ D(T_,1), D(T_,1), D(T_,1), D(T9,1), /*INEG */ D(T_,1), D(T_,1), D(T_,1), D(T1,1), /*ISHL */ D(T_,1), D(T1,1), /*ISHR */ D(T_,1), D(T1,1), /*IUSHR */ D(T_,1), D(T1,1), /*IAND */ D(T_,1), D(T1,1), /*IOR */ D(T_,1), D(T1,1), /*IXOR */ D(T_,1), D(T4,3), /*IINC */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T9,1), /*INT2BYTE */ D(T9,1), /*INT2CHAR */ D(T_,1), /*INT2SHORT */ D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T2,3), /*IFEQ */ D(T2,3), /*IFNE */ D(T2,3), /*IFLT */ D(T2,3), /*IFGE */ D(T2,3), /*IFGT */ D(T2,3), /*IFLT */ D(T3,3), /*IF_ICMPEQ */ D(T3,3), /*IF_ICMPNE */ D(T3,3), /*IF_ICMPLT */ D(T3,3), /*IF_ICMPGE */ D(T3,3), /*IF_ICMPGT */ D(T3,3), /*IF_ICMPLE */ D(T3,3), /*IF_ACMPEQ */ D(T3,3), /*IF_ACMPNE */ D(T4,3), /*GOTO */ D(T_,3), /*JSR */ D(T_,2), /*RET */ D(T2,0), /*TABLESWITCH */ D(T2,0), /*LOOKUPSWITCH*/ D(T2,1), /*IRETURN */ D(T_,1), D(T_,1), D(T_,1), D(T8,1), /*ARETURN */ D(T4,1), /*RETURN */ D(T15,3), /*GETSTATIC */ D(T15,3), /*PUTSTATIC */ D(T15,3), /*GETFIELD */ D(T15,3), /*PUTFIELD */ D(T14,3), /*INVOKEVIRTUAL */ D(T14,3), /*INVOKESPECIAL */ D(T14,3), /*INVOKESTATIC */ D(T14,5), /*INVOKEINTERFACE*/ D(T_,1), D(T11,3), /*NEW */ D(T16,2), /*NEWARRAY */ D(T_,3), D(T12,1), /*ARRAYLENGTH */ D(T8,1), /*ATHROW */ D(T16,3), /*CHECKCAST */ D(T12,3), /*INSTANCEOF */ D(T_,1), D(T_,1), D(T_,1), D(T_,4), D(T8,3), /*IFNULL */ D(T8,3), /*IFNONNULL */ D(T_,5), D(T_,5), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1), D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   D(T_,1),   (T_,1),   D(T_,1), };

附录H

通过类型对Java字节代码进行的检查

将指令解码,得出要生成下一个PC的长度、以及指令类型:

pcarg1=_GET_ILENGTH(_decodeinfo[insn]);

itype=_GET_ITYPE(_decodeinfo[insn]);

根据以下内容进行一些执行前检查:

/* 根据指令类型来检查输入堆栈类型 */ if(itype<=ITYPE9){ if(itype<=ITYPE1}{ check_stack_int(1); } check_stack_int(0); } else if(itype<=ITYPE12){ check_stack_ref(0); } else if(itype<ITYPE11){ push(1); }

最后,进行一些执行后的检查:

/*设置输出状态 */ if(itype<=ITYPE8){ if(itype<=ITYPE6){ if(itype>=ITYPE6){ pop(1); } pop(1); } pop(1); } else if(itype<=ITYPE10){ set_stack_int(0); } else if(itype>=ITYPE11 && itype<=ITYPE16){ set_stack_ref(0); }

附录I

对重编号的Java字节代码进行的检查

取指令。指令的数字值隐含地包括指令类型:

insn=getpc(-1);

根据以下内容进行一些执行前检查:

/* *检查输入堆栈状态。通过对字节代码重新编号, *如果字节代码的值(因此字节代码)属于正确的 *组,则能通过测试而进行必需的安全检查。 */ if(insn<=TYPE9_END){ if(insn<=TYPE1_END){ check_stack_int(1); } check_stack_int(0); } else if(insn<=TYPE12_END){ check_stack_ref(0); } else if(insn<=TYPE11_END){ push(1) }

最后,进行一些执行后检查;

/* *设置输出堆栈状态。 */ if(insn<=TYPE8_END){ if(insn<=TYPE6_END){ if(insn>=TYPE6_START){ pop(1); } pop(1); } pop(1); } else if(insn<=TYPE10_END){ set_stack_int(0); } else if(insn>=TYPE11_START && insn<=TYPE16_END){ set_stack-ref(0); }

按类型来对所支持的Java字节代码进行重定序

/*类型3*/ #define s_POP2 0 #define s_IF_ICMPEQ 1 #define s_IF_ICMPNE 2 #define s_IF_ICMPLT 3 #define s_IF_ICMPGE 4 #define s_IF_ICMPGT 5 #define s_IF_ICMPLE 6 #define s_IF_ACMPEQ 7 #ddfine s_IF_ACMPNE 8 /*类型6*/ #define TYPE6_START 9 #define s_SASTORE 9 #define s_AASTORE 10 #define s_BASTORE 11 #define TYPE6_END 12 /*类型1*/ #define s_IADD 13 #define s_ISUB 14 #define s_IMUL 15 #define s_IDIV 16 #define s_IREM 17 #define s_ISHL 18 #define s_ISHR 19 #define s_IUSHR 20 #define s_IAND 21 #define s_IOR 22 #define s_IXOR 23 #define TYPE1_END 23 /*类型2*/ #define s_ISTORE 24 #define s_POP 25 #define s_IFEQ 26 #define s_IFNE 27 #define s_IFLT 28 #define s_IFGE 29 #define s_IFGT 30 #define s_IFLE 31 #define s_TABLESWITCH 32 #define s_LOOKUPSWITCH 33 #define s_IRETURN 34 /*类型7*/ #define s_SALOAD 35 #define s_AALOAD 36 #define s_BALOAD 37 /*类型9*/ #define s_INEG 39 #define s_INT2BYTE 40 #define s_INT2CHAR 41 #define TYPE9_END 41 /*类型8*/ #define s_ASTORE 42 #define s_ARETURN 43 #define s_ATHROW 44 #define s_IFNULL 45 #define s_IFNONNULL 46 #define TYPE8_END 46 /*类型12*/ #define s_ARRAYLENGTH 47 #define s_INSTANCEOF 48 #define TYPE12_END 48 /*类型10*/ #define s_SIPUSH 49 #define TYPE10_END 49 /*类型5*/ #define s_ILOAD 50 #define s_ALOAD 51 /*类型11*/ #define TYPE11_START 52 #define s_ACONST_NULL 52 #define s_LDC2 53 #define s_JSR 54 #define s_NEW 55 #define TYPE11_END 55 /*类型16*/ #define s_NEWARRAY 56 #define s_CHECKCAST 57 #define TYPE16_END 57 /*类型13*/ #define s_DUP 58 #define s_DUP_X1 59 #define s_DUP_X2 60 #define s_DUP2 61 #define s_DUP2_X1 62 #define s_DUP2_X2 63 #define s_SWAP 64 /*类型14*/ #define s_INVOKEVIRTUAL 65/*01000001*/ #define s_INVOKENONVIRTUAL 66/*01000010*/ #define s_INVOKESTATIC 67/*01000011*/ #define s_INVOKEINTERFACE 68/*01000100*/ /*类型15*/ #define s_GETSTATIC 69 #define s_PUTSTATIC 70 #define s_GETFIELD 71 #define s_PUTFIELD 72 /*类型4*/ #define s_NOP 73 #define s_IINC 74 #define s_GOTO 75 #define s_RET 76 #define s_RETURN 77

本专利文件的部分内容含有受版权保护的材料。版权所有人不反 对任何人按美国专利和商标局的文件或记录的形式原样复制该专利 文件,但是保留所有的版权权利。

根据35 U.S.C.§119(e),本申请要求在先的美国临时申请系 列号60/029,057(1996年10月25日提交)的权益。

参考文献

[1]Tim Linfholm and Frank Yellin,The Java Virtual Machine Speicification,Addison-Wesley,1996。

QQ群二维码
意见反馈