软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 开发杂谈 -> 写入文件和读取文件信息—JavaCard开发第三篇 -> 正文阅读

[开发杂谈]写入文件和读取文件信息—JavaCard开发第三篇


上一篇介绍了如何创建文件,本篇介绍如何将密钥或者持卡人信息等写入相应的文件去,以及如何从文件中读取应用或者持卡人的信息。
首先是写入密钥write_key,密钥分为三种:TAC密钥、圈存密钥、消费密钥:
[img]http://img.blog.csdn.net/20160402225011384?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
其实这三种密钥在写入文件里去的时候并不会产生什么区别,区别是在使用密钥的时候体现。之前已经建好了keyfile文件(所以判断如果没建就抛出异常),所以直接调用keyfile数据结构里面的addkey函数进行密钥增加:
	public void addkey(byte tag, short length, byte[] value){
		byte[] pbuf;//只做暂存,密钥要存进Key[recNum]中
		
		pbuf = new byte[23];
		Key[recNum] = pbuf;//可看成给二维数组复制
		pbuf[0] = tag;
		pbuf[1] = (byte)length;
		Util.arrayCopyNonAtomic(value, (short)0, pbuf, (short)2, length);
		recNum ++;
	}

可以看到,使用addkey函数需要传递三个参数进去,第一个是标签,也就是密钥的类型(刚才说的三种:圈存、消费、TAC),第二个是密钥的长度,第三个是密钥的值。对应的是apdu里面的哪些参数呢?看下图:
[img]http://img.blog.csdn.net/20160402225416651?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
所以tag参数对应的是apdu命令的p2参数,密钥长度对应的而是apdu的lc,值对应data。
然后就是write_binary,写二进制文件(应用基本信息和持卡人基本信息),类似的,调用BinaryFile数据结构里面的write_bineary函数(这个其实是binary,TA给的代码命名错了):
	/*
	 * 功能:写入二进制
	 * 参数:off 写入二进制文件 的偏移量; dl  写入的数据长度; data 写入的数据
	 * 返回:无
	 */
	public final void write_bineary(short off, short dl, byte[] data){
		Util.arrayCopyNonAtomic(data, (short)0, binary, off, dl);
	}

同样要传入三个参数,参数如上图定义。
最后是读取二进制文件信息read_binary,理解了上面这里就很简单了,没什么好说的,直接调用BinaryFile结构体里面的read_binary函数:
	/*
	 * 功能:读二进制文件
	 * 参数:off 二进制读取的偏移量;len 读取的长度; data 二进制数据的缓冲区
	 * 返回:二进制数据的字节长度
	 */
	public final short read_binary(short off, short len, byte[] data){
		Util.arrayCopyNonAtomic(binary, off, data, (short)0, len);
		return len;
	}

文件读写挺简单的,其实难度还是在于判断异常的情况,因为涉及到好多参数的判断,话不多说,直接上代码,代码我写了不少注释方便理解。
purse.java
//主文件:电子钱包,调用了其他几个辅助类。
//注意这些所有的代码和应用都是运行在卡片中的!
//而终端只是在命令行敲几句命令发送的那里.
package purse;

//import Purse;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.Util;

public class Purse extends Applet {
	//APDU Object
	private Papdu papdu;
	
	//文件系统,每次run的时候都相当于一张空白卡片,全部需要重新建立,
	//并且建立之后就归当前run的applet所有,某个文件可以随时被调用(keyfile)
	private KeyFile keyfile;            //密钥文件
	private BinaryFile cardfile;       //应用基本文件
	private BinaryFile personfile;     //持卡人基本文件
	private EPFile EPfile;              //电子钱包文件 Electronic Purse
	
	public Purse(byte[] bArray, short bOffset, byte bLength){
		papdu = new Papdu();
		
		byte aidLen = bArray[bOffset];
		if(aidLen == (byte)0x00)
			register();
		else
			register(bArray, (short)(bOffset + 1), aidLen);//注册applet
	}
	//安装applet
	public static void install(byte[] bArray, short bOffset, byte bLength) {
		new Purse(bArray, bOffset, bLength);
	}
	//执行applet
	public void process(APDU apdu) {
		if (selectingApplet()) {
			return;
		}		
		//步骤1:取APDU缓冲区数组引用并将之赋给新建数组
		byte buffer[] = apdu.getBuffer();
		//步骤2:取APDU缓冲区中数据放到变量papdu
		short lc = apdu.setIncomingAndReceive();//将apdu读取到卡片缓冲区当中并返回data段的长度
		papdu.cla = buffer[ISO7816.OFFSET_CLA];
		papdu.ins = buffer[ISO7816.OFFSET_INS];
		papdu.p1 = buffer[ISO7816.OFFSET_P1];
		papdu.p2 = buffer[ISO7816.OFFSET_P2];
		Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, papdu.pdata, (short)0, lc);
		//步骤3:判断命令APDU是否包含数据段,有数据则获取数据长度,并对le赋值,
		 //否则,即不需要lc和data,则获取缓冲区原本lc实际上是le
		 	//获取le的方法,因为不确定papdu有le部分,所以IOS7816下标可选项并没有le而是放在数据块中的.
		 	//如果有数据块,那le就是buffer[ISO7816.OFFSET_CDATA+lc]
		//调用papdu函数判断,不能直接通过lc判断,因为没lc只有le也会把le赋给lc
		if(papdu.APDUContainData())//若papdu命令包含数据块
		{
			papdu.le = buffer[ISO7816.OFFSET_CDATA+lc];
			papdu.lc = buffer[ISO7816.OFFSET_LC];
		}
		else
		{
			papdu.le = buffer[ISO7816.OFFSET_LC];//若没data部分则lc部分实际是le
			papdu.lc = 0;
		}
		boolean rc = handleEvent();
		//步骤4:判断是否需要返回数据,并设置apdu缓冲区	
		if(papdu.le != 0)
		{
			Util.arrayCopyNonAtomic(papdu.pdata, (short)0, buffer, ISO7816.OFFSET_CDATA, (short)papdu.pdata.length);
			apdu.setOutgoingAndSend((short)5, (short)papdu.pdata.length);//把缓冲区的数据返回给终端			
		}
	}

	/*
	 * 功能:对命令的分析和处理
	 * 参数:无
	 * 返回:是否成功处理了命令
	 */
	private boolean handleEvent(){
		switch(papdu.ins){
			case condef.INS_CREATE_FILE:       	return create_file();
			//todo:完成写二进制命令,读二进制命令,写密钥命令
			case condef.INS_WRITE_KEY:			return write_key();
			case condef.INS_WRITE_BIN:			return write_bin();
			case condef.INS_READ_BIN:			return read_bin();
			case condef.INS_NIIT_TRANS:
				if(papdu.p1 == (byte)0x00)		return init_load();
				if(papdu.p1 == (byte)0x01)		return init_purchase();
				ISOException.throwIt(ISO7816.SW_WRONG_P1P2);//else抛出异常
			case condef.INS_LOAD:				return load();
		}	
		ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
		return false;
	}
	/*
	 * 功能:创建文件
	 */
	private boolean create_file() {
		switch(papdu.pdata[0]){ //data的第一位表示要创建文件的类型            
		case condef.EP_FILE:        return EP_file();  
		//todo:完成创建密钥文件,持卡人基本文件和应用基本文件
		case condef.KEY_FILE:		return Key_file();
		case condef.CARD_FILE:		return Card_file();
		case condef.PERSON_FILE:	return Person_file();
		default: 
			ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
		}
		return true;
	}
	/*
	 * 功能:创建电子钱包文件
	 */
	private boolean EP_file() {
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		if(papdu.lc != (byte)0x07)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		if(EPfile != null)//有文件了还重复创建则会报错
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		if(keyfile == null)//都还没密钥文件(必须先于任何其他文件创建)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		this.EPfile = new EPFile(keyfile);
		
		return true;
	}
	//创建密钥文件
	private boolean Key_file()
	{
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		if(papdu.lc != (byte)0x07)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		if(keyfile != null)//有文件了还重复创建则会报错
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		this.keyfile = new KeyFile();
		
		return true;
	}
	//创建应用基本文件
	private boolean Card_file()
	{
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		if(papdu.lc != (byte)0x07)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		if(cardfile != null)//有文件了还重复创建则会报错
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		if(keyfile == null)//都还没密钥文件(必须先于任何其他文件创建)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		this.cardfile = new BinaryFile(papdu.pdata);//传进的参数就是要写入的内容
		
		return true;
	}
	//创建持卡人信息文件
	private boolean Person_file()
	{
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		if(papdu.lc != (byte)0x07)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		if(personfile != null)//有文件了还重复创建则会报错
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		if(keyfile == null)//都还没密钥文件(必须先于任何其他文件创建)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		this.personfile = new BinaryFile(papdu.pdata);//传进的参数就是要写入的内容
		
		return true;
	}
	//写入一条密钥
	private boolean write_key()
	{
		if(keyfile == null)//都还没密钥文件
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		//文件标识有错,这句判断写得有问题啊,老是会返回这个异常--已解决,应该用and而不是or,因为要三个情况都不是才异常
		if(papdu.p2 != (byte)0x06 && papdu.p2 != (byte)0x07 && papdu.p2 != (byte)0x08)
			ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
	
		if(papdu.lc == 0 || papdu.lc > 21)//密钥长度不能为0也不能超过21
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		if(keyfile.recNum >= 3)//文件空间已满
			ISOException.throwIt(ISO7816.SW_FILE_FULL);
		
		this.keyfile.addkey(papdu.p2, papdu.lc, papdu.pdata);//写入一条密钥
		
		return true;
	}
	//写入二进制文件
	private boolean write_bin()
	{
		if(keyfile == null)//都还没密钥文件
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		//都还没二进制文件--没找到
		if(cardfile == null && papdu.p1 == (byte)0x16)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		if(personfile == null && papdu.p1 == (byte)0x17)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		if(papdu.cla != (byte)0x00)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		/*if(papdu.p2 == 0)//没有文件标识
			ISOException.throwIt(ISO7816.SW_WRONG_P1P2);*/
	
		if(papdu.lc == 0)//写入长度不能为0
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		//写入一条二进制命令到文件
		if(papdu.p1 == (byte)0x16)//表明写入的是应用信息
			this.cardfile.write_bineary(papdu.p2, papdu.lc, papdu.pdata);
		else if(papdu.p1 == (byte)0x17)//表明写入的是持卡人信息
			this.personfile.write_bineary(papdu.p2, papdu.lc, papdu.pdata);
		
		return true;
	}
	//读取二进制文件
	private boolean read_bin()
	{
		if(keyfile == null)//都还没密钥文件
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		//都还没二进制文件--没找到
		if(cardfile == null && papdu.p1 == (byte)0x16)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		if(personfile == null && papdu.p1 == (byte)0x17)
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		
		if(papdu.cla != (byte)0x00)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		/*if(papdu.p2 == 0)//没有说明读取文件偏移量
			ISOException.throwIt(ISO7816.SW_WRONG_P1P2);*/
		
		//读取相应的二进文件
		if(papdu.p1 == (byte)0x16)//表明读取的是应用文件
			this.cardfile.read_binary(papdu.p2, papdu.le, papdu.pdata);
		else if(papdu.p1 == (byte)0x17)//表明读取的是持卡人信息文件
			this.personfile.read_binary(papdu.p2, papdu.le, papdu.pdata);
		
		return true;
	}
	/*
	 * 功能:圈存命令的实现
	 */
	private boolean load() {
		short rc;
		
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		if(papdu.p1 != (byte)0x00 && papdu.p2 != (byte)0x00)
			ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
		
		if(EPfile == null)
			ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND);
		
		if(papdu.lc != (short)0x0B)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);

		/*if(keyfile.findkey(papdu.p2) == 0)//通过标识找不到相应的密钥文件
			ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND);*/
		
		rc = EPfile.load(papdu.pdata);
		
		if(rc == 1)
			ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
		else if(rc == 2)
			ISOException.throwIt(condef.SW_LOAD_FULL);
		else if(rc == 3)
			ISOException.throwIt(ISO7816.SW_RECORD_NOT_FOUND);
		
		papdu.le = (short)4;
		
		return true;
	}

	/*
	 * 功能:圈存初始化命令的实现
	 */
	private boolean init_load() {
		short num,rc;
		
		if(papdu.cla != (byte)0x80)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		if(papdu.p1 != (byte)0x00 && papdu.p2 != (byte)0x02)
			ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
		
		if(papdu.lc != (short)0x0B)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		if(EPfile == null)
			ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND);
		
		num = keyfile.findkey(papdu.pdata[0]);
		
		if(num == 0x00)
			ISOException.throwIt(ISO7816.SW_RECORD_NOT_FOUND);
		
		rc = EPfile.init4load(num, papdu.pdata);
		
		if(rc == 2)
			ISOException.throwIt((condef.SW_LOAD_FULL));
		
		papdu.le = (short)0x10;
		
		return true;
	}
	
	/*
	 * 功能:消费命令的实现
	 */
	private boolean purchase(){
		return true;
	}
	/*
	 * 功能:余额查询功能的实现
	 */
	private boolean get_balance(){
		return true;
	}
	
	/*
	 * 功能:消费初始化的实现
	 */
	private boolean init_purchase(){
		return true;
	}
}

KeyFile.java
//这个文件表示的是密钥文件
package purse;

import javacard.framework.Util;

public class KeyFile {
	public short size;         //记录的最大存储数量
	public short recNum;       //当前所存储的记录数量
	private Object[] Key;      //密钥记录
	
	public KeyFile(){
		size = 4;
		recNum = 0;
		Key = new Object[size];//每一位表示一个密钥,可看成是一个二维数组
	}
	
	/*
	 * 功能:添加密钥
	 * 参数:tag 密钥标识符; length 数值的长度;value 数值(5个字节的密钥头+16个字节的密钥值)
	 * 返回:无
	 */
	public void addkey(byte tag, short length, byte[] value){
		byte[] pbuf;//只做暂存,密钥要存进Key[recNum]中
		
		pbuf = new byte[23];
		Key[recNum] = pbuf;//可看成给二维数组复制
		pbuf[0] = tag;
		pbuf[1] = (byte)length;
		Util.arrayCopyNonAtomic(value, (short)0, pbuf, (short)2, length);
		recNum ++;
	}
	
	/*
	 * 功能:通过密钥标识符获取密钥记录号
	 * 参数:tag 密钥标识符
	 * 返回:记录号
	 */
	public short findkey(byte Tag){
		byte[] pbuf;
		if(recNum == 0)
			return 0;
		
		for(short i = 0;i < recNum;i ++){
			pbuf = (byte[])Key[i];
			if(pbuf[0] == Tag)
				return (short)(i + 1);
		}
		return 0;
	}
	
	/*
	 * 功能:通过密钥类型获取密钥记录号
	 * 参数:type 密钥类型
	 * 返回:记录号
	 */
	public short findKeyByType(byte Type){
		byte[] pbuf;
		if(recNum == 0)
			return 0;
		
		for(short i = 0;i < recNum;i ++){
			pbuf = (byte[])Key[i];
			if(pbuf[2] == Type)
				return (short)(i + 1);
		}
		return 0;
	}
	
	/*
	 * 功能:通过密钥记录号读取密钥
	 * 参数:num 密钥记录号 data 所读取到的密钥缓冲区
	 * 返回:密钥的长度
	 */
	public short readkey(short num, byte[] data){
		byte[] pdata;
		pdata = (byte[])Key[num - 1];
		Util.arrayCopyNonAtomic(pdata, (short)2, data, (short)0, (short)(pdata[1]));
		return (short)(pdata[1] - 5);
	}
	
}

BinaryFile.java
//这个文件表示的是二进制文件
package purse;

import javacard.framework.Util;

public class BinaryFile {
	private short size;         //二进投文件的大小
	private byte[] binary;      //二进制文件
	
	public BinaryFile(byte[] pdata){
		size = Util.makeShort(pdata[1], pdata[2]);
		binary = new byte[size];
	}
	
	/*
	 * 功能:写入二进制
	 * 参数:off 写入二进制文件 的偏移量; dl  写入的数据长度; data 写入的数据
	 * 返回:无
	 */
	public final void write_bineary(short off, short dl, byte[] data){
		Util.arrayCopyNonAtomic(data, (short)0, binary, off, dl);
	}
	/*
	 * 功能:读二进制文件
	 * 参数:off 二进制读取的偏移量;len 读取的长度; data 二进制数据的缓冲区
	 * 返回:二进制数据的字节长度
	 */
	public final short read_binary(short off, short len, byte[] data){
		Util.arrayCopyNonAtomic(binary, off, data, (short)0, len);
		return len;
	}
	/*
	 * 功能:获取二进制文件大小
	 * 参数:无
	 * 返回:二进制文件的大小
	 */
	public final short get_size(){
		return size;
	}
}

最后是运行命令脚本和运行结果:
[img]http://img.blog.csdn.net/20160402230502046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
[img]http://img.blog.csdn.net/20160402230520343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
[img]http://img.blog.csdn.net/20160402230531937?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
[img]http://img.blog.csdn.net/20160402230541343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
[img]http://img.blog.csdn.net/20160402230556265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
这里只贴No Error也就是执行正确命令的运行图,自己也试了一些错误命令的调试,也就是改改命令脚本的命令,看看会不会出现一些异常,同时查看自己是否没考虑完全异常情况的判断,这里就不贴图了,这个需要对读写文件的apdu命令和几大重要参数有很好的理解,所以不懂就回去看看实验2文档和ppt,还有代码。
......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-04-03 20:46:48  
开发杂谈 最新文章
BloomFilter
大学四年编程之历程
内核分析
造人论坛——意识的本质和一个人工脑模型
OFDM信号[matlab描述]
人类还会进化吗?
HDUACM1035RobotMotion简单模拟题
树、二叉树(二)
iisphpweb.config处理404,500等,跳转友好
DatabaseAsaFortress
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-17 9:19:04
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --