前言

Applet在使用前通常需要预先写入个性化数据,这些数据作为持久化信息存储,不会因断电而丢失。然而在升级过程中,旧版applet需要先被卸载才能安装新版,这会导致原有的持久化数据丢失。由于新版applet缺乏必要的数据,将无法直接投入使用。因此,applet升级时必须考虑数据保留问题。

本文提出了一套切实可行的applet升级解决方案。

一、基本思路

交付时提供两个applet,一个主applet,一个backup applet。
主applet实现apdu交互的接口以及主要的交互逻辑,而backup applet则仅用来保存个性化数据,不提供对外的apdu交互接口。
由于主要逻辑都在主applet中,因此升级是针对主applet。升级分为3个步骤:
1、将个性化数据从主applet写入到backup applet中。
2、卸载主applet并安装新的主applet。
3、新的主applet从backup applet中获取个性化数据。

二、实现方案

为了实现数据在主applet和backup applet之前的交换,需要用到Java Card的Shareable能力。

Shareable接口

我们仅实现最基本的数据backup和restore接口

public interface DataShareable extends Shareable {
	public byte backupInfo(byte[] input, short offset, short length);
    public byte restoreInfo(byte[] input, short offset, short length);

}

backup applet

backup applet需要实现DataShareable 接口中的方法。

public byte backupInfo(byte[] input, short offset, short length) {
		if(length > info.length) {
			return NOT_OK;
		}
		try {
			JCSystem.beginTransaction();
			Util.arrayCopy(input, offset, info, (short) 0, length);
			JCSystem.commitTransaction();
			return OK;
		} catch (Exception e) {
			try {
				JCSystem.abortTransaction();
			} catch (Exception ex) {
			}
			return NOT_OK;
		}
	}

	@Override
	public byte restoreInfo(byte[] input, short offset, short length) {
		if (length > info.length) {
			return NOT_OK;
		}
		try {
			Util.arrayCopyNonAtomic(info, (short) 0, input, offset, length);
			return OK;
		} catch (Exception e) {
			return NOT_OK;
		}
	}

另外为了安全
1、不要在process中实现方法,因为这个applet不接收外部的apdu指令。
2、在getShareableInterfaceObject中进行主applet的AID的判断,以避免别的applet来访问到这个backup applet。

	@Override
	public Shareable getShareableInterfaceObject(AID clientAID, byte parameter) {
		// only sAIDApplet can call
		if (!clientAID.equals(sAIDApplet, (short) 0, (byte) 15)) {
			return null;
		}
		return this;
	}

主applet

需要实现以下相关方法

    private DataShareable sio = null;

	private boolean getSIO() {
        if (this.sio == null) {
            this.sio = (DataShareable)JCSystem.getAppletShareableInterfaceObject(
                JCSystem.lookupAID(BACKUP_APPLET_AID_BYTES,(short)0, (byte)(BACKUP_APPLET_AID_BYTES.length)),
                (byte)0);
        }
        if(this.sio != null) {
        	return true;
        } else {
        	return false;
        }
    }
	private void backupInfo(APDU apdu) {
		byte[] buffer = apdu.getBuffer();
		short readCount = apdu.setIncomingAndReceive();
		
		boolean getSIO = getSIO();
		if(!getSIO) {
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
		//将个性化数据写入buffer
        ......
		//调用接口
		if(NOT_OK == sio.backupInfo(buffer, (short) 0, (short) 48)) {
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	private void restoreInfo(APDU apdu) {
		byte[] buffer = apdu.getBuffer();
		short readCount = apdu.setIncomingAndReceive();
		boolean getSIO = getSIO();
		if(!getSIO) {
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
		if(NOT_OK == sio.restoreInfo(buffer, (short) 0, (short) 48)) {
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
		JCSystem.beginTransaction();
        //保存数据
        ......
		JCSystem.commitTransaction();
	}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐