原文地址:http://blog.csdn.net/javacardsolutions/article/details/6658989
1、Native cos的基本架构
首先我们来回顾一下以往native cos的基本架构:
HAL层、通讯层、文件系统、算法、安全模块、个人化命令、应用命令等。
一般一个native cos只实现一个应用规范,也有同时实现两个或者更多应用规范的COS,COS实现规范里面定义的所有应用命令。
卡片上电之后,收到卡机发来的APDU后,通过解析APDU头的INS(还需要检查CLA、P1、P2之类)等步骤来找到该命令所对应的函数执行并返回结果。可以简单点来说就是一个APDU指令对应于卡内的一个功能函数,对于非规范内规定的INS,卡片一般都直接返回6d00表示不支持或者无法解析的指令。
2、java卡特点的简单介绍
Java卡是个多应用智能卡平台,与native cos有本质的区别 ,它不是实现某个应用规范或者多个应用规范的cos,而是为cos应用开发人员提供了另外一种途径来实现应用规范。简单的类比一下,java卡和native卡的区别就相当于PC上用C语言开发应用程序和用java语言开发应用程序的区别。
java卡是个平台,是个应用容器,可以动态的装载不同的应用,也可以装载很多相同应用的不同实例。java卡把native卡里面的HAL层、通讯层、文件系统、算法、安全等都封装成了API库,所以java卡上的智能卡应用开发人员和传统的native卡的开发人员相比,工作难度要降低了很多,像文件系统、掉电保护等等通通不用再设计了。
所以java卡和传统native卡在架构上自然有很大的区别。
章主要从代码级的角度来看Java卡的执行流程。
########################################################################################################
先看一个简单的java卡的applet代码(HelloWord):
从下面的代码可以看出一个java卡应用的简单构成,install函数和process函数。其它可选接口如select、deselect和uninstall函数这里没有。
install函数负责安装这个应用,进行一些对象的初始化和注册,告诉jcre这个应用已经被成功安装,接下来就可以对这个应用进行选择和其它命令执行。
process函数是应用的最重要的命令处理函数,这里解析apdu的ins,进行命令分派,相当于native cos的commandDispatcher函数。当然这个示例比较简单,仅仅是返回一个helloword字符串。
package com.sun.javacard.samples.HelloWorld;
import javacard.framework.*;
public class HelloWorld extends Applet
{
private byte[] echoBytes;
private static final short LENGTH_ECHO_BYTES = 256;
//private test testobj;
/**
* Only this class's install method should create the applet object.
*/
protected HelloWorld()
{
//testobj = new test();
//testobj.setsvalue((short)10);
//testobj.setivalue((int)20);
echoBytes = new byte[LENGTH_ECHO_BYTES];
register();
}
/**
* Installs this applet.
* @param bArray the array containing installation parameters
* @param bOffset the starting offset in bArray
* @param bLength the length in bytes of the parameter data in bArray
*/
public static void install(byte[] bArray, short bOffset, byte bLength)
{
new HelloWorld();
}
/**
* Processes an incoming APDU.
* @see APDU
* @param apdu the incoming APDU
* @exception ISOException with the response bytes per ISO 7816-4
*/
public void process(APDU apdu)
{
byte buffer[] = apdu.getBuffer();
short bytesRead = apdu.setIncomingAndReceive();
short echoOffset = (short)0;
while ( bytesRead > 0 ) {
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, echoBytes, echoOffset, bytesRead);
echoOffset += bytesRead;
bytesRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA);
}
apdu.setOutgoing();
apdu.setOutgoingLength( (short) (echoOffset + 5) );
// echo header
apdu.sendBytes( (short)0, (short) 5);
// echo data
apdu.sendBytesLong( echoBytes, (short) 0, echoOffset );
}
}
########################################################################################################
上面我们简单看了一下一个java卡应用的简单示例,下面我们将看一下java卡运行时的主函数。
下面这个函数就是java卡的入口main函数,在javacard\framework\Dispatcher.java文件中。
芯片在做了一些初始化之后,加载这个main函数,交给java虚拟机进行执行,进入java卡执行流程。
static void main()
{
if (!NativeMethods.isCardInitialized())
cardInit();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~判断卡片是否已经“个人化”了,这里的nativemethods是调用的c接口,库进行了一层封装,提供java代码调用c代码的一个机制。
cardReset();
~~~~~~~~~~卡片重新复位
short sw = 0;
do
{
do
{
PrivAccess.resetSelectingAppletFlag();
PrivAccess.resetProcessMethodFlag();
theAPDU.complete(sw);
~~~~~~~~~~~~~~~这是APDU接口,负责接收APDU和发送返回值和错误码,如果卡机没有下发apdu,则卡片在这里一直等待接收。对应于native cos的receiveApduHeader
byte activeInterface = NativeMethods.getActiveInterface();
try
{
theAPDU.verifyLe();
if (processAndForward())
~~~~~~~~~~~~~~~~~~~~~~该函数进行一些预处理,如果是打开通道命令,则打开相应通道回到接收apdu位置等待接收下一条命令。如果是其它命令,则继续往下执行
{
byte commandChannel = NativeMethods.getCurrentlySelectedChannel();
if (PrivAccess.getSelectedAppID(commandChannel, activeInterface) == -1)
ISOException.throwIt((short)27033);
PrivAccess.setProcessMethodFlag();
Applet selectedApplet = PrivAccess.getSelectedApplet(commandChannel, activeInterface);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~此函数是获取当前被选择的应用
if (selectedApplet instanceof ExtendedLength)
theAPDU.markExtendedSupport(true);
else
theAPDU.markExtendedSupport(false);
selectedApplet.process(theAPDU);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~调用被选择应用的process函数,进行命令处理。相当于native cos的命令分派和处理函数
if (JCSystem.getTransactionDepth() != 0)
TransactionException.throwIt((short)1);
}
sw = -28672;
}
catch (ISOException ex)
{
sw = ex.getReason();
}
catch (Throwable e)
{
sw = 28416;
}
if (JCSystem.getTransactionDepth() != 0)
JCSystem.abortTransaction();
} while (!thePrivAccess.isGarbageCollectionRequested());
GarbageCollector.startGC();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~这里执行垃圾回收,如果前面执行的代码有设置请求垃圾回收位。
} while (true);
}
private static boolean processAndForward()
throws ISOException
{
if (theAPDU.isISOInterindustryCLA())
{
setAPDUChannel();
switch (theAPDUBuffer[1])
{
default:
break;
case -92:
if (!theAPDU.isSecureMessagingCLA())
{
theDispatcher.selectAPDU(theAPDU);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~选择应用命令,根据aid找到应用,然后执行该应用的select接口。
return true;
}
break;
case 112: // 'p'
if (theAPDU.isSecureMessagingCLA())
ISOException.throwIt((short)26754);
theDispatcher.manageChannelAPDU(theAPDU);
return false;
}
}
setAPDUChannel();
return true;
}
########################################################################################################
从上面可以看出一个java卡应用是如何被成功调用的,可以从中看出java卡和native卡的区别。