【JAVACard】智能卡 电子钱包

一、需求分析

        设计一个电子钱包小程序,要求至少实现电子钱包安装、选择与撤销选择、存款、借款、获取身钱包余额、身份验证的功能。

身份验证可通过PIN码来设置。

        存款、借款、消费可以通过设置一余额变量Balance,通过读取相应操作指令,对变量Balance进行加、减、读取值来实现存款、借款、消费功能。

        对不符合规定的操作,抛出异常来中断操作。

                                       

二、APDU

C-APDU

 

CLA

INS

P1

P2

Lc

数据

Le

Verify

90

20

00

00

04

01020304

00

Credit

90

30

00

00

01

存款数值

00

Debit

90

40

00

00

01

借款数值

00

Get Balance

90

50

00

00

00

——

02 

 

 

 

 

 

 

R-APDU

指令

说明

数据

SW1

SW2

Verify

验证成功

——

90

00

 

PIN值错误

——

63

00

Credit

存款成功

——

90

00

 

需要验证身份

——

63

01

 

单次存款超出最高限制

——

6A

83

 

存款数值超过最大数值

——

6A

84

Debit

借款成功

——

90

00

 

需要身份验证

——

63

01

 

单次借款超出最高限制

——

6A

83

 

余额为负值

——

6A

85

Get Balance

返回余额数值

(余额数据)

90

00

注:

  • 初始时设定PIN验证码为01020304,身份验证时发送数据01020304正确验证身份。
  • 存款、借款时,设定单次交易数值不超过0x7F。
  • 余额总量限定为0x7530,且余额不能为负值。

 

三、测试

        采用源码为《JAVA智能卡原理与应用开发》一书中示例

  • 电子钱包安装与选择:

       电子钱包安装选择成功。

  • 未验证身份就存款:

       抛出异常:0x6301,pin码身份验证未通过。

 

  • 身份验证时输入错误PIN码:

       抛出异常:0x6300,身份验证失败。

 

  • 身份验证时输入正确PIN码:

       身份验证成功。

 

  • 查询此时余额:

       余额此时为0。

 

  • 单次存款超0x7F:

抛出异常:0x6A83,超出单次交易额最大值。

 

  • 查询此时余额:

       余额仍为0。

 

  • 存款,金额0x70,并查询余额:

       余额为存款金额0x70 = 112。

 

  • 借款,金额大于余额0x70,并查询余额:

       抛出异常:0x6A85,无效余额,借款失败;

       余额不变,仍为0x70。

 

  • 借款,金额0x20,并查询余额:

       借款成功,余额0x50。

 

  • 存款,金额0x75,并查询余额:

       存款成功,余额0xC5

 

  • 存款,金额0x70,并查询余额:

       存款成功,余额0x0135

 

四、源代码

/**
 * 
 */
package wallet;

import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.APDU;


public class WALLET extends Applet {
	
	final static byte Wallet_CLA = (byte)0x80;
	//final static byte VERIFY = (byte)0x20;
	final static byte CREDIT = (byte)0x30;
	final static byte DEBIT = (byte)0x40;
	final static byte GET_BALANCE = (byte)0x50;
	final static short MAX_BALANCE = 0x7530;
	final static byte MAX_TRANSACTION_AMOUNT = 127;
	
	//final static byte PIN_TRY_LIMIT = (byte)0x03;
	//final static byte MAX_PIN_SIZE = (byte)0x08;
	//final static short SW_VERIFICATION_FAILED = 0x6300;
	//final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;
	final static short SW_INVALID_TRANSACTION_AMOOUNT = 0x6A83;
	final static short SW_EXCEED_MAXIMUM_BALANCE = 0x6A84;
	final static short SW_NEGATIVE_BALANCE = 0x6A85;
	
	//OwnerPIN pin;
	short balance;
	
	private WALLET(byte[] bArray, short bOffset, byte bLength)
	{
		//pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);
		
		byte iLen = bArray[bOffset];
		bOffset = (short)(bOffset + iLen + 1);
		
		byte cLen = bArray[bOffset];
		bOffset = (short)(bOffset + cLen + 1);
		
		byte aLen = bArray[bOffset];
		
		//pin.update(bArray, (short)(bOffset + 1), aLen);
		register();
	}
	
	
	public static void install(byte[] bArray, short bOffset, byte bLength) {
		// GP-compliant JavaCard applet registration
		//new WALLET().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
		new WALLET(bArray, bOffset, bLength);
	}
	/*
	public boolean select()
	{
		if(pin.getTriesRemaining() == 0)
			return false;
			
		return true;
	}
	*/
	/*
	public void deselect()
	{
		pin.reset();
	}
	*/
	public void process(APDU apdu) 
	{
		/*
		// Good practice: Return 9000 on SELECT
		if (selectingApplet()) {
			return;
		}
		*/

		byte[] buffer = apdu.getBuffer();
		
		buffer[ISO7816.OFFSET_CLA] = (byte)(buffer[ISO7816.OFFSET_CLA] & (byte)0xFC);
		
		if((buffer[ISO7816.OFFSET_CLA] == 0) &&
			(buffer[ISO7816.OFFSET_INS] == (byte)(0xA4)))
			return;
		
		if(buffer[ISO7816.OFFSET_CLA] != Wallet_CLA)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		switch (buffer[ISO7816.OFFSET_INS]) {
		case GET_BALANCE:
			getBalance(apdu);
			return;
		
		case DEBIT:
			debit(apdu);
			return;
			
		case CREDIT:
			credit(apdu);
			return;
		/*	
		case VERIFY:
			verify(apdu);
			return;
		*/
		default:
			// good practice: If you don't know the INStruction, say so:
			ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
		}
	}
	
	private void credit(APDU apdu)
	{
		/*
		if(!pin.isValidated())
			ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
		*/
		byte[] buffer = apdu.getBuffer();
		
		byte numBytes = buffer[ISO7816.OFFSET_LC];
		
		byte byteRead = (byte)(apdu.setIncomingAndReceive());
		
		
		if((numBytes != 1) || (byteRead != 1))
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		byte creditAmount = buffer[ISO7816.OFFSET_CDATA];
		
		if((short)(balance + creditAmount) > MAX_BALANCE)
			ISOException.throwIt(SW_EXCEED_MAXIMUM_BALANCE);
		
		balance = (short)(balance + creditAmount);		
		
	}
	
	private void debit(APDU apdu)
	{
		/*
		if(!pin.isValidated())
			ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
		*/
		byte[] buffer = apdu.getBuffer();
		
		byte numBytes = buffer[ISO7816.OFFSET_LC];
		
		byte byteRead = (byte)(apdu.setIncomingAndReceive());
		
		if((numBytes != 1) || (byteRead != 1))
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		byte debitAmount = buffer[ISO7816.OFFSET_CDATA];
		
		if((debitAmount > MAX_TRANSACTION_AMOUNT) || (debitAmount < 0))
			ISOException.throwIt(SW_INVALID_TRANSACTION_AMOOUNT);
		
		if((short)(balance - debitAmount) < (short)0)
			ISOException.throwIt(SW_NEGATIVE_BALANCE);
		
		balance = (short)(balance - debitAmount);
			
	}
	
	private void getBalance(APDU apdu)
	{
		byte[] buffer = apdu.getBuffer();
		
		short le = apdu.setOutgoing();
		
		if(le < 2)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		apdu.setOutgoingLength((byte)2);
		
		buffer[0] = (byte)(balance >> 8);
		buffer[1] = (byte)(balance & 0xFF);
		
		apdu.sendBytes((short)0, (short)2);
		
	}
	/*
	private void verify(APDU apdu)
	{
		byte[] buffer = apdu.getBuffer();
		
		byte byteRead = (byte)(apdu.setIncomingAndReceive());
		
		if(pin.check(buffer, ISO7816.OFFSET_CDATA, byteRead) == false)
			ISOException.throwIt(SW_VERIFICATION_FAILED);
		
	}
	*/
}

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章