本章主要從代碼級的角度來看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卡的區別。